A pure Python implementation of the lxc_container ansible module
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.

251 lines
7.4 KiB

  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. DOCUMENTATION = """
  4. ---
  5. module: lilik_container
  6. short_description: Manage LXC Containers - Lilik style
  7. version_added: 2.1.0
  8. description:
  9. - Management of LXC containers
  10. options:
  11. name:
  12. description:
  13. - Name of a container
  14. required: true
  15. backing_store:
  16. choices:
  17. - dir
  18. - lvm
  19. - loop
  20. - btrfs
  21. - overlayfs
  22. - zfs
  23. description
  24. - Backend storage type for the container.
  25. required: false
  26. default: lvm
  27. config:
  28. description:
  29. - Path to the LXC configuration file
  30. required: false
  31. default: /etc/lxc/default.conf
  32. template:
  33. description:
  34. - Name of the template to use within an LXC create.
  35. required: false
  36. default: debian
  37. template_options:
  38. description:
  39. - Template options when building the container.
  40. required: false
  41. default: --release jessie --packages=ssh,python
  42. lv_name:
  43. description:
  44. - Name of the logical volume, defaults to the container name.
  45. default: "vm_{{$CONTAINER_NAME}}"
  46. required: false
  47. vg_name:
  48. description:
  49. - If Backend store is lvm, specify the name of the volume group.
  50. default: newsysvg
  51. required: false
  52. fs_type:
  53. description:
  54. - Create fstype TYPE.
  55. default: ext4
  56. required: false
  57. fs_size:
  58. description:
  59. - File system Size.
  60. default: 5G
  61. required: false
  62. container_command:
  63. description:
  64. - Run a command within a container.
  65. required: false
  66. state:
  67. choices:
  68. - started
  69. - stopped
  70. - restarted
  71. - absent
  72. - frozen
  73. description:
  74. - Define the state of a container.
  75. required: false
  76. default: started
  77. requirements:
  78. - 'liblxc1 >= 1.1.5 # OS package'
  79. - 'python >= 2.6 # OS package'
  80. - 'lxc-python2 >= 0.1 #PIP package from https://github.com/lxc/python2-lxc'
  81. """
  82. from ansible.module_utils.basic import *
  83. class LilikContainer(object):
  84. """
  85. A generic lxc container manipulation object based on python-lxc
  86. """
  87. def __init__(self, module):
  88. self.module = module
  89. self.state = module.params['state']
  90. self.name = module.params['name']
  91. self.template = module.params['template']
  92. self.template_options = module.params['template_options']
  93. self.config = module.params['config']
  94. self.backing_store = module.params['backing_store']
  95. # handle default name
  96. self.lvname = module.params.get('lv_name', 'vm_%s' % module.params['name'])
  97. self.vgname = module.params['vg_name']
  98. self.fstype = module.params['fs_type']
  99. self.fssize = module.params['fs_size']
  100. def create_container(self):
  101. """
  102. Create a lxc.Container object as specified in the playbook, use it
  103. to create a lxc container and returns the reference
  104. """
  105. container_options = {
  106. 'bdev': self.backing_store,
  107. 'config': self.config,
  108. 'lvname': self.lvname,
  109. 'vgname': self.vgname,
  110. 'fstype': self.fstype,
  111. 'fssize': self.fssize,
  112. 'bdev' : self.backing_store,
  113. }
  114. try:
  115. import lxc
  116. except ImportError:
  117. self.module.fail_json(changed=False, msg='Error importing lxc')
  118. container = lxc.Container(name = self.name)
  119. # TODO: python2-lxc does not like bdevtype but python-lxc does
  120. return container.create(
  121. template = self.template,
  122. args = container_options,
  123. # bdevtype = self.backing_store
  124. )
  125. def main():
  126. module = AnsibleModule(
  127. argument_spec = dict(
  128. backing_store = dict(
  129. default='lvm',
  130. choices=['dir', 'lvm', 'loop', 'btrsf', 'overlayfs', 'zfs',],
  131. type='str',
  132. ),
  133. config = dict(
  134. required=False,
  135. default='/etc/lxc/default.conf',
  136. ),
  137. container_command = dict(
  138. required=False,
  139. type='str',
  140. default='',
  141. ),
  142. fs_size = dict(
  143. required=False,
  144. default='5G',
  145. type='str',
  146. ),
  147. fs_type = dict(
  148. required=False,
  149. default='ext4',
  150. type='str',
  151. ),
  152. lv_name = dict(
  153. type='str',
  154. ),
  155. name = dict(
  156. required=True,
  157. type='str',
  158. ),
  159. state = dict(
  160. default='started',
  161. choices=['started', 'stopped', 'restarted', 'absent', 'frozen'],
  162. type='str',
  163. ),
  164. template = dict(
  165. required=False,
  166. default='debian',
  167. type='str',
  168. ),
  169. template_options = dict(
  170. required=False,
  171. default='--release jessie --packages=ssh,python',
  172. type='str',
  173. ),
  174. vg_name = dict(
  175. required=False,
  176. default='newsysvg',
  177. type='str',
  178. ),
  179. )
  180. )
  181. try:
  182. import lxc
  183. except ImportError:
  184. module.fail_json(changed=False, msg='liblxc is required for this module to work')
  185. lilik_container = LilikContainer(module)
  186. result = {}
  187. result['name'] = lilik_container.name
  188. result['state'] = lilik_container.state
  189. if lilik_container.state == 'absent':
  190. # destroy the container
  191. if lilik_container.destoy():
  192. module.exit_json(changed=True)
  193. # TODO: remove redundant test
  194. # test wether the container is absent or not
  195. if lilik_container.name in lxc.list_containers():
  196. module.fail_json(changed=False)
  197. # the container has been removed
  198. else:
  199. module.exit_json(changed=True)
  200. # end TODO: remove redundant test
  201. elif lilik_container.state in ['started', 'stopped', 'restarted', 'frozen']:
  202. # the container exists, just set the state as required
  203. if lilik_container.name in lxc.list_containers():
  204. container_actions_from_state = {
  205. 'started': lilik_container.start,
  206. 'stopped': lilik_container.stop,
  207. 'restarted': lilik_container.restart,
  208. 'frozen': lilik_container.freeze,
  209. }
  210. # selected action
  211. action = container_actions.get(container.state)
  212. if action():
  213. module.exit_json(changed=True)
  214. else:
  215. module.exit_json(changed=False)
  216. # the container does not exists, create it
  217. else:
  218. try:
  219. new_container = lilik_container.create_container()
  220. module.exit_json(changed=True)
  221. except Exception as e:
  222. module.fail_json(
  223. changed=False,
  224. msg='An excption was raised while creating the container',
  225. exception_message=str(e),
  226. )
  227. if __name__ == '__main__':
  228. main()