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.

zfs.py 6.6KB

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