| @@ -24,7 +24,9 @@ from .volume import command_volume_create, \ | |||
| command_volume_untag, \ | |||
| command_volume_remove, \ | |||
| command_volume_set, \ | |||
| command_volume_get | |||
| command_volume_get, \ | |||
| command_volume_protect, \ | |||
| command_volume_unprotect | |||
| import sys | |||
| from .zfs import zfs_init | |||
| from .jail import command_jail_create, \ | |||
| @@ -209,6 +211,14 @@ def create_parser(): | |||
| parser.add_argument('reference', type=str) | |||
| 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 | |||
| subparsers = ListForwarder([ subparsers_top.add_parser(cmd).add_subparsers(dest='L2_command') \ | |||
| for cmd in ['compose', 'comp', 'c'] ]) | |||
| @@ -51,7 +51,7 @@ def command_volume_remove(args): | |||
| try: | |||
| name, _ = zfs_find(ref, focker_type='volume') | |||
| print('Removing:', name) | |||
| zfs_run(['zfs', 'destroy', '-r', '-f', name]) | |||
| zfs_destroy(name) | |||
| except: | |||
| if not args.force: | |||
| raise | |||
| @@ -71,3 +71,17 @@ def command_volume_get(args): | |||
| res = zfs_parse_output(['zfs', 'get', '-H', ','.join(args.properties), name]) | |||
| res = [ [ args.properties[i], a[2] ] for i, a in enumerate(res) ] | |||
| 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 | |||
| while again: | |||
| 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() | |||
| for r in lst: | |||
| if r[2] == '-': | |||
| @@ -92,10 +92,29 @@ def zfs_prune(focker_type='image'): | |||
| for r in lst: | |||
| if r[0] == '-' or r[1] != '-': | |||
| 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): | |||
| @@ -7,13 +7,19 @@ from focker.volume import command_volume_create, \ | |||
| command_volume_untag, \ | |||
| command_volume_remove, \ | |||
| command_volume_set, \ | |||
| command_volume_get | |||
| command_volume_get, \ | |||
| command_volume_protect, \ | |||
| command_volume_unprotect | |||
| from focker.zfs import zfs_find, \ | |||
| zfs_mountpoint, \ | |||
| zfs_exists, \ | |||
| zfs_parse_output | |||
| zfs_parse_output, \ | |||
| zfs_destroy, \ | |||
| zfs_prune, \ | |||
| zfs_run | |||
| import os | |||
| import focker.volume | |||
| import focker.zfs | |||
| def test_command_volume_create(): | |||
| @@ -162,3 +168,52 @@ def test_command_volume_get(monkeypatch): | |||
| assert headers == [ 'Property', 'Value' ] | |||
| # assert lst == ['on', '1G'] | |||
| 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']) | |||