xen.py 6.1 KB

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