qubes.rst 7.4 KB

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