Image building tools for OpenStack
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

localloop.py 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. # Copyright 2016 Andreas Florath (andreas@florath.net)
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  4. # not use this file except in compliance with the License. You may obtain
  5. # a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  11. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. # License for the specific language governing permissions and limitations
  13. # under the License.
  14. import logging
  15. import os
  16. from diskimage_builder.block_device.exception import \
  17. BlockDeviceSetupException
  18. from diskimage_builder.block_device.plugin import NodeBase
  19. from diskimage_builder.block_device.plugin import PluginBase
  20. from diskimage_builder.block_device.utils import exec_sudo
  21. from diskimage_builder.block_device.utils import parse_abs_size_spec
  22. logger = logging.getLogger(__name__)
  23. def image_create(filename, size):
  24. logger.info("Create image file [%s]", filename)
  25. with open(filename, "w") as fd:
  26. fd.seek(size - 1)
  27. fd.write("\0")
  28. def image_delete(filename):
  29. logger.info("Remove image file [%s]", filename)
  30. os.remove(filename)
  31. def loopdev_attach(filename):
  32. logger.info("loopdev attach")
  33. logger.debug("Calling [sudo losetup --show -f %s]", filename)
  34. block_device = exec_sudo(["losetup", "--show", "-f", filename])
  35. # [:-1]: Cut of the newline
  36. block_device = block_device[:-1]
  37. logger.info("New block device [%s]", block_device)
  38. return block_device
  39. def loopdev_detach(loopdev):
  40. logger.info("loopdev detach")
  41. # loopback dev may be tied up a bit by udev events triggered
  42. # by partition events
  43. for try_cnt in range(10, 1, -1):
  44. try:
  45. exec_sudo(["losetup", "-d", loopdev])
  46. return
  47. except BlockDeviceSetupException as e:
  48. # Do not raise an error - maybe other cleanup methods
  49. # can at least do some more work.
  50. logger.error("loopdev detach failed (%s)", e.returncode)
  51. logger.debug("Gave up trying to detach [%s]", loopdev)
  52. return 1
  53. class LocalLoopNode(NodeBase):
  54. """Level0: Local loop image device handling.
  55. This class handles local loop devices that can be used
  56. for VM image installation.
  57. """
  58. def __init__(self, config, default_config, state):
  59. logger.debug("Creating LocalLoop object; config [%s] "
  60. "default_config [%s]", config, default_config)
  61. super(LocalLoopNode, self).__init__(config['name'], state)
  62. if 'size' in config:
  63. self.size = parse_abs_size_spec(config['size'])
  64. logger.debug("Image size [%s]", self.size)
  65. else:
  66. self.size = parse_abs_size_spec(default_config['image-size'])
  67. logger.debug("Using default image size [%s]", self.size)
  68. if 'directory' in config:
  69. self.image_dir = config['directory']
  70. else:
  71. self.image_dir = default_config['image-dir']
  72. self.filename = os.path.join(self.image_dir, self.name + ".raw")
  73. def get_edges(self):
  74. """Because this is created without base, there are no edges."""
  75. return ([], [])
  76. def create(self):
  77. logger.debug("[%s] Creating loop on [%s] with size [%d]",
  78. self.name, self.filename, self.size)
  79. self.add_rollback(image_delete, self.filename)
  80. image_create(self.filename, self.size)
  81. block_device = loopdev_attach(self.filename)
  82. self.add_rollback(loopdev_detach, block_device)
  83. if 'blockdev' not in self.state:
  84. self.state['blockdev'] = {}
  85. self.state['blockdev'][self.name] = {"device": block_device,
  86. "image": self.filename}
  87. logger.debug("Created loop name [%s] device [%s] image [%s]",
  88. self.name, block_device, self.filename)
  89. return
  90. def umount(self):
  91. loopdev_detach(self.state['blockdev'][self.name]['device'])
  92. def delete(self):
  93. image_delete(self.state['blockdev'][self.name]['image'])
  94. class LocalLoop(PluginBase):
  95. def __init__(self, config, defaults, state):
  96. super(LocalLoop, self).__init__()
  97. self.node = LocalLoopNode(config, defaults, state)
  98. def get_nodes(self):
  99. return [self.node]