xen.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. #!/usr/bin/python2 -O
  2. # vim: fileencoding=utf-8
  3. #
  4. # The Qubes OS Project, https://www.qubes-os.org/
  5. #
  6. # Copyright (C) 2015 Joanna Rutkowska <joanna@invisiblethingslab.com>
  7. # Copyright (C) 2013-2015 Marek Marczykowski-Górecki
  8. # <marmarek@invisiblethingslab.com>
  9. # Copyright (C) 2015 Wojtek Porczyk <woju@invisiblethingslab.com>
  10. #
  11. # This program is free software; you can redistribute it and/or modify
  12. # it under the terms of the GNU General Public License as published by
  13. # the Free Software Foundation; either version 2 of the License, or
  14. # (at your option) any later version.
  15. #
  16. # This program is distributed in the hope that it will be useful,
  17. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. # GNU General Public License for more details.
  20. #
  21. # You should have received a copy of the GNU General Public License along
  22. # with this program; if not, write to the Free Software Foundation, Inc.,
  23. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  24. #
  25. from __future__ import absolute_import
  26. import os
  27. import os.path
  28. import re
  29. import subprocess
  30. import lxml.etree
  31. import qubes
  32. import qubes.config
  33. import qubes.storage
  34. import qubes.vm.templatevm
  35. class XenVMStorage(qubes.storage.VMStorage):
  36. '''Class for VM storage of Xen VMs.
  37. '''
  38. root_dev = 'xvda'
  39. private_dev = 'xvdb'
  40. volatile_dev = 'xvdc'
  41. modules_dev = 'xvdd'
  42. # pylint: disable=redefined-builtin
  43. @staticmethod
  44. def _format_disk_dev(path, vdev, script=None, rw=True, type='disk',
  45. domain=None):
  46. if path is None:
  47. return ''
  48. element = lxml.etree.Element('disk')
  49. element.set('type', 'block')
  50. element.set('device', type)
  51. element.append(lxml.etree.Element('driver', name='phy'))
  52. element.append(lxml.etree.Element('source', dev=path))
  53. element.append(lxml.etree.Element('target', dev=vdev))
  54. if not rw:
  55. element.append(lxml.etree.Element('readonly'))
  56. if domain is not None:
  57. # XXX vm.name?
  58. element.append(lxml.etree.Element('domain', name=domain))
  59. if script:
  60. element.append(lxml.etree.Element('script', path=script))
  61. # TODO return element
  62. return lxml.etree.tostring(element)
  63. def _get_rootdev(self):
  64. if isinstance(self.vm, qubes.vm.templatevm.TemplateVM):
  65. return self._format_disk_dev(
  66. '{}:{}'.format(self.root_img, self.vm.rootcow_img),
  67. self.root_dev,
  68. script='block-origin')
  69. elif hasattr(self.vm, 'template'):
  70. return self._format_disk_dev(
  71. '{}:{}'.format(self.root_img, self.vm.template.rootcow_img),
  72. self.root_dev,
  73. script='block-snapshot')
  74. else:
  75. return self._format_disk_dev(self.root_img, self.root_dev)
  76. def get_config_params(self):
  77. args = {}
  78. args['rootdev'] = self._get_rootdev()
  79. args['privatedev'] = self._format_disk_dev(self.private_img,
  80. self.private_dev)
  81. args['volatiledev'] = self._format_disk_dev(self.volatile_img,
  82. self.volatile_dev)
  83. if self.modules_img is not None:
  84. args['otherdevs'] = self._format_disk_dev(self.modules_img,
  85. self.modules_dev, rw=self.modules_img_rw)
  86. elif self.drive is not None:
  87. (drive_type, drive_domain, drive_path) = self.drive.split(":")
  88. if drive_domain.lower() == "dom0":
  89. drive_domain = None
  90. args['otherdevs'] = self._format_disk_dev(drive_path,
  91. self.modules_dev,
  92. rw=(drive_type == "disk"),
  93. type=drive_type,
  94. domain=drive_domain)
  95. else:
  96. args['otherdevs'] = ''
  97. return args
  98. def create_on_disk_private_img(self, source_template=None):
  99. if source_template is None:
  100. f_private = open(self.private_img, 'a+b')
  101. f_private.truncate(self.private_img_size)
  102. f_private.close()
  103. else:
  104. self.vm.log.info("Copying the template's private image: {}".format(
  105. source_template.private_img))
  106. self._copy_file(source_template.private_img, self.private_img)
  107. def create_on_disk_root_img(self, source_template=None):
  108. if source_template is None:
  109. fd = open(self.root_img, 'a+b')
  110. fd.truncate(self.root_img_size)
  111. fd.close()
  112. elif self.vm.updateable:
  113. # if not updateable, just use template's disk
  114. self.vm.log.info("--> Copying the template's root image: {}".format(
  115. source_template.root_img))
  116. self._copy_file(source_template.root_img, self.root_img)
  117. def resize_private_img(self, size):
  118. fd = open(self.private_img, 'a+b')
  119. fd.truncate(size)
  120. fd.close()
  121. # find loop device if any
  122. p = subprocess.Popen(
  123. ['sudo', 'losetup', '--associated', self.private_img],
  124. stdout=subprocess.PIPE)
  125. result = p.communicate()
  126. m = re.match(r'^(/dev/loop\d+):\s', result[0])
  127. if m is not None:
  128. loop_dev = m.group(1)
  129. # resize loop device
  130. subprocess.check_call(
  131. ['sudo', 'losetup', '--set-capacity', loop_dev])
  132. def commit_template_changes(self):
  133. assert isinstance(self.vm, qubes.vm.templatevm.TemplateVM)
  134. # TODO: move rootcow_img to this class; the same for vm.is_outdated()
  135. if os.path.exists(self.vm.rootcow_img):
  136. os.rename(self.vm.rootcow_img, self.vm.rootcow_img + '.old')
  137. old_umask = os.umask(002)
  138. f_cow = open(self.vm.rootcow_img, 'w')
  139. f_root = open(self.root_img, 'r')
  140. f_root.seek(0, os.SEEK_END)
  141. # make empty sparse file of the same size as root.img
  142. f_cow.truncate(f_root.tell())
  143. f_cow.close()
  144. f_root.close()
  145. os.umask(old_umask)