xen.py 5.8 KB

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