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!
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.

199 lines
7.0KB

  1. import subprocess
  2. import csv
  3. import io
  4. import os
  5. class AmbiguousValueError(ValueError):
  6. def __init__(self, msg):
  7. super().__init__(msg)
  8. def zfs_run(command):
  9. # print('Running:', command)
  10. out = subprocess.check_output(command, stderr=subprocess.STDOUT)
  11. return out
  12. def zfs_parse_output(command):
  13. out = zfs_run(command)
  14. s = io.StringIO(out.decode('utf-8'))
  15. r = csv.reader(s, delimiter='\t')
  16. return [a for a in r]
  17. def zfs_get_type(name):
  18. lst = zfs_parse_output(['zfs', 'list', '-o', 'name,type', '-H', name])
  19. return lst[0][1]
  20. def zfs_snapshot_by_tag_or_sha256(s, focker_type='image'):
  21. lst = zfs_parse_output(['zfs', 'list', '-o', 'focker:sha256,focker:tags,type,name',
  22. '-H', '-t', 'snapshot', '-r', poolname + '/focker/' + focker_type + 's'])
  23. lst = list(filter(lambda a: (a[0] == s or s in a[1].split(' ')) and a[2] == 'snapshot', lst))
  24. if len(lst) == 0:
  25. raise ValueError('Reference not found: ' + s)
  26. if len(lst) > 1:
  27. raise AmbiguousValueError('Ambiguous reference: ' + s)
  28. return (lst[0][3], lst[0][0])
  29. def zfs_find(reference, focker_type='image', zfs_type='filesystem'):
  30. poolname = zfs_poolname()
  31. lst = zfs_parse_output(['zfs', 'list', '-o', 'focker:sha256,focker:tags,type,name', '-H', '-t', zfs_type, '-r', poolname + '/focker/' + focker_type + 's'])
  32. def match(sha256, tags, type, name, exact=False):
  33. if exact:
  34. predicate = lambda a: (a == reference)
  35. else:
  36. predicate = lambda a: a.startswith(reference)
  37. if predicate(sha256) or \
  38. any(map(predicate, tags.split(' '))) or \
  39. predicate(name.split('/')[-1]):
  40. return True
  41. return False
  42. lst = list(filter(lambda a: match(*a), lst))
  43. exact_lst = list(filter(lambda a: match(*a, exact=True), lst))
  44. if len(lst) == 0:
  45. raise ValueError('Reference not found: ' + reference)
  46. if len(lst) > 1:
  47. if len(exact_lst) == 1:
  48. return (exact_lst[0][3], exact_lst[0][0])
  49. raise AmbiguousValueError('Ambiguous reference: ' + reference)
  50. return (lst[0][3], lst[0][0])
  51. def zfs_list(fields=['name'], focker_type='image', zfs_type='filesystem'):
  52. poolname = zfs_poolname()
  53. fields.append('focker:sha256')
  54. lst = zfs_parse_output(['zfs', 'list', '-o', ','.join(fields),
  55. '-H', '-t', zfs_type, '-r', poolname + '/focker/' + focker_type + 's'])
  56. lst = list(filter(lambda a: a[-1] != '-', lst))
  57. return lst
  58. def zfs_prune(focker_type='image'):
  59. poolname = zfs_poolname()
  60. again = True
  61. while again:
  62. again = False
  63. lst = zfs_parse_output(['zfs', 'list', '-o', 'focker:sha256,focker:tags,origin,name', '-H', '-r', poolname + '/focker/' + focker_type + 's'])
  64. used = set()
  65. for r in lst:
  66. if r[2] == '-':
  67. continue
  68. used.add(r[2].split('@')[0])
  69. for r in lst:
  70. if r[0] == '-' or r[1] != '-':
  71. continue
  72. if r[3] not in used:
  73. print('Removing:', r[3])
  74. zfs_run(['zfs', 'destroy', '-r', '-f', r[3]])
  75. again = True
  76. def zfs_clone(name, target_name):
  77. zfs_run(['zfs', 'clone', name, target_name])
  78. def zfs_exists(name):
  79. try:
  80. zfs_run(['zfs', 'list', name])
  81. except subprocess.CalledProcessError as e:
  82. return False
  83. return True
  84. def zfs_set_props(name, props):
  85. for (k, v) in props.items():
  86. zfs_run(['zfs', 'set', k + '=' + v, name])
  87. def zfs_mountpoint(name):
  88. lst = zfs_parse_output(['zfs', 'list', '-o', 'mountpoint', '-H', name])
  89. return lst[0][0]
  90. def zfs_exists_snapshot_sha256(sha256, focker_type='image'):
  91. poolname = zfs_poolname()
  92. lst = zfs_parse_output(['zfs', 'list', '-o', 'focker:sha256', '-t', 'snap',
  93. '-r', poolname + '/focker/' + focker_type + 's'])
  94. lst = list(filter(lambda a: a[0] == sha256, lst))
  95. if len(lst) == 0:
  96. return False
  97. return True
  98. def zfs_snapshot_by_sha256(sha256, focker_type='image'):
  99. poolname = zfs_poolname()
  100. lst = zfs_parse_output(['zfs', 'list', '-o', 'focker:sha256,name',
  101. '-t', 'snap', '-H', '-r', poolname + '/focker/' + focker_type + 's'])
  102. lst = list(filter(lambda a: a[0] == sha256, lst))
  103. if len(lst) == 0:
  104. raise ValueError('Snapshot with given sha256 does not exist: ' + sha256)
  105. if len(lst) > 1:
  106. raise AmbiguousValueError('Ambiguous snapshot sha256: ' + sha256)
  107. return lst[0][1]
  108. def zfs_tag(name, tags, replace=False):
  109. if any(map(lambda a: ' ' in a, tags)):
  110. raise ValueError('Tags cannot contain spaces')
  111. lst = zfs_parse_output(['zfs', 'list', '-o', 'focker:tags', '-H', name])
  112. if not replace:
  113. tags = list(tags)
  114. tags.extend(lst[0][0].split(' '))
  115. tags = list(set(tags))
  116. tags = list(filter(lambda a: a != '-', tags))
  117. if len(tags) > 0:
  118. zfs_run(['zfs', 'set', 'focker:tags=' + ' '.join(tags), name])
  119. else:
  120. zfs_run(['zfs', 'inherit', 'focker:tags', name])
  121. def zfs_untag(tags, focker_type='image'):
  122. if any(map(lambda a: ' ' in a, tags)):
  123. raise ValueError('Tags cannot contain spaces')
  124. # print('zfs_untag(), tags:', tags)
  125. poolname = zfs_poolname()
  126. lst = zfs_parse_output(['zfs', 'list', '-o', 'name,focker:tags', '-H', '-r', poolname + '/focker/' + focker_type + 's'])
  127. lst = filter(lambda a: any([b in a[1].split(' ') for b in tags]), lst)
  128. for row in lst:
  129. cur_tags = row[1].split(' ')
  130. for t in tags:
  131. if t in cur_tags:
  132. cur_tags.remove(t)
  133. zfs_tag(row[0], cur_tags, replace=True)
  134. def zfs_name(path):
  135. lst = zfs_parse_output(['zfs', 'list', '-o', 'name', '-H', path])
  136. if len(lst) == 0:
  137. raise ValueError('Not a ZFS path')
  138. if len(lst) > 1:
  139. raise AmbiguousValueError('Ambiguous ZFS path')
  140. return lst[0][0]
  141. def zfs_poolname():
  142. poolname = zfs_parse_output(['zfs', 'list', '-H', '/'])
  143. if len(poolname) == 0:
  144. raise ValueError('Not a ZFS root')
  145. poolname = poolname[0][0].split('/')[0]
  146. return poolname
  147. def zfs_init():
  148. poolname = zfs_poolname()
  149. print('poolname:', poolname)
  150. for path in ['/focker', '/focker/images', '/focker/volumes', '/focker/jails']:
  151. if not os.path.exists(path):
  152. os.mkdir(path)
  153. if not zfs_exists(poolname + '/focker'):
  154. zfs_run(['zfs', 'create', '-o', 'canmount=off', '-o', 'mountpoint=/focker', poolname + '/focker'])
  155. if not zfs_exists(poolname + '/focker/images'):
  156. zfs_run(['zfs', 'create', '-o', 'canmount=off', poolname + '/focker/images'])
  157. if not zfs_exists(poolname + '/focker/volumes'):
  158. zfs_run(['zfs', 'create', '-o', 'canmount=off', poolname + '/focker/volumes'])
  159. if not zfs_exists(poolname + '/focker/jails'):
  160. zfs_run(['zfs', 'create', '-o', 'canmount=off', poolname + '/focker/jails'])