qubes: more documentation and doc fixes
This commit is contained in:
		
							parent
							
								
									5d9b92a039
								
							
						
					
					
						commit
						e83d21c671
					
				@ -1,15 +1,59 @@
 | 
				
			|||||||
:py:mod:`qubes.vm` -- Different Virtual Machine types
 | 
					:py:mod:`qubes.vm` -- Different Virtual Machine types
 | 
				
			||||||
=====================================================
 | 
					=====================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Qubes is composed of several virtual machines that are interconnected in
 | 
				
			||||||
 | 
					several ways. From now on they will be called „domains”, as they may not
 | 
				
			||||||
 | 
					actually be true virtual machines -- we plan to support LXC containers for
 | 
				
			||||||
 | 
					example. Because of Xen-only legacy of Qubes code, it is custom to refer to them
 | 
				
			||||||
 | 
					in long/plural as ``domains`` and in short/singular as ``vm``.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Domain object
 | 
				
			||||||
 | 
					-------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					There are couple of programming objects that refer to domain. The main is the
 | 
				
			||||||
 | 
					instance of :py:class:`qubes.vm.QubesVM`. This is the main „porcelain” object,
 | 
				
			||||||
 | 
					which carries other objects and supplies convenience methods like
 | 
				
			||||||
 | 
					:py:meth:`qubes.vm.qubesvm.QubesVM.start`. This class is actually divided in
 | 
				
			||||||
 | 
					two, the :py:class:`qubes.vm.qubesvm.QubesVM` cares about Qubes-specific
 | 
				
			||||||
 | 
					actions, that are more or less directly related to security model. It is
 | 
				
			||||||
 | 
					intended to be easily auditable by non-expert programmers (ie. we don't use
 | 
				
			||||||
 | 
					Python's magic there). The second class is its parent,
 | 
				
			||||||
 | 
					:py:class:`qubes.vm.BaseVM`, which is concerned about technicalities like XML
 | 
				
			||||||
 | 
					serialising/deserialising. It is of less concern to threat model auditors, but
 | 
				
			||||||
 | 
					still relevant to overall security of the Qubes OS. It is written for
 | 
				
			||||||
 | 
					programmers by programmers.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The second object is the XML node that refers to the domain. It can be accessed
 | 
				
			||||||
 | 
					as :py:attr:`Qubes.vm.BaseVM.xml` attribute of the domain object. The third one
 | 
				
			||||||
 | 
					is :py:attr:`Qubes.vm.qubesvm.QubesVM.libvirt_domain` object for directly
 | 
				
			||||||
 | 
					interacting with libvirt. Those objects are intended to be used from core and/or
 | 
				
			||||||
 | 
					plugins, but not directly by user or from qvm-tools. They are however public, so
 | 
				
			||||||
 | 
					there are no restrictions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Domain classes
 | 
				
			||||||
 | 
					--------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					There are several different types of VM, because not every Qubes domain is equal
 | 
				
			||||||
 | 
					-- some of them perform specific functions, like NetVM; others have different
 | 
				
			||||||
 | 
					life cycle, like DisposableVM. For that, different domains have different Python
 | 
				
			||||||
 | 
					classes. They are all defined in this package, generally one class per module,
 | 
				
			||||||
 | 
					but some modules contain private globals that serve this particular class.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Package contents
 | 
				
			||||||
 | 
					----------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Main public classes
 | 
					Main public classes
 | 
				
			||||||
-------------------
 | 
					^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. autoclass:: qubes.vm.BaseVM
 | 
					.. autoclass:: qubes.vm.BaseVM
 | 
				
			||||||
   :members:
 | 
					   :members:
 | 
				
			||||||
   :show-inheritance:
 | 
					   :show-inheritance:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Helper classes and functions
 | 
					Helper classes and functions
 | 
				
			||||||
----------------------------
 | 
					^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. autoclass:: qubes.vm.BaseVMMeta
 | 
					.. autoclass:: qubes.vm.BaseVMMeta
 | 
				
			||||||
   :members:
 | 
					   :members:
 | 
				
			||||||
@ -24,7 +68,7 @@ Helper classes and functions
 | 
				
			|||||||
   :show-inheritance:
 | 
					   :show-inheritance:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Particular VM classes
 | 
					Particular VM classes
 | 
				
			||||||
---------------------
 | 
					^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Main types:
 | 
					Main types:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										212
									
								
								doc/qubes.rst
									
									
									
									
									
								
							
							
						
						
									
										212
									
								
								doc/qubes.rst
									
									
									
									
									
								
							@ -1,6 +1,218 @@
 | 
				
			|||||||
:py:mod:`qubes` -- Common concepts
 | 
					:py:mod:`qubes` -- Common concepts
 | 
				
			||||||
==================================
 | 
					==================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Global Qubes object
 | 
				
			||||||
 | 
					-------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Because all objects in Qubes' world are interconnected, there is no possibility
 | 
				
			||||||
 | 
					to instantiate them separately. They are all loaded together and contained in
 | 
				
			||||||
 | 
					the one ``app`` object, an instance of :py:class:`qubes.Qubes` class.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The loading from XML is done in stages, because Qubes domains are dependent on
 | 
				
			||||||
 | 
					each other in what can be even a circular dependency. Therefore some properties
 | 
				
			||||||
 | 
					(especcialy those that refer to another domains) are loaded later. Refer to
 | 
				
			||||||
 | 
					:py:class:`qubes.Qubes` class documentation to get description of every stage.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Properties
 | 
				
			||||||
 | 
					----------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Many parameters of Qubes can be changed -- from names of particular domains to
 | 
				
			||||||
 | 
					default NetVM for all AppVMs. All of those *configurable* parameters are called
 | 
				
			||||||
 | 
					*properties* and can be accessed like Python attributes on their owners::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   >>> import qubes
 | 
				
			||||||
 | 
					   >>> app = qubes.Qubes()
 | 
				
			||||||
 | 
					   >>> app.domain[0] # docutils: +ELLIPSIS
 | 
				
			||||||
 | 
					   <AdminVM ...>
 | 
				
			||||||
 | 
					   >>> app.domain[0].name
 | 
				
			||||||
 | 
					   'dom0'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Definition
 | 
				
			||||||
 | 
					^^^^^^^^^^
 | 
				
			||||||
 | 
					Properties are defined on global :py:class:`qubes.Qubes` application object and
 | 
				
			||||||
 | 
					on every domain. Those classess inherit from :py:class:`PropertyHolder` class,
 | 
				
			||||||
 | 
					which is responsible for operation of properties.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Each Qubes property is actually a *data descriptor* (a Python term), which means
 | 
				
			||||||
 | 
					they are attributes of their classess, but when trying to access it from
 | 
				
			||||||
 | 
					*instance*, they return it's underlying value instead. They can be thought of as
 | 
				
			||||||
 | 
					Python's builtin :py:class:`property`, but greatly enhanced. They are defined in
 | 
				
			||||||
 | 
					definition of their class::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   >>> import qubes
 | 
				
			||||||
 | 
					   >>> class MyTestHolder(qubes.PropertyHolder):
 | 
				
			||||||
 | 
					   >>>     testprop = qubes.property('testprop')
 | 
				
			||||||
 | 
					   >>> instance = MyTestHolder()
 | 
				
			||||||
 | 
					   >>> instance.testprop = 'aqq'
 | 
				
			||||||
 | 
					   >>> instance.testprop
 | 
				
			||||||
 | 
					   'aqq'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you like to access some attributes of the property *itself*, you should refer
 | 
				
			||||||
 | 
					to instance's class::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   >>> import qubes
 | 
				
			||||||
 | 
					   >>> class MyTestHolder(qubes.PropertyHolder):
 | 
				
			||||||
 | 
					   >>>     testprop = qubes.property('testprop')
 | 
				
			||||||
 | 
					   >>> instance = MyTestHolder()
 | 
				
			||||||
 | 
					   >>> instance.testprop = 'aqq'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   >>> type(instance.testprop)
 | 
				
			||||||
 | 
					   <type 'str'>
 | 
				
			||||||
 | 
					   >>> type(instance.__class__.testprop)
 | 
				
			||||||
 | 
					   <class 'qubes.property'>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   >>> instance.__class__.testprop.__name__
 | 
				
			||||||
 | 
					   'testprop'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					As a rule, properties are intended to be serialised and deserialised to/from XML
 | 
				
			||||||
 | 
					file. There are very few exceptions, but if you don't intend to save the
 | 
				
			||||||
 | 
					property to XML, you should generally go for builtin :py:class:`property`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					One important difference from builtin properties is that there is no getter
 | 
				
			||||||
 | 
					function, only setter. In other words, they are not dynamic, you cannot return
 | 
				
			||||||
 | 
					different value every time something wants to access it. This is to ensure that
 | 
				
			||||||
 | 
					while saving the property is not a moving target.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Property's properties
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					You can specify some parameters while defining the property. The most important
 | 
				
			||||||
 | 
					is the `type`: on setting, the value is coerced to this type. It is well suited
 | 
				
			||||||
 | 
					to builtin types like :py:class:`int`::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   >>> import qubes
 | 
				
			||||||
 | 
					   >>> class MyTestHolder(qubes.PropertyHolder):
 | 
				
			||||||
 | 
					   >>>     testprop = qubes.property('testprop')
 | 
				
			||||||
 | 
					   >>>     intprop = qubes.property('intprop', type=int)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   >>> instance = MyTestHolder()
 | 
				
			||||||
 | 
					   >>> instance.testprop = '123'
 | 
				
			||||||
 | 
					   >>> instance.intprop = '123'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   >>> instance.testprop
 | 
				
			||||||
 | 
					   '123'
 | 
				
			||||||
 | 
					   >>> instance.intprop
 | 
				
			||||||
 | 
					   123
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Every property should be documented. You should add a short description to your
 | 
				
			||||||
 | 
					property, which will appear, among others, in :program:`qvm-prefs` and
 | 
				
			||||||
 | 
					:program:`qvm-ls` programs. It should not use any Sphinx-specific markup::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   >>> import qubes
 | 
				
			||||||
 | 
					   >>> class MyTestHolder(qubes.PropertyHolder):
 | 
				
			||||||
 | 
					   >>>     testprop = qubes.property('testprop',
 | 
				
			||||||
 | 
					   >>>         doc='My new and shiny property.')
 | 
				
			||||||
 | 
					   >>> MyTestHolder.testprop.__doc__
 | 
				
			||||||
 | 
					   'My new and shiny property.'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In addition to `type`, properties also support `setter` parameter. It acts
 | 
				
			||||||
 | 
					similar to `type`, but is always executed (not only when types don't agree) and
 | 
				
			||||||
 | 
					accepts more parameters: `self`, `prop` and `value` being respectively: owners'
 | 
				
			||||||
 | 
					instance, property's instance and the value being set. There is also `saver`,
 | 
				
			||||||
 | 
					which does reverse: given value of the property it should return a string that
 | 
				
			||||||
 | 
					can be parsed by `saver`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Unset properties and default values
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					Properties may be unset, even if they are defined (that is, on access they raise
 | 
				
			||||||
 | 
					:py:exc:`AttributeError` -- that is the normal Python way to tell that the
 | 
				
			||||||
 | 
					attribute is absent). You can manually unset a property using Python's ``del``
 | 
				
			||||||
 | 
					statement::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   >>> import qubes
 | 
				
			||||||
 | 
					   >>> class MyTestHolder(qubes.PropertyHolder):
 | 
				
			||||||
 | 
					   >>>     testprop = qubes.property('testprop')
 | 
				
			||||||
 | 
					   >>> instance = MyTestHolder()
 | 
				
			||||||
 | 
					   >>> instance.testprop
 | 
				
			||||||
 | 
					   AttributeError: ...
 | 
				
			||||||
 | 
					   >>> instance.testprop = 123
 | 
				
			||||||
 | 
					   >>> instance.testprop
 | 
				
			||||||
 | 
					   123
 | 
				
			||||||
 | 
					   >>> del instance.testprop
 | 
				
			||||||
 | 
					   >>> instance.testprop
 | 
				
			||||||
 | 
					   AttributeError: ...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Alternatively, some properties may return some other value instead, if that's
 | 
				
			||||||
 | 
					the reasonable thing to do. For example, when
 | 
				
			||||||
 | 
					:py:attr:`qubes.vm.qubesvm.QubesVM.netvm` is unset, we check global setting
 | 
				
			||||||
 | 
					:py:attr:`qubes.Qubes.default_netvm` instead. Returning :py:obj:`None` as
 | 
				
			||||||
 | 
					default would be wrong, as it is marker that means „no NetVM, machine
 | 
				
			||||||
 | 
					disconnected”.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can define a default value either as constant or as a callable. In the
 | 
				
			||||||
 | 
					second case, the callable should accept one argument, the instance that owns the
 | 
				
			||||||
 | 
					property::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   >>> import qubes
 | 
				
			||||||
 | 
					   >>> class MyTestHolder(qubes.PropertyHolder):
 | 
				
			||||||
 | 
					   >>>     testprop = qubes.property('testprop')
 | 
				
			||||||
 | 
					   >>>     def1prop = qubes.property('testprop', default=123)
 | 
				
			||||||
 | 
					   >>>     netvm = qubes.property('testprop',
 | 
				
			||||||
 | 
					   >>>         default=(lambda self: self.app.default_netvm))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   >>> instance = MyTestHolder()
 | 
				
			||||||
 | 
					   >>> instance.testprop
 | 
				
			||||||
 | 
					   AttributeError: ...
 | 
				
			||||||
 | 
					   >>> instance.def1prop
 | 
				
			||||||
 | 
					   123
 | 
				
			||||||
 | 
					   >>> instance.netvm # doctest: +SKIP
 | 
				
			||||||
 | 
					   <NetVM ...>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Setting netvm on particular domain of course does not affect global default, but
 | 
				
			||||||
 | 
					only this instance. But there are two problems:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- You don't know if the value of the property you just accessed was it's
 | 
				
			||||||
 | 
					  true or default value.
 | 
				
			||||||
 | 
					- After ``del``'ing a property, you still will get a value on access. You
 | 
				
			||||||
 | 
					  cannot count on `AttributeError` raised from them.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Therefore Qubes support alternative semantics. You can (and probably should,
 | 
				
			||||||
 | 
					wherever applicable) use no ``del``, but assignment of special magic object
 | 
				
			||||||
 | 
					:py:obj:`qubes.property.DEFAULT`. There is also method
 | 
				
			||||||
 | 
					:py:meth:`qubes.PropertyHolder.property_is_default`, which can be used to
 | 
				
			||||||
 | 
					distinguish unset from set properties::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   >>> import qubes
 | 
				
			||||||
 | 
					   >>> class MyTestHolder(qubes.PropertyHolder):
 | 
				
			||||||
 | 
					   >>>     testprop = qubes.property('testprop', default=123)
 | 
				
			||||||
 | 
					   >>> instance.testprop
 | 
				
			||||||
 | 
					   123
 | 
				
			||||||
 | 
					   >>> instance.property_is_default('testprop')
 | 
				
			||||||
 | 
					   True
 | 
				
			||||||
 | 
					   >>> instance.testprop = 123
 | 
				
			||||||
 | 
					   >>> instance.testprop
 | 
				
			||||||
 | 
					   >>> instance.property_is_default('testprop')
 | 
				
			||||||
 | 
					   False
 | 
				
			||||||
 | 
					   >>> instance.testprop = qubes.property.DEFAULT
 | 
				
			||||||
 | 
					   >>> instance.property_is_default('testprop')
 | 
				
			||||||
 | 
					   True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Inheritance
 | 
				
			||||||
 | 
					^^^^^^^^^^^
 | 
				
			||||||
 | 
					Properties in subclassess overload properties from their parents, like
 | 
				
			||||||
 | 
					expected::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   >>> import qubes
 | 
				
			||||||
 | 
					   >>> class MyTestHolder(qubes.PropertyHolder):
 | 
				
			||||||
 | 
					   >>>     testprop = qubes.property('testprop')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   >>> class MyOtherHolder(MyTestHolder):
 | 
				
			||||||
 | 
					   >>>     testprop = qubes.property('testprop', setter=qubes.property.forbidden)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   >>> instance = MyOtherHolder()
 | 
				
			||||||
 | 
					   >>> instane.testprop = 123
 | 
				
			||||||
 | 
					   TypeError: ...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Module contents
 | 
				
			||||||
 | 
					---------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. automodule:: qubes
 | 
					.. automodule:: qubes
 | 
				
			||||||
   :members:
 | 
					   :members:
 | 
				
			||||||
   :show-inheritance:
 | 
					   :show-inheritance:
 | 
				
			||||||
 | 
				
			|||||||
@ -153,7 +153,7 @@ class BaseVM(qubes.PropertyHolder):
 | 
				
			|||||||
        #: dictionary of services that are run on this domain
 | 
					        #: dictionary of services that are run on this domain
 | 
				
			||||||
        self.services = services or {}
 | 
					        self.services = services or {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #: :py:class`DeviceManager` object keeping devices that are attached to
 | 
					        #: :py:class:`DeviceManager` object keeping devices that are attached to
 | 
				
			||||||
        #: this domain
 | 
					        #: this domain
 | 
				
			||||||
        self.devices = DeviceManager(self) if devices is None else devices
 | 
					        self.devices = DeviceManager(self) if devices is None else devices
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -294,7 +294,7 @@ class BaseVM(qubes.PropertyHolder):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        :param str ip: IP address of the frontend
 | 
					        :param str ip: IP address of the frontend
 | 
				
			||||||
        :param str mac: MAC (Ethernet) address of the frontend
 | 
					        :param str mac: MAC (Ethernet) address of the frontend
 | 
				
			||||||
        :param qubes.vm.QubesVM backend: Backend domain
 | 
					        :param qubes.vm.qubesvm.QubesVM backend: Backend domain
 | 
				
			||||||
        :rtype: lxml.etree._Element
 | 
					        :rtype: lxml.etree._Element
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -314,7 +314,7 @@ class BaseVM(qubes.PropertyHolder):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        :param str ip: IP address of the frontend
 | 
					        :param str ip: IP address of the frontend
 | 
				
			||||||
        :param str mac: MAC (Ethernet) address of the frontend
 | 
					        :param str mac: MAC (Ethernet) address of the frontend
 | 
				
			||||||
        :param qubes.vm.QubesVM backend: Backend domain
 | 
					        :param qubes.vm.qubesvm.QubesVM backend: Backend domain
 | 
				
			||||||
        :rtype: lxml.etree._Element
 | 
					        :rtype: lxml.etree._Element
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -935,7 +935,7 @@ class QubesVM(qubes.vm.BaseVM):
 | 
				
			|||||||
        **passio_popen** and **input** are mutually exclusive.
 | 
					        **passio_popen** and **input** are mutually exclusive.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        :param str service: service name
 | 
					        :param str service: service name
 | 
				
			||||||
        :param qubes.vm.QubesVM: source domain as presented to this VM
 | 
					        :param qubes.vm.qubesvm.QubesVM: source domain as presented to this VM
 | 
				
			||||||
        :param str user: username to run service as
 | 
					        :param str user: username to run service as
 | 
				
			||||||
        :param bool passio_popen: passed verbatim to :py:meth:`run`
 | 
					        :param bool passio_popen: passed verbatim to :py:meth:`run`
 | 
				
			||||||
        :param str input: string passed as input to service
 | 
					        :param str input: string passed as input to service
 | 
				
			||||||
@ -1117,7 +1117,7 @@ class QubesVM(qubes.vm.BaseVM):
 | 
				
			|||||||
    def clone_disk_files(self, src):
 | 
					    def clone_disk_files(self, src):
 | 
				
			||||||
        '''Clone files from other vm.
 | 
					        '''Clone files from other vm.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        :param qubes.vm.QubesVM src: source VM
 | 
					        :param qubes.vm.qubesvm.QubesVM src: source VM
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if src.is_running():
 | 
					        if src.is_running():
 | 
				
			||||||
@ -1278,11 +1278,12 @@ class QubesVM(qubes.vm.BaseVM):
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        .. seealso::
 | 
					        .. seealso::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            http://wiki.libvirt.org/page/VM_lifecycle
 | 
					            http://wiki.libvirt.org/page/VM_lifecycle
 | 
				
			||||||
                Description of VM life cycle from the point of view of libvirt.
 | 
					                Description of VM life cycle from the point of view of libvirt.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainState
 | 
					            https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainState
 | 
				
			||||||
                Libvirt API for changing 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        libvirt_domain = self.libvirt_domain
 | 
					        libvirt_domain = self.libvirt_domain
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user