IF YOU WOULD LIKE TO GET AN ACCOUNT, please write an email to s dot adaszewski at gmail dot com. User accounts are meant only to report issues and/or generate pull requests. This is a purpose-specific Git hosting for ADARED projects. Thank you for your understanding!
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

217 rindas
7.7KB

  1. import subprocess
  2. from .zfs import *
  3. import random
  4. import shutil
  5. import json
  6. from tabulate import tabulate
  7. import os
  8. import jailconf
  9. import shlex
  10. from .mount import getmntinfo
  11. def gen_env_command(command, env):
  12. env = [ 'export ' + k + '=' + shlex.quote(v) \
  13. for (k, v) in env.items() ]
  14. command = ' && '.join(env + [ command ])
  15. return command
  16. def jail_create(path, command, env, mounts):
  17. name = os.path.split(path)[-1]
  18. if os.path.exists('/etc/jail.conf'):
  19. conf = jailconf.load('/etc/jail.conf')
  20. else:
  21. conf = jailconf.JailConf()
  22. conf[name] = blk = jailconf.JailBlock()
  23. blk['path'] = path
  24. if command:
  25. command = gen_env_command(command, env)
  26. blk['exec.start'] = command
  27. prestart = [ 'cp /etc/resolv.conf ' +
  28. shlex.quote(os.path.join(path, 'etc/resolv.conf')) ]
  29. poststop = []
  30. if mounts:
  31. for (from_, on) in mounts:
  32. if not from_.startswith('/'):
  33. from_, _ = zfs_find(from_, focker_type='volume')
  34. from_ = zfs_mountpoint(from_)
  35. prestart.append('mount -t nullfs ' + shlex.quote(from_) +
  36. ' ' + shlex.quote(os.path.join(path, on.strip('/'))))
  37. poststop += [ 'umount -f ' +
  38. os.path.join(path, on.strip('/')) \
  39. for (_, on) in reversed(mounts) ]
  40. if prestart:
  41. blk['exec.prestart'] = shlex.quote(' && '.join(prestart))
  42. if poststop:
  43. blk['exec.poststop'] = shlex.quote(' && '.join(poststop))
  44. blk['persist'] = True
  45. blk['interface'] = 'lo1'
  46. blk['ip4.addr'] = '127.0.1.0'
  47. blk['mount.devfs'] = True
  48. blk['exec.clean'] = True
  49. conf.write('/etc/jail.conf')
  50. def jail_run_v2(path, command, env, mounts):
  51. name = os.path.split(path)[-1]
  52. command = gen_env_command(command, env)
  53. jail_create(path, None, None, mounts)
  54. subprocess.check_output([ 'jail', '-c', name ])
  55. subprocess.run([ 'jexec', name, '/bin/sh', '-c', command ])
  56. subprocess.check_output([ 'jail', '-r', name ])
  57. def get_jid(path):
  58. data = json.loads(subprocess.check_output(['jls', '--libxo=json']))
  59. lst = data['jail-information']['jail']
  60. lst = list(filter(lambda a: a['path'] == path, lst))
  61. if len(lst) == 0:
  62. raise ValueError('JID not found for path: ' + path)
  63. if len(lst) > 1:
  64. raise ValueError('Ambiguous JID for path: ' + path)
  65. return str(lst[0]['jid'])
  66. def do_mounts(path, mounts):
  67. print('mounts:', mounts)
  68. for (source, target) in mounts:
  69. if source.startswith('/'):
  70. name = source
  71. else:
  72. name, _ = zfs_find(source, focker_type='volume')
  73. name = zfs_mountpoint(name)
  74. while target.startswith('/'):
  75. target = target[1:]
  76. subprocess.check_output(['mount', '-t', 'nullfs', name, os.path.join(path, target)])
  77. def undo_mounts(path, mounts):
  78. for (_, target) in reversed(mounts):
  79. while target.startswith('/'):
  80. target = target[1:]
  81. subprocess.check_output(['umount', '-f', os.path.join(path, target)])
  82. def jail_run(path, command, mounts=[]):
  83. command = ['jail', '-c', 'host.hostname=' + os.path.split(path)[1], 'persist=1', 'mount.devfs=1', 'interface=lo1', 'ip4.addr=127.0.1.0', 'path=' + path, 'command', '/bin/sh', '-c', command]
  84. print('Running:', ' '.join(command))
  85. try:
  86. do_mounts(path, mounts)
  87. shutil.copyfile('/etc/resolv.conf', os.path.join(path, 'etc/resolv.conf'))
  88. res = subprocess.run(command)
  89. finally:
  90. try:
  91. subprocess.run(['jail', '-r', get_jid(path)])
  92. except ValueError:
  93. pass
  94. subprocess.run(['umount', '-f', os.path.join(path, 'dev')])
  95. undo_mounts(path, mounts)
  96. if res.returncode != 0:
  97. # subprocess.run(['umount', os.path.join(path, 'dev')])
  98. raise RuntimeError('Command failed')
  99. def jail_stop(path):
  100. try:
  101. jid = get_jid(path)
  102. subprocess.run(['jail', '-r', jid])
  103. except ValueError:
  104. print('JID could not be determined')
  105. mi = getmntinfo()
  106. for m in mi:
  107. mntonname = m['f_mntonname'].decode('utf-8')
  108. if mntonname.startswith(path + os.path.sep):
  109. print('Unmounting:', mntonname)
  110. subprocess.run(['umount', '-f', mntonname])
  111. def jail_remove(path):
  112. print('Removing jail:', path)
  113. jail_stop(path)
  114. subprocess.run(['zfs', 'destroy', '-r', '-f', zfs_name(path)])
  115. if os.path.exists('/etc/jail.conf'):
  116. conf = jailconf.load('/etc/jail.conf')
  117. name = os.path.split(path)[-1]
  118. if name in conf:
  119. del conf[name]
  120. conf.write('/etc/jail.conf')
  121. def command_jail_create(args):
  122. image, _ = zfs_find(args.image, focker_type='image', zfs_type='snapshot')
  123. sha256 = bytes([ random.randint(0, 255) for _ in range(32) ]).hex()
  124. lst = zfs_list(fields=['focker:sha256'], focker_type='image')
  125. lst = list(filter(lambda a: a[0] == sha256, lst))
  126. if lst:
  127. raise ValueError('Whew, a collision...')
  128. poolname = zfs_poolname()
  129. for pre in range(7, 32):
  130. name = poolname + '/focker/jails/' + sha256[:pre]
  131. if not zfs_exists(name):
  132. break
  133. zfs_run(['zfs', 'clone', '-o', 'focker:sha256=' + sha256] + \
  134. (['-o', 'focker:tags=' + ' '.join(args.tags)] if args.tags else []) + \
  135. [image, name])
  136. path = zfs_mountpoint(name)
  137. jail_create(path, args.command,
  138. { a.split(':')[0]: ':'.join(a.split(':')[1:]) \
  139. for a in args.env },
  140. [ [a.split(':')[0], ':'.join(a.split(':')[1:])] \
  141. for a in args.mounts ] )
  142. print(sha256)
  143. print(path)
  144. def command_jail_run(args):
  145. base, _ = zfs_snapshot_by_tag_or_sha256(args.image)
  146. # root = '/'.join(base.split('/')[:-1])
  147. for _ in range(10**6):
  148. sha256 = bytes([ random.randint(0, 255) for _ in range(32) ]).hex()
  149. name = sha256[:7]
  150. name = base.split('/')[0] + '/focker/jails/' + name
  151. if not zfs_exists(name):
  152. break
  153. zfs_run(['zfs', 'clone', '-o', 'focker:sha256=' + sha256, base, name])
  154. try:
  155. mounts = list(map(lambda a: a.split(':'), args.mounts))
  156. jail_run(zfs_mountpoint(name), args.command, mounts)
  157. # subprocess.check_output(['jail', '-c', 'interface=lo1', 'ip4.addr=127.0.1.0', 'path=' + zfs_mountpoint(name), 'command', command])
  158. finally:
  159. # subprocess.run(['umount', zfs_mountpoint(name) + '/dev'])
  160. zfs_run(['zfs', 'destroy', '-f', name])
  161. # raise
  162. def command_jail_list(args):
  163. lst = zfs_list(fields=['focker:sha256,focker:tags,mountpoint'], focker_type='jail')
  164. jails = subprocess.check_output(['jls', '--libxo=json'])
  165. jails = json.loads(jails)['jail-information']['jail']
  166. jails = { j['path']: j for j in jails }
  167. lst = list(map(lambda a: [ a[1],
  168. a[0] if args.full_sha256 else a[0][:7],
  169. a[2],
  170. jails[a[2]]['jid'] if a[2] in jails else '-' ], lst))
  171. print(tabulate(lst, headers=['Tags', 'SHA256', 'mountpoint', 'JID']))
  172. def command_jail_tag(args):
  173. name, _ = zfs_find(args.reference, focker_type='jail')
  174. zfs_untag(args.tags, focker_type='jail')
  175. zfs_tag(name, args.tags)
  176. def command_jail_untag(args):
  177. zfs_untag(args.tags, focker_type='jail')
  178. def command_jail_prune(args):
  179. jails = subprocess.check_output(['jls', '--libxo=json'])
  180. jails = json.loads(jails)['jail-information']['jail']
  181. used = set()
  182. for j in jails:
  183. used.add(j['path'])
  184. lst = zfs_list(fields=['focker:sha256,focker:tags,mountpoint,name'], focker_type='jail')
  185. for j in lst:
  186. if j[1] == '-' and j[2] not in used:
  187. jail_remove(j[2])