qubes.rst 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. :py:mod:`qubes` -- Common concepts
  2. ==================================
  3. Global Qubes object
  4. -------------------
  5. Because all objects in Qubes' world are interconnected, there is no possibility
  6. to instantiate them separately. They are all loaded together and contained in
  7. the one ``app`` object, an instance of :py:class:`qubes.Qubes` class.
  8. Loading
  9. ^^^^^^^
  10. The objects may come to existence in two ways: by explicit instantiation or by
  11. loading from XML file.
  12. The loading from XML is done in stages, because Qubes domains are dependent on
  13. each other in what can be even a circular dependency. Therefore some properties
  14. (especcialy those that refer to another domains) are loaded later.
  15. .. image:: loading.svg
  16. Refer to :py:class:`qubes.Qubes` class documentation to get description of every
  17. stage.
  18. Properties
  19. ----------
  20. Many parameters of Qubes can be changed -- from names of particular domains to
  21. default NetVM for all AppVMs. All of those *configurable* parameters are called
  22. *properties* and can be accessed like Python attributes on their owners::
  23. >>> import qubes
  24. >>> app = qubes.Qubes()
  25. >>> app.domains[0] # docutils: +ELLIPSIS
  26. <AdminVM ...>
  27. >>> app.domains[0].name
  28. 'dom0'
  29. Definition
  30. ^^^^^^^^^^
  31. Properties are defined on global :py:class:`qubes.Qubes` application object and
  32. on every domain. Those classess inherit from :py:class:`PropertyHolder` class,
  33. which is responsible for operation of properties.
  34. Each Qubes property is actually a *data descriptor* (a Python term), which means
  35. they are attributes of their classess, but when trying to access it from
  36. *instance*, they return it's underlying value instead. They can be thought of as
  37. Python's builtin :py:class:`property`, but greatly enhanced. They are defined in
  38. definition of their class::
  39. >>> import qubes
  40. >>> class MyTestHolder(qubes.PropertyHolder):
  41. >>> testprop = qubes.property('testprop')
  42. >>> instance = MyTestHolder()
  43. >>> instance.testprop = 'aqq'
  44. >>> instance.testprop
  45. 'aqq'
  46. If you like to access some attributes of the property *itself*, you should refer
  47. to instance's class::
  48. >>> import qubes
  49. >>> class MyTestHolder(qubes.PropertyHolder):
  50. >>> testprop = qubes.property('testprop')
  51. >>> instance = MyTestHolder()
  52. >>> instance.testprop = 'aqq'
  53. >>> type(instance.testprop)
  54. <type 'str'>
  55. >>> type(instance.__class__.testprop)
  56. <class 'qubes.property'>
  57. >>> instance.__class__.testprop.__name__
  58. 'testprop'
  59. As a rule, properties are intended to be serialised and deserialised to/from XML
  60. file. There are very few exceptions, but if you don't intend to save the
  61. property to XML, you should generally go for builtin :py:class:`property`.
  62. One important difference from builtin properties is that there is no getter
  63. function, only setter. In other words, they are not dynamic, you cannot return
  64. different value every time something wants to access it. This is to ensure that
  65. while saving the property is not a moving target.
  66. Property's properties
  67. ^^^^^^^^^^^^^^^^^^^^^
  68. You can specify some parameters while defining the property. The most important
  69. is the `type`: on setting, the value is coerced to this type. It is well suited
  70. to builtin types like :py:class:`int`::
  71. >>> import qubes
  72. >>> class MyTestHolder(qubes.PropertyHolder):
  73. >>> testprop = qubes.property('testprop')
  74. >>> intprop = qubes.property('intprop', type=int)
  75. >>> instance = MyTestHolder()
  76. >>> instance.testprop = '123'
  77. >>> instance.intprop = '123'
  78. >>> instance.testprop
  79. '123'
  80. >>> instance.intprop
  81. 123
  82. Every property should be documented. You should add a short description to your
  83. property, which will appear, among others, in :program:`qvm-prefs` and
  84. :program:`qvm-ls` programs. It should not use any Sphinx-specific markup::
  85. >>> import qubes
  86. >>> class MyTestHolder(qubes.PropertyHolder):
  87. >>> testprop = qubes.property('testprop',
  88. >>> doc='My new and shiny property.')
  89. >>> MyTestHolder.testprop.__doc__
  90. 'My new and shiny property.'
  91. In addition to `type`, properties also support `setter` parameter. It acts
  92. similar to `type`, but is always executed (not only when types don't agree) and
  93. accepts more parameters: `self`, `prop` and `value` being respectively: owners'
  94. instance, property's instance and the value being set. There is also `saver`,
  95. which does reverse: given value of the property it should return a string that
  96. can be parsed by `saver`.
  97. Unset properties and default values
  98. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  99. Properties may be unset, even if they are defined (that is, on access they raise
  100. :py:exc:`AttributeError` -- that is the normal Python way to tell that the
  101. attribute is absent). You can manually unset a property using Python's ``del``
  102. statement::
  103. >>> import qubes
  104. >>> class MyTestHolder(qubes.PropertyHolder):
  105. >>> testprop = qubes.property('testprop')
  106. >>> instance = MyTestHolder()
  107. >>> instance.testprop
  108. AttributeError: ...
  109. >>> instance.testprop = 123
  110. >>> instance.testprop
  111. 123
  112. >>> del instance.testprop
  113. >>> instance.testprop
  114. AttributeError: ...
  115. Alternatively, some properties may return some other value instead, if that's
  116. the reasonable thing to do. For example, when
  117. :py:attr:`qubes.vm.qubesvm.QubesVM.netvm` is unset, we check global setting
  118. :py:attr:`qubes.Qubes.default_netvm` instead. Returning :py:obj:`None` as
  119. default would be wrong, as it is marker that means „no NetVM, machine
  120. disconnected”.
  121. You can define a default value either as constant or as a callable. In the
  122. second case, the callable should accept one argument, the instance that owns the
  123. property::
  124. >>> import qubes
  125. >>> class MyTestHolder(qubes.PropertyHolder):
  126. >>> testprop = qubes.property('testprop')
  127. >>> def1prop = qubes.property('testprop', default=123)
  128. >>> netvm = qubes.property('testprop',
  129. >>> default=(lambda self: self.app.default_netvm))
  130. >>> instance = MyTestHolder()
  131. >>> instance.testprop
  132. AttributeError: ...
  133. >>> instance.def1prop
  134. 123
  135. >>> instance.netvm # doctest: +SKIP
  136. <NetVM ...>
  137. Setting netvm on particular domain of course does not affect global default, but
  138. only this instance. But there are two problems:
  139. - You don't know if the value of the property you just accessed was it's
  140. true or default value.
  141. - After ``del``'ing a property, you still will get a value on access. You
  142. cannot count on `AttributeError` raised from them.
  143. Therefore Qubes support alternative semantics. You can (and probably should,
  144. wherever applicable) use no ``del``, but assignment of special magic object
  145. :py:obj:`qubes.property.DEFAULT`. There is also method
  146. :py:meth:`qubes.PropertyHolder.property_is_default`, which can be used to
  147. distinguish unset from set properties::
  148. >>> import qubes
  149. >>> class MyTestHolder(qubes.PropertyHolder):
  150. >>> testprop = qubes.property('testprop', default=123)
  151. >>> instance.testprop
  152. 123
  153. >>> instance.property_is_default('testprop')
  154. True
  155. >>> instance.testprop = 123
  156. >>> instance.testprop
  157. >>> instance.property_is_default('testprop')
  158. False
  159. >>> instance.testprop = qubes.property.DEFAULT
  160. >>> instance.property_is_default('testprop')
  161. True
  162. Inheritance
  163. ^^^^^^^^^^^
  164. Properties in subclassess overload properties from their parents, like
  165. expected::
  166. >>> import qubes
  167. >>> class MyTestHolder(qubes.PropertyHolder):
  168. >>> testprop = qubes.property('testprop')
  169. >>> class MyOtherHolder(MyTestHolder):
  170. >>> testprop = qubes.property('testprop', setter=qubes.property.forbidden)
  171. >>> instance = MyOtherHolder()
  172. >>> instane.testprop = 123
  173. TypeError: ...
  174. Module contents
  175. ---------------
  176. .. automodule:: qubes
  177. :members:
  178. :show-inheritance:
  179. .. vim: ts=3 sw=3 et