Merge remote-tracking branch 'origin/pr/287'
* origin/pr/287: app: fix docstrings PEP8 refactor tests: remove iptables_header content in test_622_qdb_keyboard_layout tests: add test for guivm and keyboard_layout gui: simplify setting guivm xid and keyboard layout Make pylint happier gui: set keyboard layout from feature Handle GuiVM properties Make PEP8 happier
This commit is contained in:
		
						commit
						656e36f1ee
					
				
							
								
								
									
										283
									
								
								qubes/app.py
									
									
									
									
									
								
							
							
						
						
									
										283
									
								
								qubes/app.py
									
									
									
									
									
								
							| @ -66,8 +66,11 @@ import qubes.vm | |||||||
| import qubes.vm.adminvm | import qubes.vm.adminvm | ||||||
| import qubes.vm.qubesvm | import qubes.vm.qubesvm | ||||||
| import qubes.vm.templatevm | import qubes.vm.templatevm | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| # pylint: enable=wrong-import-position | # pylint: enable=wrong-import-position | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| class VirDomainWrapper: | class VirDomainWrapper: | ||||||
|     # pylint: disable=too-few-public-methods |     # pylint: disable=too-few-public-methods | ||||||
| 
 | 
 | ||||||
| @ -103,6 +106,7 @@ class VirDomainWrapper: | |||||||
|                 if self._reconnect_if_dead(): |                 if self._reconnect_if_dead(): | ||||||
|                     return getattr(self._vm, attrname)(*args, **kwargs) |                     return getattr(self._vm, attrname)(*args, **kwargs) | ||||||
|                 raise |                 raise | ||||||
|  | 
 | ||||||
|         return wrapper |         return wrapper | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -145,21 +149,22 @@ class VirConnectWrapper: | |||||||
|                     return self._wrap_domain( |                     return self._wrap_domain( | ||||||
|                         getattr(self._conn, attrname)(*args, **kwargs)) |                         getattr(self._conn, attrname)(*args, **kwargs)) | ||||||
|                 raise |                 raise | ||||||
|  | 
 | ||||||
|         return wrapper |         return wrapper | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class VMMConnection: | class VMMConnection: | ||||||
|     '''Connection to Virtual Machine Manager (libvirt)''' |     """Connection to Virtual Machine Manager (libvirt)""" | ||||||
| 
 | 
 | ||||||
|     def __init__(self, offline_mode=None, libvirt_reconnect_cb=None): |     def __init__(self, offline_mode=None, libvirt_reconnect_cb=None): | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         :param offline_mode: enable/disable offline mode; default is to |         :param offline_mode: enable/disable offline mode; default is to | ||||||
|         enable when running in chroot as root, otherwise disable |         enable when running in chroot as root, otherwise disable | ||||||
|         :param libvirt_reconnect_cb: callable to be called when connection to |         :param libvirt_reconnect_cb: callable to be called when connection to | ||||||
|         libvirt is re-established; the callback is called with old connection |         libvirt is re-established; the callback is called with old connection | ||||||
|         as argument |         as argument | ||||||
|         ''' |         """ | ||||||
|         if offline_mode is None: |         if offline_mode is None: | ||||||
|             offline_mode = bool(os.getuid() == 0 and |             offline_mode = bool(os.getuid() == 0 and | ||||||
|                                 os.stat('/') != os.stat('/proc/1/root/.')) |                                 os.stat('/') != os.stat('/proc/1/root/.')) | ||||||
| @ -172,16 +177,16 @@ class VMMConnection: | |||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def offline_mode(self): |     def offline_mode(self): | ||||||
|         '''Check or enable offline mode (do not actually connect to vmm)''' |         """Check or enable offline mode (do not actually connect to vmm)""" | ||||||
|         return self._offline_mode |         return self._offline_mode | ||||||
| 
 | 
 | ||||||
|     def _libvirt_error_handler(self, ctx, error): |     def _libvirt_error_handler(self, ctx, error): | ||||||
|         pass |         pass | ||||||
| 
 | 
 | ||||||
|     def init_vmm_connection(self): |     def init_vmm_connection(self): | ||||||
|         '''Initialise connection |         """Initialise connection | ||||||
| 
 | 
 | ||||||
|         This method is automatically called when getting''' |         This method is automatically called when getting""" | ||||||
|         if self._libvirt_conn is not None: |         if self._libvirt_conn is not None: | ||||||
|             # Already initialized |             # Already initialized | ||||||
|             return |             return | ||||||
| @ -201,16 +206,16 @@ class VMMConnection: | |||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def libvirt_conn(self): |     def libvirt_conn(self): | ||||||
|         '''Connection to libvirt''' |         """Connection to libvirt""" | ||||||
|         self.init_vmm_connection() |         self.init_vmm_connection() | ||||||
|         return self._libvirt_conn |         return self._libvirt_conn | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def xs(self): |     def xs(self): | ||||||
|         '''Connection to Xen Store |         """Connection to Xen Store | ||||||
| 
 | 
 | ||||||
|         This property in available only when running on Xen. |         This property in available only when running on Xen. | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         # XXX what about the case when we run under KVM, |         # XXX what about the case when we run under KVM, | ||||||
|         # but xen modules are importable? |         # but xen modules are importable? | ||||||
| @ -223,10 +228,10 @@ class VMMConnection: | |||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def xc(self): |     def xc(self): | ||||||
|         '''Connection to Xen |         """Connection to Xen | ||||||
| 
 | 
 | ||||||
|         This property in available only when running on Xen. |         This property in available only when running on Xen. | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         # XXX what about the case when we run under KVM, |         # XXX what about the case when we run under KVM, | ||||||
|         # but xen modules are importable? |         # but xen modules are importable? | ||||||
| @ -249,11 +254,11 @@ class VMMConnection: | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class QubesHost: | class QubesHost: | ||||||
|     '''Basic information about host machine |     """Basic information about host machine | ||||||
| 
 | 
 | ||||||
|     :param qubes.Qubes app: Qubes application context (must have \ |     :param qubes.Qubes app: Qubes application context (must have \ | ||||||
|         :py:attr:`Qubes.vmm` attribute defined) |         :py:attr:`Qubes.vmm` attribute defined) | ||||||
|     ''' |     """ | ||||||
| 
 | 
 | ||||||
|     def __init__(self, app): |     def __init__(self, app): | ||||||
|         self.app = app |         self.app = app | ||||||
| @ -261,7 +266,6 @@ class QubesHost: | |||||||
|         self._total_mem = None |         self._total_mem = None | ||||||
|         self._physinfo = None |         self._physinfo = None | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def _fetch(self): |     def _fetch(self): | ||||||
|         if self._no_cpus is not None: |         if self._no_cpus is not None: | ||||||
|             return |             return | ||||||
| @ -280,20 +284,18 @@ class QubesHost: | |||||||
|         except NotImplementedError: |         except NotImplementedError: | ||||||
|             pass |             pass | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     @property |     @property | ||||||
|     def memory_total(self): |     def memory_total(self): | ||||||
|         '''Total memory, in kbytes''' |         """Total memory, in kbytes""" | ||||||
| 
 | 
 | ||||||
|         if self.app.vmm.offline_mode: |         if self.app.vmm.offline_mode: | ||||||
|             return 2**64-1 |             return 2 ** 64 - 1 | ||||||
|         self._fetch() |         self._fetch() | ||||||
|         return self._total_mem |         return self._total_mem | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     @property |     @property | ||||||
|     def no_cpus(self): |     def no_cpus(self): | ||||||
|         '''Number of CPUs''' |         """Number of CPUs""" | ||||||
| 
 | 
 | ||||||
|         if self.app.vmm.offline_mode: |         if self.app.vmm.offline_mode: | ||||||
|             return 42 |             return 42 | ||||||
| @ -301,21 +303,19 @@ class QubesHost: | |||||||
|         self._fetch() |         self._fetch() | ||||||
|         return self._no_cpus |         return self._no_cpus | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def get_free_xen_memory(self): |     def get_free_xen_memory(self): | ||||||
|         '''Get free memory from Xen's physinfo. |         """Get free memory from Xen's physinfo. | ||||||
| 
 | 
 | ||||||
|         :raises NotImplementedError: when not under Xen |         :raises NotImplementedError: when not under Xen | ||||||
|         ''' |         """ | ||||||
|         try: |         try: | ||||||
|             self._physinfo = self.app.xc.physinfo() |             self._physinfo = self.app.xc.physinfo() | ||||||
|         except AttributeError: |         except AttributeError: | ||||||
|             raise NotImplementedError('This function requires Xen hypervisor') |             raise NotImplementedError('This function requires Xen hypervisor') | ||||||
|         return int(self._physinfo['free_memory']) |         return int(self._physinfo['free_memory']) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def get_vm_stats(self, previous_time=None, previous=None, only_vm=None): |     def get_vm_stats(self, previous_time=None, previous=None, only_vm=None): | ||||||
|         '''Measure cpu usage for all domains at once. |         """Measure cpu usage for all domains at once. | ||||||
| 
 | 
 | ||||||
|         If previous measurements are given, CPU usage will be given in |         If previous measurements are given, CPU usage will be given in | ||||||
|         percents of time. Otherwise only absolute value (seconds). |         percents of time. Otherwise only absolute value (seconds). | ||||||
| @ -339,7 +339,7 @@ class QubesHost: | |||||||
|         :param only_vm: get measurements only for this VM |         :param only_vm: get measurements only for this VM | ||||||
| 
 | 
 | ||||||
|         :raises NotImplementedError: when not under Xen |         :raises NotImplementedError: when not under Xen | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         if (previous_time is None) != (previous is None): |         if (previous_time is None) != (previous is None): | ||||||
|             raise ValueError( |             raise ValueError( | ||||||
| @ -382,65 +382,59 @@ class QubesHost: | |||||||
|             current[domid]['cpu_usage'] = \ |             current[domid]['cpu_usage'] = \ | ||||||
|                 int(current[domid]['cpu_usage_raw'] / vcpus) |                 int(current[domid]['cpu_usage_raw'] / vcpus) | ||||||
| 
 | 
 | ||||||
|         return (current_time, current) |         return current_time, current | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class VMCollection: | class VMCollection: | ||||||
|     '''A collection of Qubes VMs |     """A collection of Qubes VMs | ||||||
| 
 | 
 | ||||||
|     VMCollection supports ``in`` operator. You may test for ``qid``, ``name`` |     VMCollection supports ``in`` operator. You may test for ``qid``, ``name`` | ||||||
|     and whole VM object's presence. |     and whole VM object's presence. | ||||||
| 
 | 
 | ||||||
|     Iterating over VMCollection will yield machine objects. |     Iterating over VMCollection will yield machine objects. | ||||||
|     ''' |     """ | ||||||
| 
 | 
 | ||||||
|     def __init__(self, app): |     def __init__(self, app): | ||||||
|         self.app = app |         self.app = app | ||||||
|         self._dict = dict() |         self._dict = dict() | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def close(self): |     def close(self): | ||||||
|         del self.app |         del self.app | ||||||
|         self._dict.clear() |         self._dict.clear() | ||||||
|         del self._dict |         del self._dict | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def __repr__(self): |     def __repr__(self): | ||||||
|         return '<{} {!r}>'.format( |         return '<{} {!r}>'.format( | ||||||
|             self.__class__.__name__, list(sorted(self.keys()))) |             self.__class__.__name__, list(sorted(self.keys()))) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def items(self): |     def items(self): | ||||||
|         '''Iterate over ``(qid, vm)`` pairs''' |         """Iterate over ``(qid, vm)`` pairs""" | ||||||
|         for qid in self.qids(): |         for qid in self.qids(): | ||||||
|             yield (qid, self[qid]) |             yield (qid, self[qid]) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def qids(self): |     def qids(self): | ||||||
|         '''Iterate over all qids |         """Iterate over all qids | ||||||
| 
 | 
 | ||||||
|         qids are sorted by numerical order. |         qids are sorted by numerical order. | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         return iter(sorted(self._dict.keys())) |         return iter(sorted(self._dict.keys())) | ||||||
| 
 | 
 | ||||||
|     keys = qids |     keys = qids | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def names(self): |     def names(self): | ||||||
|         '''Iterate over all names |         """Iterate over all names | ||||||
| 
 | 
 | ||||||
|         names are sorted by lexical order. |         names are sorted by lexical order. | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         return iter(sorted(vm.name for vm in self._dict.values())) |         return iter(sorted(vm.name for vm in self._dict.values())) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def vms(self): |     def vms(self): | ||||||
|         '''Iterate over all machines |         """Iterate over all machines | ||||||
| 
 | 
 | ||||||
|         vms are sorted by qid. |         vms are sorted by qid. | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         return iter(sorted(self._dict.values())) |         return iter(sorted(self._dict.values())) | ||||||
| 
 | 
 | ||||||
| @ -448,12 +442,13 @@ class VMCollection: | |||||||
|     values = vms |     values = vms | ||||||
| 
 | 
 | ||||||
|     def add(self, value, _enable_events=True): |     def add(self, value, _enable_events=True): | ||||||
|         '''Add VM to collection |         """Add VM to collection | ||||||
| 
 | 
 | ||||||
|         :param qubes.vm.BaseVM value: VM to add |         :param qubes.vm.BaseVM value: VM to add | ||||||
|  |         :param _enable_events: | ||||||
|         :raises TypeError: when value is of wrong type |         :raises TypeError: when value is of wrong type | ||||||
|         :raises ValueError: when there is already VM which has equal ``qid`` |         :raises ValueError: when there is already VM which has equal ``qid`` | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         # this violates duck typing, but is needed |         # this violates duck typing, but is needed | ||||||
|         # for VMProperty to function correctly |         # for VMProperty to function correctly | ||||||
| @ -463,9 +458,9 @@ class VMCollection: | |||||||
| 
 | 
 | ||||||
|         if value.qid in self: |         if value.qid in self: | ||||||
|             raise ValueError('This collection already holds VM that has ' |             raise ValueError('This collection already holds VM that has ' | ||||||
|                 'qid={!r} ({!r})'.format(value.qid, self[value.qid])) |                              'qid={!r} ({!r})'.format(value.qid, | ||||||
|  |                                                       self[value.qid])) | ||||||
|         if value.name in self: |         if value.name in self: | ||||||
| 
 |  | ||||||
|             raise ValueError('A VM named {!s} already exists' |             raise ValueError('A VM named {!s} already exists' | ||||||
|                              .format(value.name)) |                              .format(value.name)) | ||||||
| 
 | 
 | ||||||
| @ -518,24 +513,21 @@ class VMCollection: | |||||||
|         return any((key in (vm, vm.qid, vm.name)) |         return any((key in (vm, vm.qid, vm.name)) | ||||||
|                    for vm in self) |                    for vm in self) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def __len__(self): |     def __len__(self): | ||||||
|         return len(self._dict) |         return len(self._dict) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def get_vms_based_on(self, template): |     def get_vms_based_on(self, template): | ||||||
|         template = self[template] |         template = self[template] | ||||||
|         return set(vm for vm in self |         return set(vm for vm in self | ||||||
|                    if hasattr(vm, 'template') and vm.template == template) |                    if hasattr(vm, 'template') and vm.template == template) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def get_vms_connected_to(self, netvm): |     def get_vms_connected_to(self, netvm): | ||||||
|         new_vms = set([self[netvm]]) |         new_vms = {self[netvm]} | ||||||
|         dependent_vms = set() |         dependent_vms = set() | ||||||
| 
 | 
 | ||||||
|         # Dependency resolving only makes sense on NetVM (or derivative) |         # Dependency resolving only makes sense on NetVM (or derivative) | ||||||
| #       if not self[netvm_qid].is_netvm(): |         #       if not self[netvm_qid].is_netvm(): | ||||||
| #           return set([]) |         #           return set([]) | ||||||
| 
 | 
 | ||||||
|         while new_vms: |         while new_vms: | ||||||
|             cur_vm = new_vms.pop() |             cur_vm = new_vms.pop() | ||||||
| @ -543,12 +535,11 @@ class VMCollection: | |||||||
|                 if vm in dependent_vms: |                 if vm in dependent_vms: | ||||||
|                     continue |                     continue | ||||||
|                 dependent_vms.add(vm) |                 dependent_vms.add(vm) | ||||||
| #               if vm.is_netvm(): |                 #               if vm.is_netvm(): | ||||||
|                 new_vms.add(vm) |                 new_vms.add(vm) | ||||||
| 
 | 
 | ||||||
|         return dependent_vms |         return dependent_vms | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     # XXX with Qubes Admin Api this will probably lead to race condition |     # XXX with Qubes Admin Api this will probably lead to race condition | ||||||
|     # whole process of creating and adding should be synchronised |     # whole process of creating and adding should be synchronised | ||||||
|     def get_new_unused_qid(self): |     def get_new_unused_qid(self): | ||||||
| @ -558,7 +549,6 @@ class VMCollection: | |||||||
|                 return i |                 return i | ||||||
|         raise LookupError("Cannot find unused qid!") |         raise LookupError("Cannot find unused qid!") | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def get_new_unused_dispid(self): |     def get_new_unused_dispid(self): | ||||||
|         for _ in range(int(qubes.config.max_dispid ** 0.5)): |         for _ in range(int(qubes.config.max_dispid ** 0.5)): | ||||||
|             dispid = random.SystemRandom().randrange(qubes.config.max_dispid) |             dispid = random.SystemRandom().randrange(qubes.config.max_dispid) | ||||||
| @ -566,17 +556,18 @@ class VMCollection: | |||||||
|                 return dispid |                 return dispid | ||||||
|         raise LookupError(( |         raise LookupError(( | ||||||
|                               'https://xkcd.com/221/', |                               'https://xkcd.com/221/', | ||||||
|             'http://dilbert.com/strip/2001-10-25')[random.randint(0, 1)]) |                               'http://dilbert.com/strip/2001-10-25')[ | ||||||
|  |                               random.randint(0, 1)]) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def _default_pool(app): | def _default_pool(app): | ||||||
|     ''' Default storage pool. |     """ Default storage pool. | ||||||
| 
 | 
 | ||||||
|     1. If there is one named 'default', use it. |     1. If there is one named 'default', use it. | ||||||
|     2. Check if root fs is on LVM thin - use that |     2. Check if root fs is on LVM thin - use that | ||||||
|     3. Look for file(-reflink)-based pool pointing to /var/lib/qubes |     3. Look for file(-reflink)-based pool pointing to /var/lib/qubes | ||||||
|     4. Fail |     4. Fail | ||||||
|     ''' |     """ | ||||||
|     if 'default' in app.pools: |     if 'default' in app.pools: | ||||||
|         return app.pools['default'] |         return app.pools['default'] | ||||||
| 
 | 
 | ||||||
| @ -606,6 +597,7 @@ def _default_pool(app): | |||||||
|             return pool |             return pool | ||||||
|     raise AttributeError('Cannot determine default storage pool') |     raise AttributeError('Cannot determine default storage pool') | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| def _setter_pool(app, prop, value): | def _setter_pool(app, prop, value): | ||||||
|     if isinstance(value, qubes.storage.Pool): |     if isinstance(value, qubes.storage.Pool): | ||||||
|         return value |         return value | ||||||
| @ -615,6 +607,7 @@ def _setter_pool(app, prop, value): | |||||||
|         raise qubes.exc.QubesPropertyValueError(app, prop, value, |         raise qubes.exc.QubesPropertyValueError(app, prop, value, | ||||||
|                                                 'No such storage pool') |                                                 'No such storage pool') | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| def _setter_default_netvm(app, prop, value): | def _setter_default_netvm(app, prop, value): | ||||||
|     # skip netvm loop check while loading qubes.xml, to avoid tricky loading |     # skip netvm loop check while loading qubes.xml, to avoid tricky loading | ||||||
|     # order |     # order | ||||||
| @ -631,13 +624,13 @@ def _setter_default_netvm(app, prop, value): | |||||||
|             continue |             continue | ||||||
|         if value == vm \ |         if value == vm \ | ||||||
|                 or value in app.domains.get_vms_connected_to(vm): |                 or value in app.domains.get_vms_connected_to(vm): | ||||||
|             raise qubes.exc.QubesPropertyValueError(app, prop, value, |             raise qubes.exc.QubesPropertyValueError( | ||||||
|                 'Network loop on \'{!s}\''.format(vm)) |                 app, prop, value, 'Network loop on \'{!s}\''.format(vm)) | ||||||
|     return value |     return value | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Qubes(qubes.PropertyHolder): | class Qubes(qubes.PropertyHolder): | ||||||
|     '''Main Qubes application |     """Main Qubes application | ||||||
| 
 | 
 | ||||||
|     :param str store: path to ``qubes.xml`` |     :param str store: path to ``qubes.xml`` | ||||||
| 
 | 
 | ||||||
| @ -722,85 +715,117 @@ class Qubes(qubes.PropertyHolder): | |||||||
|             :param pool: Pool object |             :param pool: Pool object | ||||||
| 
 | 
 | ||||||
|     Methods and attributes: |     Methods and attributes: | ||||||
|     ''' |     """ | ||||||
|  |     default_guivm = qubes.VMProperty( | ||||||
|  |         'default_guivm', | ||||||
|  |         load_stage=3, | ||||||
|  |         default=None, allow_none=True, | ||||||
|  |         doc='Default GuiVM for VMs.') | ||||||
| 
 | 
 | ||||||
|     default_netvm = qubes.VMProperty('default_netvm', load_stage=3, |     default_netvm = qubes.VMProperty( | ||||||
|  |         'default_netvm', | ||||||
|  |         load_stage=3, | ||||||
|         default=None, allow_none=True, |         default=None, allow_none=True, | ||||||
|         setter=_setter_default_netvm, |         setter=_setter_default_netvm, | ||||||
|         doc='''Default NetVM for AppVMs. Initial state is `None`, which means |         doc="""Default NetVM for AppVMs. Initial state is `None`, which means | ||||||
|             that AppVMs are not connected to the Internet.''') |         that AppVMs are not connected to the Internet.""") | ||||||
|     default_template = qubes.VMProperty('default_template', load_stage=3, |     default_template = qubes.VMProperty( | ||||||
|  |         'default_template', load_stage=3, | ||||||
|         vmclass=qubes.vm.templatevm.TemplateVM, |         vmclass=qubes.vm.templatevm.TemplateVM, | ||||||
|         doc='Default template for new AppVMs', |         doc='Default template for new AppVMs', | ||||||
|         allow_none=True) |         allow_none=True) | ||||||
|     updatevm = qubes.VMProperty('updatevm', load_stage=3, |     updatevm = qubes.VMProperty( | ||||||
|  |         'updatevm', load_stage=3, | ||||||
|         default=None, allow_none=True, |         default=None, allow_none=True, | ||||||
|         doc='''Which VM to use as `yum` proxy for updating AdminVM and |         doc="""Which VM to use as `yum` proxy for updating AdminVM and | ||||||
|             TemplateVMs''') |         TemplateVMs""") | ||||||
|     clockvm = qubes.VMProperty('clockvm', load_stage=3, |     clockvm = qubes.VMProperty( | ||||||
|  |         'clockvm', load_stage=3, | ||||||
|         default=None, allow_none=True, |         default=None, allow_none=True, | ||||||
|         doc='Which VM to use as NTP proxy for updating AdminVM') |         doc='Which VM to use as NTP proxy for updating ' | ||||||
|     default_kernel = qubes.property('default_kernel', load_stage=3, |             'AdminVM') | ||||||
|  |     default_kernel = qubes.property( | ||||||
|  |         'default_kernel', load_stage=3, | ||||||
|         doc='Which kernel to use when not overriden in VM') |         doc='Which kernel to use when not overriden in VM') | ||||||
|     default_dispvm = qubes.VMProperty('default_dispvm', load_stage=3, |     default_dispvm = qubes.VMProperty( | ||||||
|  |         'default_dispvm', | ||||||
|  |         load_stage=3, | ||||||
|         default=None, |         default=None, | ||||||
|         doc='Default DispVM base for service calls', allow_none=True) |         doc='Default DispVM base for service calls', | ||||||
|  |         allow_none=True) | ||||||
| 
 | 
 | ||||||
|     management_dispvm = qubes.VMProperty('management_dispvm', load_stage=3, |     management_dispvm = qubes.VMProperty( | ||||||
|  |         'management_dispvm', | ||||||
|  |         load_stage=3, | ||||||
|         default=None, |         default=None, | ||||||
|         doc='Default DispVM base for managing VMs', allow_none=True) |         doc='Default DispVM base for managing VMs', | ||||||
|  |         allow_none=True) | ||||||
| 
 | 
 | ||||||
|     default_pool = qubes.property('default_pool', load_stage=3, |     default_pool = qubes.property( | ||||||
|  |         'default_pool', | ||||||
|  |         load_stage=3, | ||||||
|         default=_default_pool, |         default=_default_pool, | ||||||
|         setter=_setter_pool, |         setter=_setter_pool, | ||||||
|         doc='Default storage pool') |         doc='Default storage pool') | ||||||
| 
 | 
 | ||||||
|     default_pool_private = qubes.property('default_pool_private', load_stage=3, |     default_pool_private = qubes.property( | ||||||
|  |         'default_pool_private', | ||||||
|  |         load_stage=3, | ||||||
|         default=lambda app: app.default_pool, |         default=lambda app: app.default_pool, | ||||||
|         setter=_setter_pool, |         setter=_setter_pool, | ||||||
|         doc='Default storage pool for private volumes') |         doc='Default storage pool for private volumes') | ||||||
| 
 | 
 | ||||||
|     default_pool_root = qubes.property('default_pool_root', load_stage=3, |     default_pool_root = qubes.property( | ||||||
|  |         'default_pool_root', | ||||||
|  |         load_stage=3, | ||||||
|         default=lambda app: app.default_pool, |         default=lambda app: app.default_pool, | ||||||
|         setter=_setter_pool, |         setter=_setter_pool, | ||||||
|         doc='Default storage pool for root volumes') |         doc='Default storage pool for root volumes') | ||||||
| 
 | 
 | ||||||
|     default_pool_volatile = qubes.property('default_pool_volatile', |     default_pool_volatile = qubes.property( | ||||||
|  |         'default_pool_volatile', | ||||||
|         load_stage=3, |         load_stage=3, | ||||||
|         default=lambda app: app.default_pool, |         default=lambda app: app.default_pool, | ||||||
|         setter=_setter_pool, |         setter=_setter_pool, | ||||||
|         doc='Default storage pool for volatile volumes') |         doc='Default storage pool for volatile volumes') | ||||||
| 
 | 
 | ||||||
|     default_pool_kernel = qubes.property('default_pool_kernel', load_stage=3, |     default_pool_kernel = qubes.property( | ||||||
|  |         'default_pool_kernel', | ||||||
|  |         load_stage=3, | ||||||
|         default=lambda app: app.default_pool, |         default=lambda app: app.default_pool, | ||||||
|         setter=_setter_pool, |         setter=_setter_pool, | ||||||
|         doc='Default storage pool for kernel volumes') |         doc='Default storage pool for kernel volumes') | ||||||
| 
 | 
 | ||||||
|     default_qrexec_timeout = qubes.property('default_qrexec_timeout', |     default_qrexec_timeout = qubes.property( | ||||||
|  |         'default_qrexec_timeout', | ||||||
|         load_stage=3, |         load_stage=3, | ||||||
|         default=60, |         default=60, | ||||||
|         type=int, |         type=int, | ||||||
|         doc='''Default time in seconds after which qrexec connection attempt is |         doc="""Default time in seconds after which qrexec connection attempt | ||||||
|             deemed failed''') |         is deemed failed""") | ||||||
| 
 | 
 | ||||||
|     default_shutdown_timeout = qubes.property('default_shutdown_timeout', |     default_shutdown_timeout = qubes.property( | ||||||
|  |         'default_shutdown_timeout', | ||||||
|         load_stage=3, |         load_stage=3, | ||||||
|         default=60, |         default=60, | ||||||
|         type=int, |         type=int, | ||||||
|         doc='''Default time in seconds for VM shutdown to complete''') |         doc="""Default time in seconds for VM shutdown to complete""") | ||||||
| 
 | 
 | ||||||
|     stats_interval = qubes.property('stats_interval', |     stats_interval = qubes.property( | ||||||
|  |         'stats_interval', | ||||||
|         load_stage=3, |         load_stage=3, | ||||||
|         default=3, |         default=3, | ||||||
|         type=int, |         type=int, | ||||||
|         doc='Interval in seconds for VM stats reporting (memory, CPU usage)') |         doc='Interval in seconds for VM stats reporting (memory, CPU usage)') | ||||||
| 
 | 
 | ||||||
|     # TODO #1637 #892 |     # TODO #1637 #892 | ||||||
|     check_updates_vm = qubes.property('check_updates_vm', |     check_updates_vm = qubes.property( | ||||||
|         type=bool, setter=qubes.property.bool, |         'check_updates_vm', | ||||||
|  |         type=bool, | ||||||
|  |         setter=qubes.property.bool, | ||||||
|         load_stage=3, |         load_stage=3, | ||||||
|         default=True, |         default=True, | ||||||
|         doc='check for updates inside qubes') |         doc='Check for updates inside qubes') | ||||||
| 
 | 
 | ||||||
|     def __init__(self, store=None, load=True, offline_mode=None, lock=False, |     def __init__(self, store=None, load=True, offline_mode=None, lock=False, | ||||||
|                  **kwargs): |                  **kwargs): | ||||||
| @ -823,7 +848,8 @@ class Qubes(qubes.PropertyHolder): | |||||||
|         self.pools = {} |         self.pools = {} | ||||||
| 
 | 
 | ||||||
|         #: Connection to VMM |         #: Connection to VMM | ||||||
|         self.vmm = VMMConnection(offline_mode=offline_mode, |         self.vmm = VMMConnection( | ||||||
|  |             offline_mode=offline_mode, | ||||||
|             libvirt_reconnect_cb=self.register_event_handlers) |             libvirt_reconnect_cb=self.register_event_handlers) | ||||||
| 
 | 
 | ||||||
|         #: Information about host system |         #: Information about host system | ||||||
| @ -835,7 +861,8 @@ class Qubes(qubes.PropertyHolder): | |||||||
|             self._store = os.environ.get('QUBES_XML_PATH', |             self._store = os.environ.get('QUBES_XML_PATH', | ||||||
|                                          os.path.join( |                                          os.path.join( | ||||||
|                                              qubes.config.qubes_base_dir, |                                              qubes.config.qubes_base_dir, | ||||||
|                     qubes.config.system_path['qubes_store_filename'])) |                                              qubes.config.system_path[ | ||||||
|  |                                                  'qubes_store_filename'])) | ||||||
| 
 | 
 | ||||||
|         super(Qubes, self).__init__(xml=None, **kwargs) |         super(Qubes, self).__init__(xml=None, **kwargs) | ||||||
| 
 | 
 | ||||||
| @ -861,7 +888,7 @@ class Qubes(qubes.PropertyHolder): | |||||||
|         return self._store |         return self._store | ||||||
| 
 | 
 | ||||||
|     def _migrate_global_properties(self): |     def _migrate_global_properties(self): | ||||||
|         '''Migrate renamed/dropped properties''' |         """Migrate renamed/dropped properties""" | ||||||
|         if self.xml is None: |         if self.xml is None: | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
| @ -905,12 +932,12 @@ class Qubes(qubes.PropertyHolder): | |||||||
|             node_default_fw_netvm.getparent().remove(node_default_fw_netvm) |             node_default_fw_netvm.getparent().remove(node_default_fw_netvm) | ||||||
| 
 | 
 | ||||||
|     def load(self, lock=False): |     def load(self, lock=False): | ||||||
|         '''Open qubes.xml |         """Open qubes.xml | ||||||
| 
 | 
 | ||||||
|         :throws EnvironmentError: failure on parsing store |         :throws EnvironmentError: failure on parsing store | ||||||
|         :throws xml.parsers.expat.ExpatError: failure on parsing store |         :throws xml.parsers.expat.ExpatError: failure on parsing store | ||||||
|         :raises lxml.etree.XMLSyntaxError: on syntax error in qubes.xml |         :raises lxml.etree.XMLSyntaxError: on syntax error in qubes.xml | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         fh = self._acquire_lock() |         fh = self._acquire_lock() | ||||||
|         self.xml = lxml.etree.parse(fh) |         self.xml = lxml.etree.parse(fh) | ||||||
| @ -954,6 +981,7 @@ class Qubes(qubes.PropertyHolder): | |||||||
| 
 | 
 | ||||||
|         # stage 5: misc fixups |         # stage 5: misc fixups | ||||||
| 
 | 
 | ||||||
|  |         self.property_require('default_guivm', allow_none=True) | ||||||
|         self.property_require('default_netvm', allow_none=True) |         self.property_require('default_netvm', allow_none=True) | ||||||
|         self.property_require('default_template', allow_none=True) |         self.property_require('default_template', allow_none=True) | ||||||
|         self.property_require('clockvm', allow_none=True) |         self.property_require('clockvm', allow_none=True) | ||||||
| @ -970,7 +998,6 @@ class Qubes(qubes.PropertyHolder): | |||||||
|         if not lock: |         if not lock: | ||||||
|             self._release_lock() |             self._release_lock() | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def __xml__(self): |     def __xml__(self): | ||||||
|         element = lxml.etree.Element('qubes') |         element = lxml.etree.Element('qubes') | ||||||
| 
 | 
 | ||||||
| @ -997,7 +1024,7 @@ class Qubes(qubes.PropertyHolder): | |||||||
|         return type(self).__name__ |         return type(self).__name__ | ||||||
| 
 | 
 | ||||||
|     def save(self, lock=True): |     def save(self, lock=True): | ||||||
|         '''Save all data to qubes.xml |         """Save all data to qubes.xml | ||||||
| 
 | 
 | ||||||
|         There are several problems with saving :file:`qubes.xml` which must be |         There are several problems with saving :file:`qubes.xml` which must be | ||||||
|         mitigated: |         mitigated: | ||||||
| @ -1009,7 +1036,7 @@ class Qubes(qubes.PropertyHolder): | |||||||
| 
 | 
 | ||||||
|         :param bool lock: keep file locked after saving |         :param bool lock: keep file locked after saving | ||||||
|         :throws EnvironmentError: failure on saving |         :throws EnvironmentError: failure on saving | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         if not self.__locked_fh: |         if not self.__locked_fh: | ||||||
|             self._acquire_lock(for_save=True) |             self._acquire_lock(for_save=True) | ||||||
| @ -1039,11 +1066,10 @@ class Qubes(qubes.PropertyHolder): | |||||||
|         if not lock: |         if not lock: | ||||||
|             self._release_lock() |             self._release_lock() | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def close(self): |     def close(self): | ||||||
|         '''Deconstruct the object and break circular references |         """Deconstruct the object and break circular references | ||||||
| 
 | 
 | ||||||
|         After calling this the object is unusable, not even for saving.''' |         After calling this the object is unusable, not even for saving.""" | ||||||
| 
 | 
 | ||||||
|         self.log.debug('close() <- %#x', id(self)) |         self.log.debug('close() <- %#x', id(self)) | ||||||
|         for frame in traceback.extract_stack(): |         for frame in traceback.extract_stack(): | ||||||
| @ -1073,7 +1099,6 @@ class Qubes(qubes.PropertyHolder): | |||||||
|         if self.__locked_fh: |         if self.__locked_fh: | ||||||
|             self._release_lock() |             self._release_lock() | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def _acquire_lock(self, for_save=False): |     def _acquire_lock(self, for_save=False): | ||||||
|         assert self.__locked_fh is None, 'double lock' |         assert self.__locked_fh is None, 'double lock' | ||||||
| 
 | 
 | ||||||
| @ -1116,7 +1141,6 @@ class Qubes(qubes.PropertyHolder): | |||||||
|         self.__locked_fh = os.fdopen(fd, 'r+b') |         self.__locked_fh = os.fdopen(fd, 'r+b') | ||||||
|         return self.__locked_fh |         return self.__locked_fh | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def _release_lock(self): |     def _release_lock(self): | ||||||
|         assert self.__locked_fh is not None, 'double release' |         assert self.__locked_fh is not None, 'double release' | ||||||
| 
 | 
 | ||||||
| @ -1125,7 +1149,6 @@ class Qubes(qubes.PropertyHolder): | |||||||
|         self.__locked_fh.close() |         self.__locked_fh.close() | ||||||
|         self.__locked_fh = None |         self.__locked_fh = None | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def load_initial_values(self): |     def load_initial_values(self): | ||||||
|         self.labels = { |         self.labels = { | ||||||
|             1: qubes.Label(1, '0xcc0000', 'red'), |             1: qubes.Label(1, '0xcc0000', 'red'), | ||||||
| @ -1181,12 +1204,11 @@ class Qubes(qubes.PropertyHolder): | |||||||
| 
 | 
 | ||||||
|         return self |         return self | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def xml_labels(self): |     def xml_labels(self): | ||||||
|         '''Serialise labels |         """Serialise labels | ||||||
| 
 | 
 | ||||||
|         :rtype: lxml.etree._Element |         :rtype: lxml.etree._Element | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         labels = lxml.etree.Element('labels') |         labels = lxml.etree.Element('labels') | ||||||
|         for label in sorted(self.labels.values(), key=lambda labl: labl.index): |         for label in sorted(self.labels.values(), key=lambda labl: labl.index): | ||||||
| @ -1195,14 +1217,14 @@ class Qubes(qubes.PropertyHolder): | |||||||
| 
 | 
 | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def get_vm_class(clsname): |     def get_vm_class(clsname): | ||||||
|         '''Find the class for a domain. |         """Find the class for a domain. | ||||||
| 
 | 
 | ||||||
|         Classes are registered as setuptools' entry points in ``qubes.vm`` |         Classes are registered as setuptools' entry points in ``qubes.vm`` | ||||||
|         group. Any package may supply their own classes. |         group. Any package may supply their own classes. | ||||||
| 
 | 
 | ||||||
|         :param str clsname: name of the class |         :param str clsname: name of the class | ||||||
|         :return type: class |         :return type: class | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         try: |         try: | ||||||
|             return qubes.utils.get_entry_point_one( |             return qubes.utils.get_entry_point_one( | ||||||
| @ -1213,9 +1235,9 @@ class Qubes(qubes.PropertyHolder): | |||||||
|         # don't catch TypeError |         # don't catch TypeError | ||||||
| 
 | 
 | ||||||
|     def add_new_vm(self, cls, qid=None, **kwargs): |     def add_new_vm(self, cls, qid=None, **kwargs): | ||||||
|         '''Add new Virtual Machine to collection |         """Add new Virtual Machine to collection | ||||||
| 
 | 
 | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         if qid is None: |         if qid is None: | ||||||
|             qid = self.domains.get_new_unused_qid() |             qid = self.domains.get_new_unused_qid() | ||||||
| @ -1239,10 +1261,10 @@ class Qubes(qubes.PropertyHolder): | |||||||
|         return self.domains.add(cls(self, None, qid=qid, **kwargs)) |         return self.domains.add(cls(self, None, qid=qid, **kwargs)) | ||||||
| 
 | 
 | ||||||
|     def get_label(self, label): |     def get_label(self, label): | ||||||
|         '''Get label as identified by index or name |         """Get label as identified by index or name | ||||||
| 
 | 
 | ||||||
|         :throws KeyError: when label is not found |         :throws KeyError: when label is not found | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         # first search for index, verbatim |         # first search for index, verbatim | ||||||
|         try: |         try: | ||||||
| @ -1297,9 +1319,10 @@ class Qubes(qubes.PropertyHolder): | |||||||
|             prop_suffixes = ['', '_kernel', '_private', '_root', '_volatile'] |             prop_suffixes = ['', '_kernel', '_private', '_root', '_volatile'] | ||||||
|             for suffix in prop_suffixes: |             for suffix in prop_suffixes: | ||||||
|                 if getattr(self, 'default_pool' + suffix, None) is pool: |                 if getattr(self, 'default_pool' + suffix, None) is pool: | ||||||
|                     raise qubes.exc.QubesPoolInUseError(pool, |                     raise qubes.exc.QubesPoolInUseError( | ||||||
|                         'Storage pool is in use: set as {}'.format( |                         pool, | ||||||
|                             'default_pool' + suffix)) |                         'Storage pool is in use: ' | ||||||
|  |                         'set as {}'.format('default_pool' + suffix)) | ||||||
|             yield from self.fire_event_async('pool-pre-delete', |             yield from self.fire_event_async('pool-pre-delete', | ||||||
|                                              pre_event=True, pool=pool) |                                              pre_event=True, pool=pool) | ||||||
|             del self.pools[name] |             del self.pools[name] | ||||||
| @ -1308,9 +1331,8 @@ class Qubes(qubes.PropertyHolder): | |||||||
|         except KeyError: |         except KeyError: | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def get_pool(self, pool): |     def get_pool(self, pool): | ||||||
|         '''  Returns a :py:class:`qubes.storage.Pool` instance ''' |         """  Returns a :py:class:`qubes.storage.Pool` instance """ | ||||||
|         if isinstance(pool, qubes.storage.Pool): |         if isinstance(pool, qubes.storage.Pool): | ||||||
|             return pool |             return pool | ||||||
|         try: |         try: | ||||||
| @ -1341,10 +1363,10 @@ class Qubes(qubes.PropertyHolder): | |||||||
|                                            (driver, name)) |                                            (driver, name)) | ||||||
| 
 | 
 | ||||||
|     def register_event_handlers(self, old_connection=None): |     def register_event_handlers(self, old_connection=None): | ||||||
|         '''Register libvirt event handlers, which will translate libvirt |         """Register libvirt event handlers, which will translate libvirt | ||||||
|         events into qubes.events. This function should be called only in |         events into qubes.events. This function should be called only in | ||||||
|         'qubesd' process and only when mainloop has been already set. |         'qubesd' process and only when mainloop has been already set. | ||||||
|         ''' |         """ | ||||||
|         if old_connection: |         if old_connection: | ||||||
|             try: |             try: | ||||||
|                 old_connection.domainEventDeregisterAny( |                 old_connection.domainEventDeregisterAny( | ||||||
| @ -1361,9 +1383,9 @@ class Qubes(qubes.PropertyHolder): | |||||||
|                 None)) |                 None)) | ||||||
| 
 | 
 | ||||||
|     def _domain_event_callback(self, _conn, domain, event, _detail, _opaque): |     def _domain_event_callback(self, _conn, domain, event, _detail, _opaque): | ||||||
|         '''Generic libvirt event handler (virConnectDomainEventCallback), |         """Generic libvirt event handler (virConnectDomainEventCallback), | ||||||
|         translate libvirt event into qubes.events. |         translate libvirt event into qubes.events. | ||||||
|         ''' |         """ | ||||||
|         if not self.events_enabled: |         if not self.events_enabled: | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
| @ -1404,9 +1426,12 @@ class Qubes(qubes.PropertyHolder): | |||||||
|                         self.log.error( |                         self.log.error( | ||||||
|                             'Cannot remove %s, used by %s.%s', |                             'Cannot remove %s, used by %s.%s', | ||||||
|                             vm, obj, prop.__name__) |                             vm, obj, prop.__name__) | ||||||
|                         raise qubes.exc.QubesVMInUseError(vm, 'Domain is in ' |                         raise qubes.exc.QubesVMInUseError( | ||||||
|                         'use: {!r}; see /var/log/qubes/qubes.log in dom0 for ' |                             vm, | ||||||
|                         'details'.format(vm.name)) |                             'Domain is in use: {!r};' | ||||||
|  |                             'see /var/log/qubes/qubes.log in dom0 for ' | ||||||
|  |                             'details'.format( | ||||||
|  |                                 vm.name)) | ||||||
|                 except AttributeError: |                 except AttributeError: | ||||||
|                     pass |                     pass | ||||||
| 
 | 
 | ||||||
| @ -1414,6 +1439,7 @@ class Qubes(qubes.PropertyHolder): | |||||||
|     def on_domain_deleted(self, event, vm): |     def on_domain_deleted(self, event, vm): | ||||||
|         # pylint: disable=unused-argument |         # pylint: disable=unused-argument | ||||||
|         for propname in ( |         for propname in ( | ||||||
|  |                 'default_guivm' | ||||||
|                 'default_netvm', |                 'default_netvm', | ||||||
|                 'default_fw_netvm', |                 'default_fw_netvm', | ||||||
|                 'clockvm', |                 'clockvm', | ||||||
| @ -1426,7 +1452,6 @@ class Qubes(qubes.PropertyHolder): | |||||||
|             except AttributeError: |             except AttributeError: | ||||||
|                 pass |                 pass | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     @qubes.events.handler('property-pre-set:clockvm') |     @qubes.events.handler('property-pre-set:clockvm') | ||||||
|     def on_property_pre_set_clockvm(self, event, name, newvalue, oldvalue=None): |     def on_property_pre_set_clockvm(self, event, name, newvalue, oldvalue=None): | ||||||
|         # pylint: disable=unused-argument,no-self-use |         # pylint: disable=unused-argument,no-self-use | ||||||
| @ -1448,10 +1473,11 @@ class Qubes(qubes.PropertyHolder): | |||||||
|         if newvalue is not None and oldvalue is not None \ |         if newvalue is not None and oldvalue is not None \ | ||||||
|                 and oldvalue.is_running() and not newvalue.is_running() \ |                 and oldvalue.is_running() and not newvalue.is_running() \ | ||||||
|                 and self.domains.get_vms_connected_to(oldvalue): |                 and self.domains.get_vms_connected_to(oldvalue): | ||||||
|             raise qubes.exc.QubesVMNotRunningError(newvalue, |             raise qubes.exc.QubesVMNotRunningError( | ||||||
|  |                 newvalue, | ||||||
|                 'Cannot change {!r} to domain that ' |                 'Cannot change {!r} to domain that ' | ||||||
|                 'is not running ({!r}).'.format(name, newvalue.name)) |                 'is not running ({!r}).'.format( | ||||||
| 
 |                     name, newvalue.name)) | ||||||
| 
 | 
 | ||||||
|     @qubes.events.handler('property-set:default_fw_netvm') |     @qubes.events.handler('property-set:default_fw_netvm') | ||||||
|     def on_property_set_default_fw_netvm(self, event, name, newvalue, |     def on_property_set_default_fw_netvm(self, event, name, newvalue, | ||||||
| @ -1467,7 +1493,6 @@ class Qubes(qubes.PropertyHolder): | |||||||
|                 vm.fire_event('property-del:netvm', |                 vm.fire_event('property-del:netvm', | ||||||
|                               name='netvm', oldvalue=oldvalue) |                               name='netvm', oldvalue=oldvalue) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     @qubes.events.handler('property-set:default_netvm') |     @qubes.events.handler('property-set:default_netvm') | ||||||
|     def on_property_set_default_netvm(self, event, name, newvalue, |     def on_property_set_default_netvm(self, event, name, newvalue, | ||||||
|                                       oldvalue=None): |                                       oldvalue=None): | ||||||
|  | |||||||
| @ -34,12 +34,39 @@ class GUI(qubes.ext.Extension): | |||||||
|                               if vm.features.get('gui-seamless', False) |                               if vm.features.get('gui-seamless', False) | ||||||
|                               else 'FULLSCREEN')) |                               else 'FULLSCREEN')) | ||||||
| 
 | 
 | ||||||
|  |     @qubes.ext.handler('property-set:guivm') | ||||||
|  |     def on_property_set(self, subject, event, name, newvalue, oldvalue=None): | ||||||
|  |         # pylint: disable=unused-argument,no-self-use | ||||||
|  | 
 | ||||||
|  |         # Clean other 'guivm-XXX' tags. | ||||||
|  |         # gui-daemon can connect to only one domain | ||||||
|  |         tags_list = list(subject.tags) | ||||||
|  |         for tag in tags_list: | ||||||
|  |             if 'guivm-' in tag: | ||||||
|  |                 subject.tags.remove(tag) | ||||||
|  | 
 | ||||||
|  |         guivm = 'guivm-' + newvalue.name | ||||||
|  |         subject.tags.add(guivm) | ||||||
|  | 
 | ||||||
|     @qubes.ext.handler('domain-qdb-create') |     @qubes.ext.handler('domain-qdb-create') | ||||||
|     def on_domain_qdb_create(self, vm, event): |     def on_domain_qdb_create(self, vm, event): | ||||||
|         # pylint: disable=unused-argument,no-self-use |         # pylint: disable=unused-argument,no-self-use | ||||||
|         for feature in ('gui-videoram-overhead', 'gui-videoram-min'): |         for feature in ('gui-videoram-overhead', 'gui-videoram-min'): | ||||||
|             try: |             try: | ||||||
|                 vm.untrusted_qdb.write('/qubes-{}'.format(feature), |                 vm.untrusted_qdb.write( | ||||||
|                     vm.features.check_with_template_and_adminvm(feature)) |                     '/qubes-{}'.format(feature), | ||||||
|  |                     vm.features.check_with_template_and_adminvm( | ||||||
|  |                         feature)) | ||||||
|             except KeyError: |             except KeyError: | ||||||
|                 pass |                 pass | ||||||
|  | 
 | ||||||
|  |         # Add GuiVM Xen ID for gui-daemon | ||||||
|  |         if getattr(vm, 'guivm', None): | ||||||
|  |             if vm != vm.guivm: | ||||||
|  |                 vm.untrusted_qdb.write('/qubes-gui-domain-xid', | ||||||
|  |                                        str(vm.guivm.xid)) | ||||||
|  | 
 | ||||||
|  |             # Add keyboard layout from that of GuiVM | ||||||
|  |             kbd_layout = vm.guivm.features.get('keyboard-layout', None) | ||||||
|  |             if kbd_layout: | ||||||
|  |                 vm.untrusted_qdb.write('/keyboard-layout', kbd_layout) | ||||||
|  | |||||||
| @ -1469,6 +1469,52 @@ class TC_90_QubesVM(QubesVMTestsMixin, qubes.tests.QubesTestCase): | |||||||
|                 netvm.create_qdb_entries() |                 netvm.create_qdb_entries() | ||||||
|             self.assertEqual(test_qubesdb.data, expected) |             self.assertEqual(test_qubesdb.data, expected) | ||||||
| 
 | 
 | ||||||
|  |     @unittest.mock.patch('qubes.utils.get_timezone') | ||||||
|  |     @unittest.mock.patch('qubes.utils.urandom') | ||||||
|  |     @unittest.mock.patch('qubes.vm.qubesvm.QubesVM.untrusted_qdb') | ||||||
|  |     def test_622_qdb_keyboard_layout(self, mock_qubesdb, mock_urandom, | ||||||
|  |             mock_timezone): | ||||||
|  |         mock_urandom.return_value = b'A' * 64 | ||||||
|  |         mock_timezone.return_value = 'UTC' | ||||||
|  |         template = self.get_vm( | ||||||
|  |             cls=qubes.vm.templatevm.TemplateVM, name='template') | ||||||
|  |         template.netvm = None | ||||||
|  |         guivm = self.get_vm(cls=qubes.vm.appvm.AppVM, template=template, | ||||||
|  |             name='sys-gui', qid=2, provides_network=False) | ||||||
|  |         vm = self.get_vm(cls=qubes.vm.appvm.AppVM, template=template, | ||||||
|  |             name='appvm', qid=3) | ||||||
|  |         vm.netvm = None | ||||||
|  |         vm.guivm = guivm | ||||||
|  |         guivm.features['keyboard-layout'] = 'fr' | ||||||
|  |         vm.events_enabled = True | ||||||
|  |         test_qubesdb = TestQubesDB() | ||||||
|  |         mock_qubesdb.write.side_effect = test_qubesdb.write | ||||||
|  |         mock_qubesdb.rm.side_effect = test_qubesdb.rm | ||||||
|  |         vm.create_qdb_entries() | ||||||
|  |         self.maxDiff = None | ||||||
|  |         self.assertEqual(test_qubesdb.data, { | ||||||
|  |             '/name': 'test-inst-appvm', | ||||||
|  |             '/type': 'AppVM', | ||||||
|  |             '/default-user': 'user', | ||||||
|  |             '/keyboard-layout': 'fr', | ||||||
|  |             '/qubes-vm-type': 'AppVM', | ||||||
|  |             '/qubes-gui-domain-xid': '{}'.format(guivm.xid), | ||||||
|  |             '/qubes-debug-mode': '0', | ||||||
|  |             '/qubes-base-template': 'test-inst-template', | ||||||
|  |             '/qubes-timezone': 'UTC', | ||||||
|  |             '/qubes-random-seed': base64.b64encode(b'A' * 64), | ||||||
|  |             '/qubes-vm-persistence': 'rw-only', | ||||||
|  |             '/qubes-vm-updateable': 'False', | ||||||
|  |             '/qubes-block-devices': '', | ||||||
|  |             '/qubes-usb-devices': '', | ||||||
|  |             '/qubes-iptables': 'reload', | ||||||
|  |             '/qubes-iptables-error': '', | ||||||
|  |             '/qubes-iptables-header': unittest.mock.ANY, | ||||||
|  |             '/qubes-service/qubes-update-check': '0', | ||||||
|  |             '/qubes-service/meminfo-writer': '1', | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def coroutine_mock(self, mock, *args, **kwargs): |     def coroutine_mock(self, mock, *args, **kwargs): | ||||||
|         return mock(*args, **kwargs) |         return mock(*args, **kwargs) | ||||||
|  | |||||||
| @ -48,6 +48,7 @@ import qubes.vm.mix.net | |||||||
| qmemman_present = False | qmemman_present = False | ||||||
| try: | try: | ||||||
|     import qubes.qmemman.client  # pylint: disable=wrong-import-position |     import qubes.qmemman.client  # pylint: disable=wrong-import-position | ||||||
|  | 
 | ||||||
|     qmemman_present = True |     qmemman_present = True | ||||||
| except ImportError: | except ImportError: | ||||||
|     pass |     pass | ||||||
| @ -61,27 +62,29 @@ MEM_OVERHEAD_PER_VCPU = 3 * 1024 * 1024 / 2 | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def _setter_kernel(self, prop, value): | def _setter_kernel(self, prop, value): | ||||||
|     ''' Helper for setting the domain kernel and running sanity checks on it. |     """ Helper for setting the domain kernel and running sanity checks on it. | ||||||
|     '''  # pylint: disable=unused-argument |     """  # pylint: disable=unused-argument | ||||||
|     if not value: |     if not value: | ||||||
|         return '' |         return '' | ||||||
|     value = str(value) |     value = str(value) | ||||||
|     if '/' in value: |     if '/' in value: | ||||||
|         raise qubes.exc.QubesPropertyValueError(self, prop, value, |         raise qubes.exc.QubesPropertyValueError( | ||||||
|  |             self, prop, value, | ||||||
|             'Kernel name cannot contain \'/\'') |             'Kernel name cannot contain \'/\'') | ||||||
|     return value |     return value | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def _setter_positive_int(self, prop, value): | def _setter_positive_int(self, prop, value): | ||||||
|     ''' Helper for setting a positive int. Checks that the int is > 0 ''' |     """ Helper for setting a positive int. Checks that the int is > 0 """ | ||||||
|     # pylint: disable=unused-argument |     # pylint: disable=unused-argument | ||||||
|     value = int(value) |     value = int(value) | ||||||
|     if value <= 0: |     if value <= 0: | ||||||
|         raise ValueError('Value must be positive') |         raise ValueError('Value must be positive') | ||||||
|     return value |     return value | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| def _setter_non_negative_int(self, prop, value): | def _setter_non_negative_int(self, prop, value): | ||||||
|     ''' Helper for setting a positive int. Checks that the int is >= 0 ''' |     """ Helper for setting a positive int. Checks that the int is >= 0 """ | ||||||
|     # pylint: disable=unused-argument |     # pylint: disable=unused-argument | ||||||
|     value = int(value) |     value = int(value) | ||||||
|     if value < 0: |     if value < 0: | ||||||
| @ -90,26 +93,31 @@ def _setter_non_negative_int(self, prop, value): | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def _setter_default_user(self, prop, value): | def _setter_default_user(self, prop, value): | ||||||
|     ''' Helper for setting default user ''' |     """ Helper for setting default user """ | ||||||
|     value = str(value) |     value = str(value) | ||||||
|     # specifically forbid: ':', ' ', ''', '"' |     # specifically forbid: ':', ' ', """, '"' | ||||||
|     allowed_chars = string.ascii_letters + string.digits + '_-+,.' |     allowed_chars = string.ascii_letters + string.digits + '_-+,.' | ||||||
|     if not all(c in allowed_chars for c in value): |     if not all(c in allowed_chars for c in value): | ||||||
|         raise qubes.exc.QubesPropertyValueError(self, prop, value, |         raise qubes.exc.QubesPropertyValueError( | ||||||
|  |             self, prop, value, | ||||||
|             'Username can contain only those characters: ' + allowed_chars) |             'Username can contain only those characters: ' + allowed_chars) | ||||||
|     return value |     return value | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| def _setter_virt_mode(self, prop, value): | def _setter_virt_mode(self, prop, value): | ||||||
|     value = str(value) |     value = str(value) | ||||||
|     value = value.lower() |     value = value.lower() | ||||||
|     if value not in ('hvm', 'pv', 'pvh'): |     if value not in ('hvm', 'pv', 'pvh'): | ||||||
|         raise qubes.exc.QubesPropertyValueError(self, prop, value, |         raise qubes.exc.QubesPropertyValueError( | ||||||
|  |             self, prop, value, | ||||||
|             'Invalid virtualization mode, supported values: hvm, pv, pvh') |             'Invalid virtualization mode, supported values: hvm, pv, pvh') | ||||||
|     if value == 'pvh' and list(self.devices['pci'].persistent()): |     if value == 'pvh' and list(self.devices['pci'].persistent()): | ||||||
|         raise qubes.exc.QubesPropertyValueError(self, prop, value, |         raise qubes.exc.QubesPropertyValueError( | ||||||
|  |             self, prop, value, | ||||||
|             "pvh mode can't be set if pci devices are attached") |             "pvh mode can't be set if pci devices are attached") | ||||||
|     return value |     return value | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| def _default_virt_mode(self): | def _default_virt_mode(self): | ||||||
|     if self.devices['pci'].persistent(): |     if self.devices['pci'].persistent(): | ||||||
|         return 'hvm' |         return 'hvm' | ||||||
| @ -118,10 +126,11 @@ def _default_virt_mode(self): | |||||||
|     except AttributeError: |     except AttributeError: | ||||||
|         return 'pvh' |         return 'pvh' | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| def _default_with_template(prop, default): | def _default_with_template(prop, default): | ||||||
|     '''Return a callable for 'default' argument of a property. Use a value |     """Return a callable for 'default' argument of a property. Use a value | ||||||
|     from a template (if any), otherwise *default* |     from a template (if any), otherwise *default* | ||||||
|     ''' |     """ | ||||||
| 
 | 
 | ||||||
|     def _func(self): |     def _func(self): | ||||||
|         try: |         try: | ||||||
| @ -152,14 +161,15 @@ def _default_maxmem(self): | |||||||
| 
 | 
 | ||||||
|     return _default_with_template('maxmem', default_maxmem)(self) |     return _default_with_template('maxmem', default_maxmem)(self) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| def _default_kernelopts(self): | def _default_kernelopts(self): | ||||||
|     ''' |     """ | ||||||
|     Return default kernel options for the given kernel. If kernel directory |     Return default kernel options for the given kernel. If kernel directory | ||||||
|     contains 'default-kernelopts-{pci,nopci}.txt' file, use that. Otherwise |     contains 'default-kernelopts-{pci,nopci}.txt' file, use that. Otherwise | ||||||
|     use built-in defaults. |     use built-in defaults. | ||||||
|     For qubes without PCI devices, kernelopts of qube's template are |     For qubes without PCI devices, kernelopts of qube's template are | ||||||
|     considered (for template-based qubes). |     considered (for template-based qubes). | ||||||
|     ''' |     """ | ||||||
|     if not self.kernel: |     if not self.kernel: | ||||||
|         return '' |         return '' | ||||||
|     if 'kernel' in self.volumes: |     if 'kernel' in self.volumes: | ||||||
| @ -186,7 +196,7 @@ def _default_kernelopts(self): | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | ||||||
|     '''Base functionality of Qubes VM shared between all VMs. |     """Base functionality of Qubes VM shared between all VMs. | ||||||
| 
 | 
 | ||||||
|     The following events are raised on this class or its subclasses: |     The following events are raised on this class or its subclasses: | ||||||
| 
 | 
 | ||||||
| @ -496,7 +506,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|             performing various post-installation setup. |             performing various post-installation setup. | ||||||
| 
 | 
 | ||||||
|             Handler for this event can be asynchronous (a coroutine). |             Handler for this event can be asynchronous (a coroutine). | ||||||
|     ''' |     """ | ||||||
| 
 | 
 | ||||||
|     # |     # | ||||||
|     # per-class properties |     # per-class properties | ||||||
| @ -508,42 +518,53 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|     # |     # | ||||||
|     # properties loaded from XML |     # properties loaded from XML | ||||||
|     # |     # | ||||||
|  |     guivm = qubes.VMProperty('guivm', load_stage=4, allow_none=True, | ||||||
|  |                              default=(lambda self: self.app.default_guivm), | ||||||
|  |                              doc='VM used for Gui') | ||||||
| 
 | 
 | ||||||
|     virt_mode = qubes.property('virt_mode', |     virt_mode = qubes.property( | ||||||
|  |         'virt_mode', | ||||||
|         type=str, setter=_setter_virt_mode, |         type=str, setter=_setter_virt_mode, | ||||||
|         default=_default_virt_mode, |         default=_default_virt_mode, | ||||||
|         doc='''Virtualisation mode: full virtualisation ("HVM"), |         doc="""Virtualisation mode: full virtualisation ("HVM"), | ||||||
|             or paravirtualisation ("PV"), or hybrid ("PVH"). TemplateBasedVMs use its ' |             or paravirtualisation ("PV"), or hybrid ("PVH"). | ||||||
|             'template\'s value by default.''') |              TemplateBasedVMs use its template\'s value by default.""") | ||||||
| 
 | 
 | ||||||
|     installed_by_rpm = qubes.property('installed_by_rpm', |     installed_by_rpm = qubes.property( | ||||||
|  |         'installed_by_rpm', | ||||||
|         type=bool, setter=qubes.property.bool, |         type=bool, setter=qubes.property.bool, | ||||||
|         default=False, |         default=False, | ||||||
|         doc='''If this domain's image was installed from package tracked by |         doc="""If this domain's image was installed from package tracked by | ||||||
|             package manager.''') |             package manager.""") | ||||||
| 
 | 
 | ||||||
|     memory = qubes.property('memory', type=int, |     memory = qubes.property( | ||||||
|  |         'memory', type=int, | ||||||
|         setter=_setter_positive_int, |         setter=_setter_positive_int, | ||||||
|         default=_default_with_template('memory', lambda self: |         default=_default_with_template( | ||||||
|  |             'memory', | ||||||
|  |             lambda self: | ||||||
|             qubes.config.defaults[ |             qubes.config.defaults[ | ||||||
|                 'hvm_memory' if self.virt_mode == 'hvm' else 'memory']), |                 'hvm_memory' if self.virt_mode == 'hvm' else 'memory']), | ||||||
|         doc='Memory currently available for this VM. TemplateBasedVMs use its ' |         doc='Memory currently available for this VM. TemplateBasedVMs use its ' | ||||||
|             'template\'s value by default.') |             'template\'s value by default.') | ||||||
| 
 | 
 | ||||||
|     maxmem = qubes.property('maxmem', type=int, |     maxmem = qubes.property( | ||||||
|  |         'maxmem', type=int, | ||||||
|         setter=_setter_non_negative_int, |         setter=_setter_non_negative_int, | ||||||
|         default=_default_maxmem, |         default=_default_maxmem, | ||||||
|         doc='''Maximum amount of memory available for this VM (for the purpose |         doc="""Maximum amount of memory available for this VM (for the purpose | ||||||
|             of the memory balancer). Set to 0 to disable memory balancing for |             of the memory balancer). Set to 0 to disable memory balancing for | ||||||
|             this qube. TemplateBasedVMs use its template\'s value by default |             this qube. TemplateBasedVMs use its template\'s value by default | ||||||
|             (unless memory balancing not supported for this qube).''') |             (unless memory balancing not supported for this qube).""") | ||||||
| 
 | 
 | ||||||
|     stubdom_mem = qubes.property('stubdom_mem', type=int, |     stubdom_mem = qubes.property( | ||||||
|  |         'stubdom_mem', type=int, | ||||||
|         setter=_setter_positive_int, |         setter=_setter_positive_int, | ||||||
|         default=None, |         default=None, | ||||||
|         doc='Memory amount allocated for the stubdom') |         doc='Memory amount allocated for the stubdom') | ||||||
| 
 | 
 | ||||||
|     vcpus = qubes.property('vcpus', |     vcpus = qubes.property( | ||||||
|  |         'vcpus', | ||||||
|         type=int, |         type=int, | ||||||
|         setter=_setter_positive_int, |         setter=_setter_positive_int, | ||||||
|         default=_default_with_template('vcpus', 2), |         default=_default_with_template('vcpus', 2), | ||||||
| @ -551,7 +572,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|             'template\'s value by default.') |             'template\'s value by default.') | ||||||
| 
 | 
 | ||||||
|     # CORE2: swallowed uses_default_kernel |     # CORE2: swallowed uses_default_kernel | ||||||
|     kernel = qubes.property('kernel', type=str, |     kernel = qubes.property( | ||||||
|  |         'kernel', type=str, | ||||||
|         setter=_setter_kernel, |         setter=_setter_kernel, | ||||||
|         default=_default_with_template('kernel', |         default=_default_with_template('kernel', | ||||||
|                                        lambda self: self.app.default_kernel), |                                        lambda self: self.app.default_kernel), | ||||||
| @ -560,78 +582,94 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     # CORE2: swallowed uses_default_kernelopts |     # CORE2: swallowed uses_default_kernelopts | ||||||
|     # pylint: disable=no-member |     # pylint: disable=no-member | ||||||
|     kernelopts = qubes.property('kernelopts', type=str, load_stage=4, |     kernelopts = qubes.property( | ||||||
|  |         'kernelopts', type=str, load_stage=4, | ||||||
|         default=_default_kernelopts, |         default=_default_kernelopts, | ||||||
|         doc='Kernel command line passed to domain. TemplateBasedVMs use its ' |         doc='Kernel command line passed to domain. TemplateBasedVMs use its ' | ||||||
|             'template\'s value by default.') |             'template\'s value by default.') | ||||||
| 
 | 
 | ||||||
|     debug = qubes.property('debug', type=bool, default=False, |     debug = qubes.property( | ||||||
|  |         'debug', type=bool, default=False, | ||||||
|         setter=qubes.property.bool, |         setter=qubes.property.bool, | ||||||
|         doc='Turns on debugging features.') |         doc='Turns on debugging features.') | ||||||
| 
 | 
 | ||||||
|     # XXX what this exactly does? |     # XXX what this exactly does? | ||||||
|     # XXX shouldn't this go to standalone VM and TemplateVM, and leave here |     # XXX shouldn't this go to standalone VM and TemplateVM, and leave here | ||||||
|     #     only plain property? |     #     only plain property? | ||||||
|     default_user = qubes.property('default_user', type=str, |     default_user = qubes.property( | ||||||
|  |         'default_user', type=str, | ||||||
|         # pylint: disable=no-member |         # pylint: disable=no-member | ||||||
|         default=_default_with_template('default_user', 'user'), |         default=_default_with_template('default_user', | ||||||
|  |                                        'user'), | ||||||
|         setter=_setter_default_user, |         setter=_setter_default_user, | ||||||
|         doc='Default user to start applications as. TemplateBasedVMs use its ' |         doc='Default user to start applications as. TemplateBasedVMs use its ' | ||||||
|             'template\'s value by default.') |             'template\'s value by default.') | ||||||
| 
 | 
 | ||||||
|     # pylint: enable=no-member |     # pylint: enable=no-member | ||||||
| 
 | 
 | ||||||
| #   @property |     #   @property | ||||||
| #   def default_user(self): |     #   def default_user(self): | ||||||
| #       if self.template is not None: |     #       if self.template is not None: | ||||||
| #           return self.template.default_user |     #           return self.template.default_user | ||||||
| #       else: |     #       else: | ||||||
| #           return self._default_user |     #           return self._default_user | ||||||
| 
 | 
 | ||||||
|     qrexec_timeout = qubes.property('qrexec_timeout', type=int, |     qrexec_timeout = qubes.property( | ||||||
|         default=_default_with_template('qrexec_timeout', |         'qrexec_timeout', type=int, | ||||||
|  |         default=_default_with_template( | ||||||
|  |             'qrexec_timeout', | ||||||
|             lambda self: self.app.default_qrexec_timeout), |             lambda self: self.app.default_qrexec_timeout), | ||||||
|         setter=_setter_positive_int, |         setter=_setter_positive_int, | ||||||
|         doc='''Time in seconds after which qrexec connection attempt is deemed |         doc="""Time in seconds after which qrexec connection attempt is deemed | ||||||
|             failed. Operating system inside VM should be able to boot in this |             failed. Operating system inside VM should be able to boot in this | ||||||
|             time.''') |             time.""") | ||||||
| 
 | 
 | ||||||
|     shutdown_timeout = qubes.property('shutdown_timeout', type=int, |     shutdown_timeout = qubes.property( | ||||||
|         default=_default_with_template('shutdown_timeout', |         'shutdown_timeout', type=int, | ||||||
|  |         default=_default_with_template( | ||||||
|  |             'shutdown_timeout', | ||||||
|             lambda self: self.app.default_shutdown_timeout), |             lambda self: self.app.default_shutdown_timeout), | ||||||
|         setter=_setter_positive_int, |         setter=_setter_positive_int, | ||||||
|         doc='''Time in seconds for shutdown of the VM, after which VM may be |         doc="""Time in seconds for shutdown of the VM, after which VM may be | ||||||
|             forcefully powered off. Operating system inside VM should be |             forcefully powered off. Operating system inside VM should be | ||||||
|             able to fully shutdown in this time.''') |             able to fully shutdown in this time.""") | ||||||
| 
 | 
 | ||||||
|     autostart = qubes.property('autostart', default=False, |     autostart = qubes.property( | ||||||
|  |         'autostart', default=False, | ||||||
|         type=bool, setter=qubes.property.bool, |         type=bool, setter=qubes.property.bool, | ||||||
|         doc='''Setting this to `True` means that VM should be autostarted on |         doc="""Setting this to `True` means that VM should be autostarted on | ||||||
|             dom0 boot.''') |             dom0 boot.""") | ||||||
| 
 | 
 | ||||||
|     include_in_backups = qubes.property('include_in_backups', |     include_in_backups = qubes.property( | ||||||
|  |         'include_in_backups', | ||||||
|         default=True, |         default=True, | ||||||
|         type=bool, setter=qubes.property.bool, |         type=bool, setter=qubes.property.bool, | ||||||
|         doc='If this domain is to be included in default backup.') |         doc='If this domain is to be included in default backup.') | ||||||
| 
 | 
 | ||||||
|     backup_timestamp = qubes.property('backup_timestamp', default=None, |     backup_timestamp = qubes.property( | ||||||
|  |         'backup_timestamp', default=None, | ||||||
|         type=int, |         type=int, | ||||||
|         doc='Time of last backup of the qube, in seconds since unix epoch') |         doc='Time of last backup of the qube, in seconds since unix epoch') | ||||||
| 
 | 
 | ||||||
|     default_dispvm = qubes.VMProperty('default_dispvm', |     default_dispvm = qubes.VMProperty( | ||||||
|  |         'default_dispvm', | ||||||
|         load_stage=4, |         load_stage=4, | ||||||
|         allow_none=True, |         allow_none=True, | ||||||
|         default=(lambda self: self.app.default_dispvm), |         default=( | ||||||
|  |             lambda self: self.app.default_dispvm), | ||||||
|         doc='Default VM to be used as Disposable VM for service calls.') |         doc='Default VM to be used as Disposable VM for service calls.') | ||||||
| 
 | 
 | ||||||
|     management_dispvm = qubes.VMProperty('management_dispvm', |     management_dispvm = qubes.VMProperty( | ||||||
|  |         'management_dispvm', | ||||||
|         load_stage=4, |         load_stage=4, | ||||||
|         allow_none=True, |         allow_none=True, | ||||||
|         default=_default_with_template('management_dispvm', |         default=_default_with_template( | ||||||
|  |             'management_dispvm', | ||||||
|             (lambda self: self.app.management_dispvm)), |             (lambda self: self.app.management_dispvm)), | ||||||
|         doc='Default DVM template for Disposable VM for managing this VM.') |         doc='Default DVM template for Disposable VM for managing this VM.') | ||||||
| 
 | 
 | ||||||
|     updateable = qubes.property('updateable', |     updateable = qubes.property( | ||||||
|  |         'updateable', | ||||||
|         default=(lambda self: not hasattr(self, 'template')), |         default=(lambda self: not hasattr(self, 'template')), | ||||||
|         type=bool, |         type=bool, | ||||||
|         setter=qubes.property.forbidden, |         setter=qubes.property.forbidden, | ||||||
| @ -652,10 +690,10 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @qubes.stateless_property |     @qubes.stateless_property | ||||||
|     def xid(self): |     def xid(self): | ||||||
|         '''Xen ID. |         """Xen ID. | ||||||
| 
 | 
 | ||||||
|         Or not Xen, but ID. |         Or not Xen, but ID. | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         if self.libvirt_domain is None: |         if self.libvirt_domain is None: | ||||||
|             return -1 |             return -1 | ||||||
| @ -676,8 +714,9 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|         if self.app.vmm.xs is None: |         if self.app.vmm.xs is None: | ||||||
|             return -1 |             return -1 | ||||||
| 
 | 
 | ||||||
|         stubdom_xid_str = self.app.vmm.xs.read('', |         stubdom_xid_str = self.app.vmm.xs.read( | ||||||
|             '/local/domain/{}/image/device-model-domid'.format(self.xid)) |             '', '/local/domain/{}/image/device-model-domid'.format( | ||||||
|  |                 self.xid)) | ||||||
|         if stubdom_xid_str is None or not stubdom_xid_str.isdigit(): |         if stubdom_xid_str is None or not stubdom_xid_str.isdigit(): | ||||||
|             return -1 |             return -1 | ||||||
| 
 | 
 | ||||||
| @ -702,10 +741,10 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def libvirt_domain(self): |     def libvirt_domain(self): | ||||||
|         '''Libvirt domain object from libvirt. |         """Libvirt domain object from libvirt. | ||||||
| 
 | 
 | ||||||
|         May be :py:obj:`None`, if libvirt knows nothing about this domain. |         May be :py:obj:`None`, if libvirt knows nothing about this domain. | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         if self._libvirt_domain is not None: |         if self._libvirt_domain is not None: | ||||||
|             return self._libvirt_domain |             return self._libvirt_domain | ||||||
| @ -726,9 +765,9 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def block_devices(self): |     def block_devices(self): | ||||||
|         ''' Return all :py:class:`qubes.storage.BlockDevice` for current domain |         """ Return all :py:class:`qubes.storage.BlockDevice` for current domain | ||||||
|         for serialization in the libvirt XML template as <disk>. |         for serialization in the libvirt XML template as <disk>. | ||||||
|         ''' |         """ | ||||||
|         for v in self.volumes.values(): |         for v in self.volumes.values(): | ||||||
|             block_dev = v.block_device() |             block_dev = v.block_device() | ||||||
|             if block_dev is not None: |             if block_dev is not None: | ||||||
| @ -736,7 +775,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def untrusted_qdb(self): |     def untrusted_qdb(self): | ||||||
|         '''QubesDB handle for this domain.''' |         """QubesDB handle for this domain.""" | ||||||
|         if self._qdb_connection is None: |         if self._qdb_connection is None: | ||||||
|             if self.is_running(): |             if self.is_running(): | ||||||
|                 import qubesdb  # pylint: disable=import-error |                 import qubesdb  # pylint: disable=import-error | ||||||
| @ -745,7 +784,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def dir_path(self): |     def dir_path(self): | ||||||
|         '''Root directory for files related to this domain''' |         """Root directory for files related to this domain""" | ||||||
|         return os.path.join( |         return os.path.join( | ||||||
|             qubes.config.qubes_base_dir, |             qubes.config.qubes_base_dir, | ||||||
|             self.dir_path_prefix, |             self.dir_path_prefix, | ||||||
| @ -918,15 +957,17 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|             qubes.config.system_path['qubes_kernels_base_dir'], |             qubes.config.system_path['qubes_kernels_base_dir'], | ||||||
|             newvalue) |             newvalue) | ||||||
|         if not os.path.exists(dirname): |         if not os.path.exists(dirname): | ||||||
|             raise qubes.exc.QubesPropertyValueError(self, |             raise qubes.exc.QubesPropertyValueError( | ||||||
|                 self.property_get_def(name), newvalue, |                 self, self.property_get_def(name), newvalue, | ||||||
|                 'Kernel {!r} not installed'.format(newvalue)) |                 'Kernel {!r} not installed'.format( | ||||||
|  |                     newvalue)) | ||||||
|         for filename in ('vmlinuz', 'initramfs'): |         for filename in ('vmlinuz', 'initramfs'): | ||||||
|             if not os.path.exists(os.path.join(dirname, filename)): |             if not os.path.exists(os.path.join(dirname, filename)): | ||||||
|                 raise qubes.exc.QubesPropertyValueError(self, |                 raise qubes.exc.QubesPropertyValueError( | ||||||
|                     self.property_get_def(name), newvalue, |                     self, self.property_get_def(name), newvalue, | ||||||
|                     'Kernel {!r} not properly installed: ' |                     'Kernel {!r} not properly installed: ' | ||||||
|                     'missing {!r} file'.format(newvalue, filename)) |                     'missing {!r} file'.format( | ||||||
|  |                         newvalue, filename)) | ||||||
| 
 | 
 | ||||||
|     @qubes.events.handler('property-pre-set:autostart') |     @qubes.events.handler('property-pre-set:autostart') | ||||||
|     def on_property_pre_set_autostart(self, event, name, newvalue, |     def on_property_pre_set_autostart(self, event, name, newvalue, | ||||||
| @ -980,9 +1021,9 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def _ensure_shutdown_handled(self): |     def _ensure_shutdown_handled(self): | ||||||
|         '''Make sure previous shutdown is fully handled. |         """Make sure previous shutdown is fully handled. | ||||||
|         MUST NOT be called when domain is running. |         MUST NOT be called when domain is running. | ||||||
|         ''' |         """ | ||||||
|         with (yield from self._domain_stopped_lock): |         with (yield from self._domain_stopped_lock): | ||||||
|             # Don't accept any new stopped event's till a new VM has been |             # Don't accept any new stopped event's till a new VM has been | ||||||
|             # created. If we didn't received any stopped event or it wasn't |             # created. If we didn't received any stopped event or it wasn't | ||||||
| @ -1007,16 +1048,15 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|                 yield from self.fire_event_async('domain-stopped') |                 yield from self.fire_event_async('domain-stopped') | ||||||
|                 yield from self.fire_event_async('domain-shutdown') |                 yield from self.fire_event_async('domain-shutdown') | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def start(self, start_guid=True, notify_function=None, |     def start(self, start_guid=True, notify_function=None, | ||||||
|               mem_required=None): |               mem_required=None): | ||||||
|         '''Start domain |         """Start domain | ||||||
| 
 | 
 | ||||||
|         :param bool start_guid: FIXME |         :param bool start_guid: FIXME | ||||||
|         :param collections.Callable notify_function: FIXME |         :param collections.Callable notify_function: FIXME | ||||||
|         :param int mem_required: FIXME |         :param int mem_required: FIXME | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         with (yield from self.startup_lock): |         with (yield from self.startup_lock): | ||||||
|             # check if domain wasn't removed in the meantime |             # check if domain wasn't removed in the meantime | ||||||
| @ -1034,7 +1074,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|             try: |             try: | ||||||
|                 yield from self.fire_event_async('domain-pre-start', |                 yield from self.fire_event_async('domain-pre-start', | ||||||
|                                                  pre_event=True, |                                                  pre_event=True, | ||||||
|                     start_guid=start_guid, mem_required=mem_required) |                                                  start_guid=start_guid, | ||||||
|  |                                                  mem_required=mem_required) | ||||||
|             except Exception as exc: |             except Exception as exc: | ||||||
|                 yield from self.fire_event_async('domain-start-failed', |                 yield from self.fire_event_async('domain-start-failed', | ||||||
|                                                  reason=str(exc)) |                                                  reason=str(exc)) | ||||||
| @ -1058,10 +1099,11 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|                     # pylint: disable = no-member |                     # pylint: disable = no-member | ||||||
|                     if self.netvm.qid != 0: |                     if self.netvm.qid != 0: | ||||||
|                         if not self.netvm.is_running(): |                         if not self.netvm.is_running(): | ||||||
|                             yield from self.netvm.start(start_guid=start_guid, |                             yield from self.netvm.start( | ||||||
|  |                                 start_guid=start_guid, | ||||||
|                                 notify_function=notify_function) |                                 notify_function=notify_function) | ||||||
| 
 | 
 | ||||||
|                 qmemman_client = yield from asyncio.get_event_loop().\ |                 qmemman_client = yield from asyncio.get_event_loop(). \ | ||||||
|                     run_in_executor(None, self.request_memory, mem_required) |                     run_in_executor(None, self.request_memory, mem_required) | ||||||
| 
 | 
 | ||||||
|                 yield from self.storage.start() |                 yield from self.storage.start() | ||||||
| @ -1130,11 +1172,11 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|         return self |         return self | ||||||
| 
 | 
 | ||||||
|     def on_libvirt_domain_stopped(self): |     def on_libvirt_domain_stopped(self): | ||||||
|         ''' Handle VIR_DOMAIN_EVENT_STOPPED events from libvirt. |         """ Handle VIR_DOMAIN_EVENT_STOPPED events from libvirt. | ||||||
| 
 | 
 | ||||||
|         This is not a Qubes event handler. Instead we do some sanity checks |         This is not a Qubes event handler. Instead we do some sanity checks | ||||||
|         and synchronization with start() and then emits Qubes events. |         and synchronization with start() and then emits Qubes events. | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         state = self.get_power_state() |         state = self.get_power_state() | ||||||
|         if state not in ['Halted', 'Crashed', 'Dying']: |         if state not in ['Halted', 'Crashed', 'Dying']: | ||||||
| @ -1169,7 +1211,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|     @qubes.events.handler('domain-stopped') |     @qubes.events.handler('domain-stopped') | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def on_domain_stopped(self, _event, **_kwargs): |     def on_domain_stopped(self, _event, **_kwargs): | ||||||
|         '''Cleanup after domain was stopped''' |         """Cleanup after domain was stopped""" | ||||||
|         try: |         try: | ||||||
|             yield from self.storage.stop() |             yield from self.storage.stop() | ||||||
|         except qubes.storage.StoragePoolException: |         except qubes.storage.StoragePoolException: | ||||||
| @ -1178,7 +1220,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def shutdown(self, force=False, wait=False, timeout=None): |     def shutdown(self, force=False, wait=False, timeout=None): | ||||||
|         '''Shutdown domain. |         """Shutdown domain. | ||||||
| 
 | 
 | ||||||
|         :param force: ignored |         :param force: ignored | ||||||
|         :param wait: wait for shutdown to complete |         :param wait: wait for shutdown to complete | ||||||
| @ -1186,7 +1228,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|         :py:attr:`shutdown_timeout` |         :py:attr:`shutdown_timeout` | ||||||
|         :raises qubes.exc.QubesVMNotStartedError: \ |         :raises qubes.exc.QubesVMNotStartedError: \ | ||||||
|             when domain is already shut down. |             when domain is already shut down. | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         if self.is_halted(): |         if self.is_halted(): | ||||||
|             raise qubes.exc.QubesVMNotStartedError(self) |             raise qubes.exc.QubesVMNotStartedError(self) | ||||||
| @ -1218,11 +1260,11 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def kill(self): |     def kill(self): | ||||||
|         '''Forcefully shutdown (destroy) domain. |         """Forcefully shutdown (destroy) domain. | ||||||
| 
 | 
 | ||||||
|         :raises qubes.exc.QubesVMNotStartedError: \ |         :raises qubes.exc.QubesVMNotStartedError: \ | ||||||
|             when domain is already shut down. |             when domain is already shut down. | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         if not self.is_running() and not self.is_paused(): |         if not self.is_running() and not self.is_paused(): | ||||||
|             raise qubes.exc.QubesVMNotStartedError(self) |             raise qubes.exc.QubesVMNotStartedError(self) | ||||||
| @ -1234,9 +1276,9 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def _kill_locked(self): |     def _kill_locked(self): | ||||||
|         '''Forcefully shutdown (destroy) domain. |         """Forcefully shutdown (destroy) domain. | ||||||
| 
 | 
 | ||||||
|         This function needs to be called with self.startup_lock held.''' |         This function needs to be called with self.startup_lock held.""" | ||||||
|         try: |         try: | ||||||
|             self.libvirt_domain.destroy() |             self.libvirt_domain.destroy() | ||||||
|         except libvirt.libvirtError as e: |         except libvirt.libvirtError as e: | ||||||
| @ -1248,7 +1290,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|         yield from self._ensure_shutdown_handled() |         yield from self._ensure_shutdown_handled() | ||||||
| 
 | 
 | ||||||
|     def force_shutdown(self, *args, **kwargs): |     def force_shutdown(self, *args, **kwargs): | ||||||
|         '''Deprecated alias for :py:meth:`kill`''' |         """Deprecated alias for :py:meth:`kill`""" | ||||||
|         warnings.warn( |         warnings.warn( | ||||||
|             'Call to deprecated function force_shutdown(), use kill() instead', |             'Call to deprecated function force_shutdown(), use kill() instead', | ||||||
|             DeprecationWarning, stacklevel=2) |             DeprecationWarning, stacklevel=2) | ||||||
| @ -1256,11 +1298,11 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def suspend(self): |     def suspend(self): | ||||||
|         '''Suspend (pause) domain. |         """Suspend (pause) domain. | ||||||
| 
 | 
 | ||||||
|         :raises qubes.exc.QubesVMNotRunnignError: \ |         :raises qubes.exc.QubesVMNotRunnignError: \ | ||||||
|             when domain is already shut down. |             when domain is already shut down. | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         if not self.is_running() and not self.is_paused(): |         if not self.is_running() and not self.is_paused(): | ||||||
|             raise qubes.exc.QubesVMNotRunningError(self) |             raise qubes.exc.QubesVMNotRunningError(self) | ||||||
| @ -1278,7 +1320,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def pause(self): |     def pause(self): | ||||||
|         '''Pause (suspend) domain.''' |         """Pause (suspend) domain.""" | ||||||
| 
 | 
 | ||||||
|         if not self.is_running(): |         if not self.is_running(): | ||||||
|             raise qubes.exc.QubesVMNotRunningError(self) |             raise qubes.exc.QubesVMNotRunningError(self) | ||||||
| @ -1289,11 +1331,11 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def resume(self): |     def resume(self): | ||||||
|         '''Resume suspended domain. |         """Resume suspended domain. | ||||||
| 
 | 
 | ||||||
|         :raises qubes.exc.QubesVMNotSuspendedError: when machine is not paused |         :raises qubes.exc.QubesVMNotSuspendedError: when machine is not paused | ||||||
|         :raises qubes.exc.QubesVMError: when machine is suspended |         :raises qubes.exc.QubesVMError: when machine is suspended | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         # pylint: disable=not-an-iterable |         # pylint: disable=not-an-iterable | ||||||
|         if self.get_power_state() == "Suspended": |         if self.get_power_state() == "Suspended": | ||||||
| @ -1308,7 +1350,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def unpause(self): |     def unpause(self): | ||||||
|         '''Resume (unpause) a domain''' |         """Resume (unpause) a domain""" | ||||||
|         if not self.is_paused(): |         if not self.is_paused(): | ||||||
|             raise qubes.exc.QubesVMNotPausedError(self) |             raise qubes.exc.QubesVMNotPausedError(self) | ||||||
| 
 | 
 | ||||||
| @ -1319,7 +1361,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def run_service(self, service, source=None, user=None, |     def run_service(self, service, source=None, user=None, | ||||||
|                     filter_esc=False, autostart=False, gui=False, **kwargs): |                     filter_esc=False, autostart=False, gui=False, **kwargs): | ||||||
|         '''Run service on this VM |         """Run service on this VM | ||||||
| 
 | 
 | ||||||
|         :param str service: service name |         :param str service: service name | ||||||
|         :param qubes.vm.qubesvm.QubesVM source: source domain as presented to |         :param qubes.vm.qubesvm.QubesVM source: source domain as presented to | ||||||
| @ -1334,7 +1376,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|         .. note:: |         .. note:: | ||||||
|             User ``root`` is redefined to ``SYSTEM`` in the Windows agent code |             User ``root`` is redefined to ``SYSTEM`` in the Windows agent code | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         # UNSUPPORTED from previous incarnation: |         # UNSUPPORTED from previous incarnation: | ||||||
|         #   localcmd, wait, passio*, notify_function, `-e` switch |         #   localcmd, wait, passio*, notify_function, `-e` switch | ||||||
| @ -1375,7 +1417,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def run_service_for_stdio(self, *args, input=None, **kwargs): |     def run_service_for_stdio(self, *args, input=None, **kwargs): | ||||||
|         '''Run a service, pass an optional input and return (stdout, stderr). |         """Run a service, pass an optional input and return (stdout, stderr). | ||||||
| 
 | 
 | ||||||
|         Raises an exception if return code != 0. |         Raises an exception if return code != 0. | ||||||
| 
 | 
 | ||||||
| @ -1385,7 +1427,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|             There are some combinations if stdio-related *kwargs*, which are |             There are some combinations if stdio-related *kwargs*, which are | ||||||
|             not filtered for problems originating between the keyboard and the |             not filtered for problems originating between the keyboard and the | ||||||
|             chair. |             chair. | ||||||
|         '''  # pylint: disable=redefined-builtin |         """  # pylint: disable=redefined-builtin | ||||||
| 
 | 
 | ||||||
|         kwargs.setdefault('stdin', subprocess.PIPE) |         kwargs.setdefault('stdin', subprocess.PIPE) | ||||||
|         kwargs.setdefault('stdout', subprocess.PIPE) |         kwargs.setdefault('stdout', subprocess.PIPE) | ||||||
| @ -1403,17 +1445,17 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def _prepare_input_for_vmshell(command, input): |     def _prepare_input_for_vmshell(command, input): | ||||||
|         '''Prepare shell input for the given command and optional (real) input |         """Prepare shell input for the given command and optional (real) input | ||||||
|         '''  # pylint: disable=redefined-builtin |         """  # pylint: disable=redefined-builtin | ||||||
|         if input is None: |         if input is None: | ||||||
|             input = b'' |             input = b'' | ||||||
|         return b''.join((command.rstrip('\n').encode('utf-8'), b'\n', input)) |         return b''.join((command.rstrip('\n').encode('utf-8'), b'\n', input)) | ||||||
| 
 | 
 | ||||||
|     def run(self, command, user=None, **kwargs): |     def run(self, command, user=None, **kwargs): | ||||||
|         '''Run a shell command inside the domain using qrexec. |         """Run a shell command inside the domain using qrexec. | ||||||
| 
 | 
 | ||||||
|         This method is a coroutine. |         This method is a coroutine. | ||||||
|         '''  # pylint: disable=redefined-builtin |         """  # pylint: disable=redefined-builtin | ||||||
| 
 | 
 | ||||||
|         if user is None: |         if user is None: | ||||||
|             user = self.default_user |             user = self.default_user | ||||||
| @ -1426,13 +1468,13 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def run_for_stdio(self, *args, input=None, **kwargs): |     def run_for_stdio(self, *args, input=None, **kwargs): | ||||||
|         '''Run a shell command inside the domain using qubes.VMShell qrexec. |         """Run a shell command inside the domain using qubes.VMShell qrexec. | ||||||
| 
 | 
 | ||||||
|         This method is a coroutine. |         This method is a coroutine. | ||||||
| 
 | 
 | ||||||
|         *kwargs* are passed verbatim to :py:meth:`run_service_for_stdio`. |         *kwargs* are passed verbatim to :py:meth:`run_service_for_stdio`. | ||||||
|         See disclaimer there. |         See disclaimer there. | ||||||
|         '''  # pylint: disable=redefined-builtin |         """  # pylint: disable=redefined-builtin | ||||||
| 
 | 
 | ||||||
|         kwargs.setdefault('stdin', subprocess.PIPE) |         kwargs.setdefault('stdin', subprocess.PIPE) | ||||||
|         kwargs.setdefault('stdout', subprocess.PIPE) |         kwargs.setdefault('stdout', subprocess.PIPE) | ||||||
| @ -1447,7 +1489,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|         return stdouterr |         return stdouterr | ||||||
| 
 | 
 | ||||||
|     def is_memory_balancing_possible(self): |     def is_memory_balancing_possible(self): | ||||||
|         '''Check if memory balancing can be enabled. |         """Check if memory balancing can be enabled. | ||||||
|         Reasons to not enable it: |         Reasons to not enable it: | ||||||
|          - have PCI devices |          - have PCI devices | ||||||
|          - balloon driver not present |          - balloon driver not present | ||||||
| @ -1456,7 +1498,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|         heuristic is HVM virt_mode (PV and PVH require OS support and it does |         heuristic is HVM virt_mode (PV and PVH require OS support and it does | ||||||
|         include balloon driver) and lack of qrexec/meminfo-writer service |         include balloon driver) and lack of qrexec/meminfo-writer service | ||||||
|         support (no qubes tools installed). |         support (no qubes tools installed). | ||||||
|         ''' |         """ | ||||||
|         if list(self.devices['pci'].persistent()): |         if list(self.devices['pci'].persistent()): | ||||||
|             return False |             return False | ||||||
|         if self.virt_mode == 'hvm': |         if self.virt_mode == 'hvm': | ||||||
| @ -1469,7 +1511,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|             supported_services = any(f.startswith('supported-service.') |             supported_services = any(f.startswith('supported-service.') | ||||||
|                                      for f in features_set) |                                      for f in features_set) | ||||||
|             if (not self.features.check_with_template('qrexec', False) or |             if (not self.features.check_with_template('qrexec', False) or | ||||||
|                 (supported_services and not self.features.check_with_template( |                     (supported_services and | ||||||
|  |                      not self.features.check_with_template( | ||||||
|                          'supported-service.meminfo-writer', False))): |                          'supported-service.meminfo-writer', False))): | ||||||
|                 return False |                 return False | ||||||
|         return True |         return True | ||||||
| @ -1513,7 +1556,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|     @staticmethod |     @staticmethod | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def start_daemon(*command, input=None, **kwargs): |     def start_daemon(*command, input=None, **kwargs): | ||||||
|         '''Start a daemon for the VM |         """Start a daemon for the VM | ||||||
| 
 | 
 | ||||||
|         This function take care to run it as appropriate user. |         This function take care to run it as appropriate user. | ||||||
| 
 | 
 | ||||||
| @ -1521,7 +1564,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|             :py:meth:`subprocess.check_call`) |             :py:meth:`subprocess.check_call`) | ||||||
|         :param kwargs: args for :py:meth:`subprocess.check_call` |         :param kwargs: args for :py:meth:`subprocess.check_call` | ||||||
|         :return: None |         :return: None | ||||||
|         '''  # pylint: disable=redefined-builtin |         """  # pylint: disable=redefined-builtin | ||||||
| 
 | 
 | ||||||
|         if os.getuid() == 0: |         if os.getuid() == 0: | ||||||
|             # try to always have VM daemons running as normal user, otherwise |             # try to always have VM daemons running as normal user, otherwise | ||||||
| @ -1538,10 +1581,10 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def start_qrexec_daemon(self): |     def start_qrexec_daemon(self): | ||||||
|         '''Start qrexec daemon. |         """Start qrexec daemon. | ||||||
| 
 | 
 | ||||||
|         :raises OSError: when starting fails. |         :raises OSError: when starting fails. | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         self.log.debug('Starting the qrexec daemon') |         self.log.debug('Starting the qrexec daemon') | ||||||
|         qrexec_args = [str(self.xid), self.name, self.default_user] |         qrexec_args = [str(self.xid), self.name, self.default_user] | ||||||
| @ -1562,20 +1605,21 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|                 env=qrexec_env, stderr=subprocess.PIPE) |                 env=qrexec_env, stderr=subprocess.PIPE) | ||||||
|         except subprocess.CalledProcessError as err: |         except subprocess.CalledProcessError as err: | ||||||
|             if err.returncode == 3: |             if err.returncode == 3: | ||||||
|                 raise qubes.exc.QubesVMError(self, |                 raise qubes.exc.QubesVMError( | ||||||
|  |                     self, | ||||||
|                     'Cannot connect to qrexec agent for {} seconds, ' |                     'Cannot connect to qrexec agent for {} seconds, ' | ||||||
|                     'see /var/log/xen/console/guest-{}.log for details'.format( |                     'see /var/log/xen/console/guest-{}.log for details'.format( | ||||||
|                         self.qrexec_timeout, self.name |                         self.qrexec_timeout, self.name | ||||||
|                     )) |                     )) | ||||||
|             raise qubes.exc.QubesVMError(self, |             raise qubes.exc.QubesVMError( | ||||||
|                 'qrexec-daemon startup failed: ' + err.stderr.decode()) |                 self, 'qrexec-daemon startup failed: ' + err.stderr.decode()) | ||||||
| 
 | 
 | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def start_qubesdb(self): |     def start_qubesdb(self): | ||||||
|         '''Start QubesDB daemon. |         """Start QubesDB daemon. | ||||||
| 
 | 
 | ||||||
|         :raises OSError: when starting fails. |         :raises OSError: when starting fails. | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         # drop old connection to QubesDB, if any |         # drop old connection to QubesDB, if any | ||||||
|         self._qdb_connection = None |         self._qdb_connection = None | ||||||
| @ -1591,8 +1635,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def create_on_disk(self, pool=None, pools=None): |     def create_on_disk(self, pool=None, pools=None): | ||||||
|         '''Create files needed for VM. |         """Create files needed for VM. | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         self.log.info('Creating directory: {0}'.format(self.dir_path)) |         self.log.info('Creating directory: {0}'.format(self.dir_path)) | ||||||
|         os.makedirs(self.dir_path, mode=0o775, exist_ok=True) |         os.makedirs(self.dir_path, mode=0o775, exist_ok=True) | ||||||
| @ -1628,7 +1672,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def remove_from_disk(self): |     def remove_from_disk(self): | ||||||
|         '''Remove domain remnants from disk.''' |         """Remove domain remnants from disk.""" | ||||||
|         if not self.is_halted(): |         if not self.is_halted(): | ||||||
|             raise qubes.exc.QubesVMNotHaltedError( |             raise qubes.exc.QubesVMNotHaltedError( | ||||||
|                 "Can't remove VM {!s}, because it's in state {!r}.".format( |                 "Can't remove VM {!s}, because it's in state {!r}.".format( | ||||||
| @ -1652,10 +1696,10 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def clone_disk_files(self, src, pool=None, pools=None, ): |     def clone_disk_files(self, src, pool=None, pools=None, ): | ||||||
|         '''Clone files from other vm. |         """Clone files from other vm. | ||||||
| 
 | 
 | ||||||
|         :param qubes.vm.qubesvm.QubesVM src: source VM |         :param qubes.vm.qubesvm.QubesVM src: source VM | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         # If the current vm name is not a part of `self.app.domains.keys()`, |         # If the current vm name is not a part of `self.app.domains.keys()`, | ||||||
|         # then the current vm is in creation process. Calling |         # then the current vm is in creation process. Calling | ||||||
| @ -1706,7 +1750,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|     # state of the machine |     # state of the machine | ||||||
| 
 | 
 | ||||||
|     def get_power_state(self): |     def get_power_state(self): | ||||||
|         '''Return power state description string. |         """Return power state description string. | ||||||
| 
 | 
 | ||||||
|         Return value may be one of those: |         Return value may be one of those: | ||||||
| 
 | 
 | ||||||
| @ -1804,7 +1848,7 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|             https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainState |             https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainState | ||||||
|                 Libvirt's enum describing precise state of a domain. |                 Libvirt's enum describing precise state of a domain. | ||||||
|         '''  # pylint: disable=too-many-return-statements |         """  # pylint: disable=too-many-return-statements | ||||||
| 
 | 
 | ||||||
|         # don't try to define libvirt domain, if it isn't there, VM surely |         # don't try to define libvirt domain, if it isn't there, VM surely | ||||||
|         # isn't running |         # isn't running | ||||||
| @ -1836,7 +1880,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|                     return "Halting" |                     return "Halting" | ||||||
|                 if libvirt_domain.state()[0] == libvirt.VIR_DOMAIN_SHUTOFF: |                 if libvirt_domain.state()[0] == libvirt.VIR_DOMAIN_SHUTOFF: | ||||||
|                     return "Dying" |                     return "Dying" | ||||||
|                 if libvirt_domain.state()[0] == libvirt.VIR_DOMAIN_PMSUSPENDED:  # nopep8 |                 if libvirt_domain.state()[ | ||||||
|  |                     0] == libvirt.VIR_DOMAIN_PMSUSPENDED:  # nopep8 | ||||||
|                     return "Suspended" |                     return "Suspended" | ||||||
|                 if not self.is_fully_usable(): |                 if not self.is_fully_usable(): | ||||||
|                     return "Transient" |                     return "Transient" | ||||||
| @ -1851,20 +1896,20 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|         assert False |         assert False | ||||||
| 
 | 
 | ||||||
|     def is_halted(self): |     def is_halted(self): | ||||||
|         ''' Check whether this domain's state is 'Halted' |         """ Check whether this domain's state is 'Halted' | ||||||
|             :returns: :py:obj:`True` if this domain is halted, \ |             :returns: :py:obj:`True` if this domain is halted, \ | ||||||
|                 :py:obj:`False` otherwise. |                 :py:obj:`False` otherwise. | ||||||
|             :rtype: bool |             :rtype: bool | ||||||
|         ''' |         """ | ||||||
|         return self.get_power_state() == 'Halted' |         return self.get_power_state() == 'Halted' | ||||||
| 
 | 
 | ||||||
|     def is_running(self): |     def is_running(self): | ||||||
|         '''Check whether this domain is running. |         """Check whether this domain is running. | ||||||
| 
 | 
 | ||||||
|         :returns: :py:obj:`True` if this domain is started, \ |         :returns: :py:obj:`True` if this domain is started, \ | ||||||
|             :py:obj:`False` otherwise. |             :py:obj:`False` otherwise. | ||||||
|         :rtype: bool |         :rtype: bool | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         if self.app.vmm.offline_mode: |         if self.app.vmm.offline_mode: | ||||||
|             return False |             return False | ||||||
| @ -1885,23 +1930,23 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|         return bool(self.libvirt_domain.isActive()) |         return bool(self.libvirt_domain.isActive()) | ||||||
| 
 | 
 | ||||||
|     def is_paused(self): |     def is_paused(self): | ||||||
|         '''Check whether this domain is paused. |         """Check whether this domain is paused. | ||||||
| 
 | 
 | ||||||
|         :returns: :py:obj:`True` if this domain is paused, \ |         :returns: :py:obj:`True` if this domain is paused, \ | ||||||
|             :py:obj:`False` otherwise. |             :py:obj:`False` otherwise. | ||||||
|         :rtype: bool |         :rtype: bool | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         return self.libvirt_domain \ |         return self.libvirt_domain \ | ||||||
|                and self.libvirt_domain.state()[0] == libvirt.VIR_DOMAIN_PAUSED |                and self.libvirt_domain.state()[0] == libvirt.VIR_DOMAIN_PAUSED | ||||||
| 
 | 
 | ||||||
|     def is_qrexec_running(self): |     def is_qrexec_running(self): | ||||||
|         '''Check whether qrexec for this domain is available. |         """Check whether qrexec for this domain is available. | ||||||
| 
 | 
 | ||||||
|         :returns: :py:obj:`True` if qrexec is running, \ |         :returns: :py:obj:`True` if qrexec is running, \ | ||||||
|             :py:obj:`False` otherwise. |             :py:obj:`False` otherwise. | ||||||
|         :rtype: bool |         :rtype: bool | ||||||
|         ''' |         """ | ||||||
|         if self.xid < 0:  # pylint: disable=comparison-with-callable |         if self.xid < 0:  # pylint: disable=comparison-with-callable | ||||||
|             return False |             return False | ||||||
|         return os.path.exists('/var/run/qubes/qrexec.%s' % self.name) |         return os.path.exists('/var/run/qubes/qrexec.%s' % self.name) | ||||||
| @ -1911,10 +1956,10 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @qubes.events.handler('domain-is-fully-usable') |     @qubes.events.handler('domain-is-fully-usable') | ||||||
|     def on_domain_is_fully_usable(self, event): |     def on_domain_is_fully_usable(self, event): | ||||||
|         '''Check whether domain is running and sane. |         """Check whether domain is running and sane. | ||||||
| 
 | 
 | ||||||
|         Currently this checks for running qrexec. |         Currently this checks for running qrexec. | ||||||
|         '''  # pylint: disable=unused-argument |         """  # pylint: disable=unused-argument | ||||||
| 
 | 
 | ||||||
|         # Running gui-daemon implies also VM running |         # Running gui-daemon implies also VM running | ||||||
|         if not self.is_qrexec_running(): |         if not self.is_qrexec_running(): | ||||||
| @ -1923,11 +1968,11 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|     # memory and disk |     # memory and disk | ||||||
| 
 | 
 | ||||||
|     def get_mem(self): |     def get_mem(self): | ||||||
|         '''Get current memory usage from VM. |         """Get current memory usage from VM. | ||||||
| 
 | 
 | ||||||
|         :returns: Memory usage [FIXME unit]. |         :returns: Memory usage [FIXME unit]. | ||||||
|         :rtype: FIXME |         :rtype: FIXME | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         if self.libvirt_domain is None: |         if self.libvirt_domain is None: | ||||||
|             return 0 |             return 0 | ||||||
| @ -1951,11 +1996,11 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|             raise |             raise | ||||||
| 
 | 
 | ||||||
|     def get_mem_static_max(self): |     def get_mem_static_max(self): | ||||||
|         '''Get maximum memory available to VM. |         """Get maximum memory available to VM. | ||||||
| 
 | 
 | ||||||
|         :returns: Memory limit [FIXME unit]. |         :returns: Memory limit [FIXME unit]. | ||||||
|         :rtype: FIXME |         :rtype: FIXME | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         if self.libvirt_domain is None: |         if self.libvirt_domain is None: | ||||||
|             return 0 |             return 0 | ||||||
| @ -1977,11 +2022,11 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|             raise |             raise | ||||||
| 
 | 
 | ||||||
|     def get_cputime(self): |     def get_cputime(self): | ||||||
|         '''Get total CPU time burned by this domain since start. |         """Get total CPU time burned by this domain since start. | ||||||
| 
 | 
 | ||||||
|         :returns: CPU time usage [FIXME unit]. |         :returns: CPU time usage [FIXME unit]. | ||||||
|         :rtype: FIXME |         :rtype: FIXME | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         if self.libvirt_domain is None: |         if self.libvirt_domain is None: | ||||||
|             return 0 |             return 0 | ||||||
| @ -1996,8 +2041,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|                 return 0 |                 return 0 | ||||||
| 
 | 
 | ||||||
|             # this does not work, because libvirt |             # this does not work, because libvirt | ||||||
| #           return self.libvirt_domain.getCPUStats( |             # return self.libvirt_domain.getCPUStats( | ||||||
| #               libvirt.VIR_NODE_CPU_STATS_ALL_CPUS, 0)[0]['cpu_time']/10**9 |             #      libvirt.VIR_NODE_CPU_STATS_ALL_CPUS, 0)[0]['cpu_time']/10**9 | ||||||
| 
 | 
 | ||||||
|             return self.libvirt_domain.info()[4] |             return self.libvirt_domain.info()[4] | ||||||
| 
 | 
 | ||||||
| @ -2018,10 +2063,10 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @qubes.stateless_property |     @qubes.stateless_property | ||||||
|     def start_time(self): |     def start_time(self): | ||||||
|         '''Tell when machine was started. |         """Tell when machine was started. | ||||||
| 
 | 
 | ||||||
|         :rtype: float or None |         :rtype: float or None | ||||||
|         ''' |         """ | ||||||
|         if not self.is_running(): |         if not self.is_running(): | ||||||
|             return None |             return None | ||||||
| 
 | 
 | ||||||
| @ -2035,11 +2080,11 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def kernelopts_common(self): |     def kernelopts_common(self): | ||||||
|         '''Kernel options which should be used in addition to *kernelopts* |         """Kernel options which should be used in addition to *kernelopts* | ||||||
|         property. |         property. | ||||||
| 
 | 
 | ||||||
|         This is specific to kernel (and initrd if any) |         This is specific to kernel (and initrd if any) | ||||||
|         ''' |         """ | ||||||
|         if not self.kernel: |         if not self.kernel: | ||||||
|             return '' |             return '' | ||||||
|         kernels_dir = self.storage.kernels_dir |         kernels_dir = self.storage.kernels_dir | ||||||
| @ -2057,17 +2102,17 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|     # |     # | ||||||
| 
 | 
 | ||||||
|     def relative_path(self, path): |     def relative_path(self, path): | ||||||
|         '''Return path relative to py:attr:`dir_path`. |         """Return path relative to py:attr:`dir_path`. | ||||||
| 
 | 
 | ||||||
|         :param str path: Path in question. |         :param str path: Path in question. | ||||||
|         :returns: Relative path. |         :returns: Relative path. | ||||||
|         ''' |         """ | ||||||
| 
 | 
 | ||||||
|         return os.path.relpath(path, self.dir_path) |         return os.path.relpath(path, self.dir_path) | ||||||
| 
 | 
 | ||||||
|     def create_qdb_entries(self): |     def create_qdb_entries(self): | ||||||
|         '''Create entries in Qubes DB. |         """Create entries in Qubes DB. | ||||||
|         ''' |         """ | ||||||
|         # pylint: disable=no-member |         # pylint: disable=no-member | ||||||
| 
 | 
 | ||||||
|         self.untrusted_qdb.write('/name', self.name) |         self.untrusted_qdb.write('/name', self.name) | ||||||
| @ -2114,27 +2159,26 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|                 self.untrusted_qdb.write('/qubes-gateway6', |                 self.untrusted_qdb.write('/qubes-gateway6', | ||||||
|                                          str(self.visible_gateway6)) |                                          str(self.visible_gateway6)) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|         tzname = qubes.utils.get_timezone() |         tzname = qubes.utils.get_timezone() | ||||||
|         if tzname: |         if tzname: | ||||||
|             self.untrusted_qdb.write('/qubes-timezone', tzname) |             self.untrusted_qdb.write('/qubes-timezone', tzname) | ||||||
| 
 | 
 | ||||||
|         self.untrusted_qdb.write('/qubes-block-devices', '') |         self.untrusted_qdb.write('/qubes-block-devices', '') | ||||||
| 
 |  | ||||||
|         self.untrusted_qdb.write('/qubes-usb-devices', '') |         self.untrusted_qdb.write('/qubes-usb-devices', '') | ||||||
| 
 | 
 | ||||||
|         # TODO: Currently the whole qmemman is quite Xen-specific, so stay with |         # TODO: Currently the whole qmemman is quite Xen-specific, so stay with | ||||||
|         # xenstore for it until decided otherwise |         # xenstore for it until decided otherwise | ||||||
|         if qmemman_present: |         if qmemman_present: | ||||||
|             self.app.vmm.xs.set_permissions('', |             self.app.vmm.xs.set_permissions('', | ||||||
|                 '/local/domain/{}/memory'.format(self.xid), |                                             '/local/domain/{}/memory'.format( | ||||||
|  |                                                 self.xid), | ||||||
|                                             [{'dom': self.xid}]) |                                             [{'dom': self.xid}]) | ||||||
| 
 | 
 | ||||||
|         self.fire_event('domain-qdb-create') |         self.fire_event('domain-qdb-create') | ||||||
| 
 | 
 | ||||||
|     # TODO async; update this in constructor |     # TODO async; update this in constructor | ||||||
|     def _update_libvirt_domain(self): |     def _update_libvirt_domain(self): | ||||||
|         '''Re-initialise :py:attr:`libvirt_domain`.''' |         """Re-initialise :py:attr:`libvirt_domain`.""" | ||||||
|         domain_config = self.create_config_file() |         domain_config = self.create_config_file() | ||||||
|         try: |         try: | ||||||
|             self._libvirt_domain = self.app.vmm.libvirt_conn.defineXML( |             self._libvirt_domain = self.app.vmm.libvirt_conn.defineXML( | ||||||
| @ -2142,7 +2186,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
|         except libvirt.libvirtError as e: |         except libvirt.libvirtError as e: | ||||||
|             if e.get_error_code() == libvirt.VIR_ERR_OS_TYPE \ |             if e.get_error_code() == libvirt.VIR_ERR_OS_TYPE \ | ||||||
|                     and e.get_str2() == 'hvm': |                     and e.get_str2() == 'hvm': | ||||||
|                 raise qubes.exc.QubesVMError(self, |                 raise qubes.exc.QubesVMError( | ||||||
|  |                     self, | ||||||
|                     'HVM qubes are not supported on this machine. ' |                     'HVM qubes are not supported on this machine. ' | ||||||
|                     'Check BIOS settings for VT-x/AMD-V extensions.') |                     'Check BIOS settings for VT-x/AMD-V extensions.') | ||||||
|             raise |             raise | ||||||
| @ -2153,8 +2198,8 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): | |||||||
| 
 | 
 | ||||||
|     def get_prefmem(self): |     def get_prefmem(self): | ||||||
|         # TODO: qmemman is still xen specific |         # TODO: qmemman is still xen specific | ||||||
|         untrusted_meminfo_key = self.app.vmm.xs.read('', |         untrusted_meminfo_key = self.app.vmm.xs.read( | ||||||
|             '/local/domain/{}/memory/meminfo'.format(self.xid)) |             '', '/local/domain/{}/memory/meminfo'.format(self.xid)) | ||||||
| 
 | 
 | ||||||
|         if untrusted_meminfo_key is None or untrusted_meminfo_key == '': |         if untrusted_meminfo_key is None or untrusted_meminfo_key == '': | ||||||
|             return 0 |             return 0 | ||||||
| @ -2199,6 +2244,7 @@ def _patch_pool_config(config, pool=None, pools=None): | |||||||
|             raise qubes.exc.QubesException(msg) |             raise qubes.exc.QubesException(msg) | ||||||
|     return config |     return config | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| def _patch_volume_config(volume_config, pool=None, pools=None): | def _patch_volume_config(volume_config, pool=None, pools=None): | ||||||
|     assert not (pool and pools), \ |     assert not (pool and pools), \ | ||||||
|         'You can not pass pool & pools parameter at same time' |         'You can not pass pool & pools parameter at same time' | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Marek Marczykowski-Górecki
						Marek Marczykowski-Górecki