moat-kv 0.70.23__py3-none-any.whl → 0.70.24__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- build/lib/docs/source/conf.py +201 -0
- build/lib/examples/pathify.py +45 -0
- build/lib/moat/kv/__init__.py +19 -0
- build/lib/moat/kv/_cfg.yaml +97 -0
- build/lib/moat/kv/_main.py +91 -0
- build/lib/moat/kv/actor/__init__.py +98 -0
- build/lib/moat/kv/actor/deletor.py +139 -0
- build/lib/moat/kv/auth/__init__.py +444 -0
- build/lib/moat/kv/auth/_test.py +166 -0
- build/lib/moat/kv/auth/password.py +234 -0
- build/lib/moat/kv/auth/root.py +58 -0
- build/lib/moat/kv/backend/__init__.py +67 -0
- build/lib/moat/kv/backend/mqtt.py +74 -0
- build/lib/moat/kv/backend/serf.py +45 -0
- build/lib/moat/kv/client.py +1025 -0
- build/lib/moat/kv/code.py +236 -0
- build/lib/moat/kv/codec.py +11 -0
- build/lib/moat/kv/command/__init__.py +1 -0
- build/lib/moat/kv/command/acl.py +180 -0
- build/lib/moat/kv/command/auth.py +261 -0
- build/lib/moat/kv/command/code.py +293 -0
- build/lib/moat/kv/command/codec.py +186 -0
- build/lib/moat/kv/command/data.py +265 -0
- build/lib/moat/kv/command/dump/__init__.py +143 -0
- build/lib/moat/kv/command/error.py +149 -0
- build/lib/moat/kv/command/internal.py +248 -0
- build/lib/moat/kv/command/job.py +433 -0
- build/lib/moat/kv/command/log.py +53 -0
- build/lib/moat/kv/command/server.py +114 -0
- build/lib/moat/kv/command/type.py +201 -0
- build/lib/moat/kv/config.py +46 -0
- build/lib/moat/kv/data.py +216 -0
- build/lib/moat/kv/errors.py +561 -0
- build/lib/moat/kv/exceptions.py +126 -0
- build/lib/moat/kv/mock/__init__.py +101 -0
- build/lib/moat/kv/mock/mqtt.py +159 -0
- build/lib/moat/kv/mock/serf.py +250 -0
- build/lib/moat/kv/mock/tracer.py +63 -0
- build/lib/moat/kv/model.py +1069 -0
- build/lib/moat/kv/obj/__init__.py +646 -0
- build/lib/moat/kv/obj/command.py +241 -0
- build/lib/moat/kv/runner.py +1347 -0
- build/lib/moat/kv/server.py +2809 -0
- build/lib/moat/kv/types.py +513 -0
- debian/moat-kv/usr/lib/python3/dist-packages/docs/source/conf.py +201 -0
- debian/moat-kv/usr/lib/python3/dist-packages/examples/pathify.py +45 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/__init__.py +19 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/_cfg.yaml +97 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/_main.py +91 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/actor/__init__.py +98 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/actor/deletor.py +139 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/__init__.py +444 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/_test.py +166 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/password.py +234 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/auth/root.py +58 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/__init__.py +67 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/mqtt.py +74 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/backend/serf.py +45 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/client.py +1025 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/code.py +236 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/codec.py +11 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/__init__.py +1 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/acl.py +180 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/auth.py +261 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/code.py +293 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/codec.py +186 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/data.py +265 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/dump/__init__.py +143 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/error.py +149 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/internal.py +248 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/job.py +433 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/log.py +53 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/server.py +114 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/command/type.py +201 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/config.py +46 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/data.py +216 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/errors.py +561 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/exceptions.py +126 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/__init__.py +101 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/mqtt.py +159 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/serf.py +250 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/mock/tracer.py +63 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/model.py +1069 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/obj/__init__.py +646 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/obj/command.py +241 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/runner.py +1347 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/server.py +2809 -0
- debian/moat-kv/usr/lib/python3/dist-packages/moat/kv/types.py +513 -0
- docs/source/conf.py +201 -0
- examples/pathify.py +45 -0
- moat/kv/actor/__init__.py +98 -0
- moat/kv/actor/deletor.py +139 -0
- moat/kv/auth/__init__.py +444 -0
- moat/kv/auth/_test.py +166 -0
- moat/kv/auth/password.py +234 -0
- moat/kv/auth/root.py +58 -0
- moat/kv/backend/__init__.py +67 -0
- moat/kv/backend/mqtt.py +74 -0
- moat/kv/backend/serf.py +45 -0
- moat/kv/command/__init__.py +1 -0
- moat/kv/command/acl.py +180 -0
- moat/kv/command/auth.py +261 -0
- moat/kv/command/code.py +293 -0
- moat/kv/command/codec.py +186 -0
- moat/kv/command/data.py +265 -0
- moat/kv/command/dump/__init__.py +143 -0
- moat/kv/command/error.py +149 -0
- moat/kv/command/internal.py +248 -0
- moat/kv/command/job.py +433 -0
- moat/kv/command/log.py +53 -0
- moat/kv/command/server.py +114 -0
- moat/kv/command/type.py +201 -0
- moat/kv/mock/__init__.py +101 -0
- moat/kv/mock/mqtt.py +159 -0
- moat/kv/mock/serf.py +250 -0
- moat/kv/mock/tracer.py +63 -0
- moat/kv/obj/__init__.py +646 -0
- moat/kv/obj/command.py +241 -0
- {moat_kv-0.70.23.dist-info → moat_kv-0.70.24.dist-info}/METADATA +2 -2
- moat_kv-0.70.24.dist-info/RECORD +137 -0
- moat_kv-0.70.24.dist-info/top_level.txt +9 -0
- moat_kv-0.70.23.dist-info/RECORD +0 -19
- moat_kv-0.70.23.dist-info/top_level.txt +0 -1
- {moat_kv-0.70.23.dist-info → moat_kv-0.70.24.dist-info}/WHEEL +0 -0
- {moat_kv-0.70.23.dist-info → moat_kv-0.70.24.dist-info}/licenses/LICENSE.txt +0 -0
moat/kv/command/error.py
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
# command line interface
|
2
|
+
from __future__ import annotations
|
3
|
+
|
4
|
+
import sys
|
5
|
+
|
6
|
+
import asyncclick as click
|
7
|
+
from moat.util import P, Path, yprint
|
8
|
+
|
9
|
+
from moat.kv.data import add_dates
|
10
|
+
from moat.kv.errors import ErrorRoot
|
11
|
+
|
12
|
+
|
13
|
+
@click.group() # pylint: disable=undefined-variable
|
14
|
+
@click.pass_obj
|
15
|
+
async def cli(obj):
|
16
|
+
"""Manage error records in MoaT-KV."""
|
17
|
+
obj.err = await ErrorRoot.as_handler(obj.client)
|
18
|
+
|
19
|
+
|
20
|
+
@cli.command()
|
21
|
+
@click.option("-s", "--subsys", help="Subsystem to access")
|
22
|
+
@click.argument("path", nargs=1)
|
23
|
+
@click.pass_obj
|
24
|
+
async def resolve(obj, path, subsys):
|
25
|
+
"""
|
26
|
+
Mark an error as resolved.
|
27
|
+
"""
|
28
|
+
path = P(path)
|
29
|
+
if subsys:
|
30
|
+
e = obj.err.get_error_record(subsys, path)
|
31
|
+
else:
|
32
|
+
e = obj.err.follow(path)
|
33
|
+
if e.resolved:
|
34
|
+
print("Already resolved.", file=sys.stderr)
|
35
|
+
return
|
36
|
+
await e.resolve()
|
37
|
+
|
38
|
+
|
39
|
+
@cli.command()
|
40
|
+
@click.option("-n", "--node", help="add details from this node")
|
41
|
+
@click.option("-s", "--subsystem", "subsys", help="only show errors from this subsystem")
|
42
|
+
@click.option("-r", "--resolved", is_flag=True, help="only resolved errors")
|
43
|
+
@click.option("-v", "--verbose", count=True, help="add per-node details (-vv for traces)")
|
44
|
+
@click.option("-a", "--all-errors", is_flag=True, help="add details from all nodes")
|
45
|
+
@click.option("-d", "--as-dict", default=None, help="Dump a list of all open (or resolved) error.")
|
46
|
+
@click.option("-p", "--path", default=":", help="only show errors below this subpath")
|
47
|
+
@click.pass_obj
|
48
|
+
async def dump(obj, as_dict, path, node, all_errors, verbose, resolved, subsys):
|
49
|
+
"""Dump error entries."""
|
50
|
+
path = P(path)
|
51
|
+
path_ = obj.cfg["errors"].prefix
|
52
|
+
d = 2
|
53
|
+
|
54
|
+
async def one(r):
|
55
|
+
nonlocal y
|
56
|
+
val = r.value
|
57
|
+
if subsys and val.get("subsystem", "") != subsys:
|
58
|
+
return
|
59
|
+
if path and val.get("path", ())[: len(path)] != path:
|
60
|
+
return
|
61
|
+
|
62
|
+
if not all_errors and bool(val.get("resolved", False)) != resolved:
|
63
|
+
return
|
64
|
+
|
65
|
+
add_dates(val)
|
66
|
+
try:
|
67
|
+
rp = val.path
|
68
|
+
if as_dict:
|
69
|
+
del val.path
|
70
|
+
elif not isinstance(rp, Path):
|
71
|
+
rp = Path.build(rp)
|
72
|
+
except AttributeError:
|
73
|
+
rp = Path("incomplete") + r.path
|
74
|
+
if not as_dict:
|
75
|
+
val.path = rp
|
76
|
+
if rp[: len(path)] != path:
|
77
|
+
return
|
78
|
+
rp = rp[len(path) :]
|
79
|
+
if node is None:
|
80
|
+
val.syspath = r.path
|
81
|
+
else:
|
82
|
+
val.syspath = Path(node) + r.path
|
83
|
+
|
84
|
+
rn = {}
|
85
|
+
|
86
|
+
def disp(rr):
|
87
|
+
if node and rr.path[-1] != node:
|
88
|
+
return
|
89
|
+
val = rr.value
|
90
|
+
add_dates(val)
|
91
|
+
if verbose < 2:
|
92
|
+
rr.value.pop("trace", None)
|
93
|
+
rn[rr.path[-1]] = rr if obj.meta else rr.value
|
94
|
+
|
95
|
+
if verbose:
|
96
|
+
rs = await obj.client._request(
|
97
|
+
action="get_tree",
|
98
|
+
min_depth=1,
|
99
|
+
max_depth=1,
|
100
|
+
path=path_ + r.path[-2:],
|
101
|
+
iter=True,
|
102
|
+
nchain=3 if obj.meta else 0,
|
103
|
+
)
|
104
|
+
async for rr in rs:
|
105
|
+
disp(rr)
|
106
|
+
|
107
|
+
elif node:
|
108
|
+
rs = await obj.client.get(path_ + r.path[-2:] | node)
|
109
|
+
if "value" in rs:
|
110
|
+
disp(rs)
|
111
|
+
|
112
|
+
if rn:
|
113
|
+
val["nodes"] = rn
|
114
|
+
|
115
|
+
if as_dict is not None:
|
116
|
+
yy = y
|
117
|
+
if subsys is None:
|
118
|
+
yy = yy.setdefault(val.get("subsystem", "unknown"), {})
|
119
|
+
for p in rp:
|
120
|
+
yy = yy.setdefault(p, {})
|
121
|
+
yy[as_dict] = r if obj.meta else val
|
122
|
+
else:
|
123
|
+
yy = r if obj.meta else r.value
|
124
|
+
|
125
|
+
yprint([yy], stream=obj.stdout)
|
126
|
+
print("---", file=obj.stdout)
|
127
|
+
|
128
|
+
y = {}
|
129
|
+
res = None
|
130
|
+
|
131
|
+
if node is None and len(path) == 2 and isinstance(path[-1], int): # single error?
|
132
|
+
r = await obj.client.get(path_ + path, nchain=3 if obj.meta else 0)
|
133
|
+
# Mangle a few variables so that the output is still OK
|
134
|
+
r.path = path
|
135
|
+
node = None
|
136
|
+
|
137
|
+
async def ait(r):
|
138
|
+
yield r
|
139
|
+
|
140
|
+
res = ait(r)
|
141
|
+
path = ()
|
142
|
+
|
143
|
+
if res is None:
|
144
|
+
res = obj.client.get_tree(path_, min_depth=d, max_depth=d, nchain=3 if obj.meta else 0)
|
145
|
+
async for r in res:
|
146
|
+
await one(r)
|
147
|
+
|
148
|
+
if as_dict is not None:
|
149
|
+
yprint(y, stream=obj.stdout)
|
@@ -0,0 +1,248 @@
|
|
1
|
+
# command line interface
|
2
|
+
from __future__ import annotations
|
3
|
+
|
4
|
+
from collections.abc import Mapping
|
5
|
+
|
6
|
+
import asyncclick as click
|
7
|
+
from moat.util import P, PathLongener, yprint
|
8
|
+
from range_set import RangeSet
|
9
|
+
|
10
|
+
|
11
|
+
@click.group(short_help="Control internal state.") # pylint: disable=undefined-variable
|
12
|
+
async def cli():
|
13
|
+
"""
|
14
|
+
This subcommand queries and controls the server's internal state.
|
15
|
+
"""
|
16
|
+
pass
|
17
|
+
|
18
|
+
|
19
|
+
@cli.command()
|
20
|
+
@click.option("-n", "--nodes", is_flag=True, help="Get node status.")
|
21
|
+
@click.option("-d", "--deleted", is_flag=True, help="Get deletion status.")
|
22
|
+
@click.option("-m", "--missing", is_flag=True, help="Get missing-node status.")
|
23
|
+
@click.option(
|
24
|
+
"-r",
|
25
|
+
"--remote-missing",
|
26
|
+
"remote_missing",
|
27
|
+
is_flag=True,
|
28
|
+
help="Get remote-missing-node status.",
|
29
|
+
)
|
30
|
+
@click.option("-p", "--present", is_flag=True, help="Get known-data status.")
|
31
|
+
@click.option("-s", "--superseded", is_flag=True, help="Get superseded-data status.")
|
32
|
+
@click.option("-D", "--debug", is_flag=True, help="Get internal verbosity.")
|
33
|
+
@click.option("--debugger", is_flag=True, help="Start a remote debugger. DO NOT USE.")
|
34
|
+
@click.option("-k", "--known", hidden=True, is_flag=True, help="Get superseded-data status.")
|
35
|
+
@click.option("-a", "--all", is_flag=True, help="All available data.")
|
36
|
+
@click.pass_obj
|
37
|
+
async def state(obj, **flags):
|
38
|
+
"""
|
39
|
+
Dump the server's state.
|
40
|
+
"""
|
41
|
+
if flags.pop("known", None):
|
42
|
+
flags["superseded"] = True
|
43
|
+
if flags.pop("all", None):
|
44
|
+
flags["superseded"] = True
|
45
|
+
flags["present"] = True
|
46
|
+
flags["nodes"] = True
|
47
|
+
flags["deleted"] = True
|
48
|
+
flags["missing"] = True
|
49
|
+
flags["remote_missing"] = True
|
50
|
+
res = await obj.client._request("get_state", iter=False, **flags)
|
51
|
+
k = res.pop("known", None)
|
52
|
+
if k is not None:
|
53
|
+
res["superseded"] = k
|
54
|
+
yprint(res, stream=obj.stdout)
|
55
|
+
|
56
|
+
|
57
|
+
@cli.command()
|
58
|
+
@click.option("-d", "--deleted", is_flag=True, help="Mark as deleted. Default: superseded")
|
59
|
+
@click.option(
|
60
|
+
"-n",
|
61
|
+
"--node",
|
62
|
+
"source",
|
63
|
+
default="?",
|
64
|
+
help="The node this message is faked as being from.",
|
65
|
+
)
|
66
|
+
@click.option("-b", "--broadcast", is_flag=True, help="Send to all servers")
|
67
|
+
@click.argument("node", nargs=1)
|
68
|
+
@click.argument("items", type=int, nargs=-1)
|
69
|
+
@click.pass_obj
|
70
|
+
async def mark(obj, deleted, source, node, items, broadcast):
|
71
|
+
"""
|
72
|
+
Fix internal state. Use no items to fetch the current list from the
|
73
|
+
server's ``missing`` state. Use an empty node name to add the whole
|
74
|
+
list, not just a single node's.
|
75
|
+
|
76
|
+
This is a dangerous command.
|
77
|
+
"""
|
78
|
+
|
79
|
+
k = "deleted" if deleted else "superseded"
|
80
|
+
if not items:
|
81
|
+
r = await obj.client._request("get_state", iter=False, missing=True)
|
82
|
+
r = r["missing"]
|
83
|
+
if node != "":
|
84
|
+
r = {node: r[node]}
|
85
|
+
elif node == "":
|
86
|
+
raise click.UsageError("You can't do that with an empty node")
|
87
|
+
else:
|
88
|
+
r = RangeSet()
|
89
|
+
for i in items:
|
90
|
+
r.add(i)
|
91
|
+
r = {node: r.__getstate__()}
|
92
|
+
|
93
|
+
msg = {k: r, "node": source}
|
94
|
+
|
95
|
+
await obj.client._request("fake_info", iter=False, **msg)
|
96
|
+
if broadcast:
|
97
|
+
await obj.client._request("fake_info_send", iter=False, **msg)
|
98
|
+
|
99
|
+
res = await obj.client._request("get_state", iter=False, **{k: True})
|
100
|
+
yprint(res, stream=obj.stdout)
|
101
|
+
|
102
|
+
|
103
|
+
@cli.command(short_help="Manage the Deleter list")
|
104
|
+
@click.option("-d", "--delete", is_flag=True, help="Remove these nodes")
|
105
|
+
@click.argument("nodes", nargs=-1)
|
106
|
+
@click.pass_obj
|
107
|
+
async def deleter(obj, delete, nodes):
|
108
|
+
"""
|
109
|
+
Manage the Deleter list
|
110
|
+
|
111
|
+
This is the set of nodes that must be online for removal of deleted
|
112
|
+
entries from MoaT-KV's data.
|
113
|
+
|
114
|
+
There should be one such node in every possible network partition.
|
115
|
+
Also, all nodes with permanent storage should be on the list.
|
116
|
+
|
117
|
+
Usage:
|
118
|
+
- … deleter -- list state
|
119
|
+
- … deleter NODE… -- add this node
|
120
|
+
- … deleter -d NODE… -- remove this node
|
121
|
+
"""
|
122
|
+
|
123
|
+
res = await obj.client._request(
|
124
|
+
action="get_internal",
|
125
|
+
path=("actor", "del"),
|
126
|
+
iter=False,
|
127
|
+
nchain=3 if delete or nodes else 2,
|
128
|
+
)
|
129
|
+
val = res.get("value", {})
|
130
|
+
if isinstance(val, Mapping):
|
131
|
+
val = val.get("nodes", [])
|
132
|
+
# else: compatibility, TODO remove
|
133
|
+
val = set(val)
|
134
|
+
if delete:
|
135
|
+
if nodes:
|
136
|
+
val -= set(nodes)
|
137
|
+
else:
|
138
|
+
val = set()
|
139
|
+
elif nodes:
|
140
|
+
val |= set(nodes)
|
141
|
+
else:
|
142
|
+
yprint(res, stream=obj.stdout)
|
143
|
+
return
|
144
|
+
|
145
|
+
val = {"nodes": list(val)}
|
146
|
+
res = await obj.client._request(
|
147
|
+
action="set_internal",
|
148
|
+
path=("actor", "del"),
|
149
|
+
iter=False,
|
150
|
+
chain=res.chain,
|
151
|
+
value=val,
|
152
|
+
)
|
153
|
+
res.value = val
|
154
|
+
yprint(res, stream=obj.stdout)
|
155
|
+
|
156
|
+
|
157
|
+
@cli.command()
|
158
|
+
@click.argument("path", nargs=1)
|
159
|
+
@click.pass_obj
|
160
|
+
async def dump(obj, path):
|
161
|
+
"""
|
162
|
+
Dump internal state.
|
163
|
+
|
164
|
+
This displays MoaT-KV's internal state.
|
165
|
+
"""
|
166
|
+
|
167
|
+
path = P(path)
|
168
|
+
y = {}
|
169
|
+
pl = PathLongener()
|
170
|
+
async for r in await obj.client._request("get_tree_internal", path=path, iter=True, nchain=0):
|
171
|
+
pl(r)
|
172
|
+
path = r["path"]
|
173
|
+
yy = y
|
174
|
+
for p in path:
|
175
|
+
yy = yy.setdefault(p, {})
|
176
|
+
try:
|
177
|
+
yy["_"] = r["value"]
|
178
|
+
except KeyError:
|
179
|
+
pass
|
180
|
+
yprint(y, stream=obj.stdout)
|
181
|
+
|
182
|
+
|
183
|
+
@cli.command()
|
184
|
+
@click.argument("node", nargs=1)
|
185
|
+
@click.argument("tick", type=int, nargs=1)
|
186
|
+
@click.pass_obj
|
187
|
+
async def get(obj, node, tick):
|
188
|
+
"""
|
189
|
+
Fetch data by node+tick.
|
190
|
+
|
191
|
+
This looks up internal data.
|
192
|
+
"""
|
193
|
+
|
194
|
+
res = await obj.client._request("get_value", node=node, tick=tick, nchain=99)
|
195
|
+
if not obj.meta:
|
196
|
+
res = res.value
|
197
|
+
yprint(res, stream=obj.stdout)
|
198
|
+
|
199
|
+
|
200
|
+
@cli.command()
|
201
|
+
@click.option("-n", "--num", type=int, help="Return at most this many IDs")
|
202
|
+
@click.option("-c", "--current", is_flag=True, help="Return only IDs with current data")
|
203
|
+
@click.option("-C", "--copy", is_flag=True, help="Create an no-op change")
|
204
|
+
@click.argument("node", nargs=1)
|
205
|
+
@click.pass_obj
|
206
|
+
async def enum(obj, node, num, current, copy):
|
207
|
+
"""
|
208
|
+
List IDs of live data by a specific node.
|
209
|
+
|
210
|
+
Can be used to determine whether a node still has live data,
|
211
|
+
otherwise it can be deleted.
|
212
|
+
|
213
|
+
If '--current' is set, only the IDs of the entries that have last been
|
214
|
+
updated by that node are shown. '--copy' rewrites these entries.
|
215
|
+
|
216
|
+
Increase verbosity to also show the oject paths.
|
217
|
+
"""
|
218
|
+
|
219
|
+
res = await obj.client._request("enum_node", node=node, max=num, current=current)
|
220
|
+
if obj.meta and not copy and obj.debug <= 1:
|
221
|
+
yprint(res, stream=obj.stdout)
|
222
|
+
else:
|
223
|
+
for k in res.result:
|
224
|
+
if copy or obj.debug > 1:
|
225
|
+
res = await obj.client._request("get_value", node=node, tick=k, nchain=3)
|
226
|
+
if obj.debug > 1:
|
227
|
+
print(k, res.path)
|
228
|
+
else:
|
229
|
+
print(k)
|
230
|
+
if copy and res.chain.node == node:
|
231
|
+
res = await obj.client.set(res.path, value=res.value, chain=res.chain)
|
232
|
+
else:
|
233
|
+
print(k)
|
234
|
+
|
235
|
+
|
236
|
+
@cli.command()
|
237
|
+
@click.argument("node", nargs=1)
|
238
|
+
@click.pass_obj
|
239
|
+
async def kill(obj, node):
|
240
|
+
"""
|
241
|
+
Remove a node from the node list.
|
242
|
+
|
243
|
+
This command only works if this node does not have any current data in
|
244
|
+
the system.
|
245
|
+
"""
|
246
|
+
res = await obj.client._request("kill_node", node=node)
|
247
|
+
if obj.meta:
|
248
|
+
yprint(res, stream=obj.stdout)
|