@@ -24,7 +24,9 @@ from .volume import command_volume_create, \ | |||||
command_volume_untag, \ | command_volume_untag, \ | ||||
command_volume_remove, \ | command_volume_remove, \ | ||||
command_volume_set, \ | command_volume_set, \ | ||||
command_volume_get | |||||
command_volume_get, \ | |||||
command_volume_protect, \ | |||||
command_volume_unprotect | |||||
import sys | import sys | ||||
from .zfs import zfs_init | from .zfs import zfs_init | ||||
from .jail import command_jail_create, \ | from .jail import command_jail_create, \ | ||||
@@ -209,6 +211,14 @@ def create_parser(): | |||||
parser.add_argument('reference', type=str) | parser.add_argument('reference', type=str) | ||||
parser.add_argument('properties', type=str, nargs=argparse.REMAINDER) | parser.add_argument('properties', type=str, nargs=argparse.REMAINDER) | ||||
parser = ListForwarder([subparsers.add_parser(cmd) for cmd in ['protect']]) | |||||
parser.set_defaults(func=command_volume_protect) | |||||
parser.add_argument('references', type=str, nargs='+') | |||||
parser = ListForwarder([subparsers.add_parser(cmd) for cmd in ['unprotect']]) | |||||
parser.set_defaults(func=command_volume_unprotect) | |||||
parser.add_argument('references', type=str, nargs='+') | |||||
# compose | # compose | ||||
subparsers = ListForwarder([ subparsers_top.add_parser(cmd).add_subparsers(dest='L2_command') \ | subparsers = ListForwarder([ subparsers_top.add_parser(cmd).add_subparsers(dest='L2_command') \ | ||||
for cmd in ['compose', 'comp', 'c'] ]) | for cmd in ['compose', 'comp', 'c'] ]) | ||||
@@ -51,7 +51,7 @@ def command_volume_remove(args): | |||||
try: | try: | ||||
name, _ = zfs_find(ref, focker_type='volume') | name, _ = zfs_find(ref, focker_type='volume') | ||||
print('Removing:', name) | print('Removing:', name) | ||||
zfs_run(['zfs', 'destroy', '-r', '-f', name]) | |||||
zfs_destroy(name) | |||||
except: | except: | ||||
if not args.force: | if not args.force: | ||||
raise | raise | ||||
@@ -71,3 +71,17 @@ def command_volume_get(args): | |||||
res = zfs_parse_output(['zfs', 'get', '-H', ','.join(args.properties), name]) | res = zfs_parse_output(['zfs', 'get', '-H', ','.join(args.properties), name]) | ||||
res = [ [ args.properties[i], a[2] ] for i, a in enumerate(res) ] | res = [ [ args.properties[i], a[2] ] for i, a in enumerate(res) ] | ||||
print(tabulate(res, headers=['Property', 'Value'])) | print(tabulate(res, headers=['Property', 'Value'])) | ||||
def command_volume_protect(args): | |||||
for ref in args.references: | |||||
name, _ = zfs_find(ref, focker_type='volume') | |||||
print('Protecting:', name) | |||||
zfs_protect(name) | |||||
def command_volume_unprotect(args): | |||||
for ref in args.references: | |||||
name, _ = zfs_find(ref, focker_type='volume') | |||||
print('Unprotecting:', name) | |||||
zfs_unprotect(name) |
@@ -83,7 +83,7 @@ def zfs_prune(focker_type='image'): | |||||
again = True | again = True | ||||
while again: | while again: | ||||
again = False | again = False | ||||
lst = zfs_parse_output(['zfs', 'list', '-o', 'focker:sha256,focker:tags,origin,name', '-H', '-r', poolname + '/focker/' + focker_type + 's']) | |||||
lst = zfs_parse_output(['zfs', 'list', '-o', 'focker:sha256,focker:tags,origin,name,focker:protect', '-H', '-r', poolname + '/focker/' + focker_type + 's']) | |||||
used = set() | used = set() | ||||
for r in lst: | for r in lst: | ||||
if r[2] == '-': | if r[2] == '-': | ||||
@@ -92,10 +92,29 @@ def zfs_prune(focker_type='image'): | |||||
for r in lst: | for r in lst: | ||||
if r[0] == '-' or r[1] != '-': | if r[0] == '-' or r[1] != '-': | ||||
continue | continue | ||||
if r[3] not in used: | |||||
print('Removing:', r[3]) | |||||
zfs_run(['zfs', 'destroy', '-r', '-f', r[3]]) | |||||
again = True | |||||
if r[3] in used: | |||||
continue | |||||
if r[4] != '-': | |||||
print('%s is protected against removal' % r[3]) | |||||
continue | |||||
print('Removing:', r[3]) | |||||
zfs_run(['zfs', 'destroy', '-r', '-f', r[3]]) | |||||
again = True | |||||
def zfs_destroy(name): | |||||
lst = zfs_parse_output(['zfs', 'get', '-H', 'focker:protect', name]) | |||||
if lst[0][2] != '-': | |||||
raise RuntimeError('%s is protected against removal' % name) | |||||
zfs_run(['zfs', 'destroy', '-r', '-f', name]) | |||||
def zfs_protect(name): | |||||
zfs_run(['zfs', 'set', 'focker:protect=on', name]) | |||||
def zfs_unprotect(name): | |||||
zfs_run(['zfs', 'inherit', '-r', 'focker:protect', name]) | |||||
def zfs_clone(name, target_name): | def zfs_clone(name, target_name): | ||||
@@ -7,13 +7,19 @@ from focker.volume import command_volume_create, \ | |||||
command_volume_untag, \ | command_volume_untag, \ | ||||
command_volume_remove, \ | command_volume_remove, \ | ||||
command_volume_set, \ | command_volume_set, \ | ||||
command_volume_get | |||||
command_volume_get, \ | |||||
command_volume_protect, \ | |||||
command_volume_unprotect | |||||
from focker.zfs import zfs_find, \ | from focker.zfs import zfs_find, \ | ||||
zfs_mountpoint, \ | zfs_mountpoint, \ | ||||
zfs_exists, \ | zfs_exists, \ | ||||
zfs_parse_output | |||||
zfs_parse_output, \ | |||||
zfs_destroy, \ | |||||
zfs_prune, \ | |||||
zfs_run | |||||
import os | import os | ||||
import focker.volume | import focker.volume | ||||
import focker.zfs | |||||
def test_command_volume_create(): | def test_command_volume_create(): | ||||
@@ -162,3 +168,52 @@ def test_command_volume_get(monkeypatch): | |||||
assert headers == [ 'Property', 'Value' ] | assert headers == [ 'Property', 'Value' ] | ||||
# assert lst == ['on', '1G'] | # assert lst == ['on', '1G'] | ||||
subprocess.check_output(['focker', 'volume', 'remove', 'test-command-volume-get']) | subprocess.check_output(['focker', 'volume', 'remove', 'test-command-volume-get']) | ||||
def test_command_volume_protect(monkeypatch): | |||||
subprocess.check_output(['focker', 'volume', 'remove', '--force', 'test-command-volume-protect']) | |||||
subprocess.check_output(['focker', 'volume', 'create', '-t', 'test-command-volume-protect']) | |||||
args = lambda: 0 | |||||
args.references = ['test-command-volume-protect'] | |||||
command_volume_protect(args) | |||||
name, sha256 = zfs_find('test-command-volume-protect', focker_type='volume') | |||||
mountpoint = zfs_mountpoint(name) | |||||
lst = zfs_parse_output(['zfs', 'get', '-H', 'focker:protect', name]) | |||||
assert len(lst) == 1 | |||||
assert lst[0][2] == 'on' | |||||
with pytest.raises(RuntimeError): | |||||
zfs_destroy(name) | |||||
subprocess.check_output(['focker', 'volume', 'untag', 'test-command-volume-protect']) | |||||
lst = zfs_parse_output(['zfs', 'get', '-H', 'focker:tags', name]) | |||||
assert len(lst) == 1 | |||||
assert lst[0][2] == '-' | |||||
n_called = 0 | |||||
def fake_run(*args, **kwargs): | |||||
nonlocal n_called | |||||
n_called += 1 | |||||
return zfs_run(*args, **kwargs) | |||||
monkeypatch.setattr(focker.zfs, 'zfs_run', fake_run) | |||||
zfs_prune(focker_type='volume') | |||||
assert not n_called == 1 | |||||
with pytest.raises(subprocess.CalledProcessError): | |||||
subprocess.check_output(['focker', 'volume', 'remove', sha256]) | |||||
subprocess.check_output(['zfs', 'destroy', '-r', '-f', name]) | |||||
assert not zfs_exists(name) | |||||
assert not os.path.exists(mountpoint) | |||||
def test_command_volume_unprotect(): | |||||
subprocess.check_output(['focker', 'volume', 'remove', '--force', 'test-command-volume-unprotect']) | |||||
subprocess.check_output(['focker', 'volume', 'create', '-t', 'test-command-volume-unprotect']) | |||||
subprocess.check_output(['focker', 'volume', 'protect', 'test-command-volume-unprotect']) | |||||
name, _ = zfs_find('test-command-volume-unprotect', focker_type='volume') | |||||
lst = zfs_parse_output(['zfs', 'get', '-H', 'focker:protect', name]) | |||||
assert len(lst) == 1 | |||||
assert lst[0][2] == 'on' | |||||
args = lambda: 0 | |||||
args.references = ['test-command-volume-unprotect'] | |||||
command_volume_unprotect(args) | |||||
lst = zfs_parse_output(['zfs', 'get', '-H', 'focker:protect', name]) | |||||
assert len(lst) == 1 | |||||
assert lst[0][2] == '-' | |||||
subprocess.check_output(['focker', 'volume', 'remove', 'test-command-volume-unprotect']) |