synapse 2.223.0__py311-none-any.whl → 2.225.0__py311-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.
Potentially problematic release.
This version of synapse might be problematic. Click here for more details.
- synapse/axon.py +10 -5
- synapse/common.py +2 -2
- synapse/cortex.py +52 -3
- synapse/datamodel.py +1 -1
- synapse/lib/cell.py +1 -1
- synapse/lib/const.py +4 -0
- synapse/lib/layer.py +6 -6
- synapse/lib/multislabseqn.py +36 -1
- synapse/lib/nexus.py +67 -8
- synapse/lib/queue.py +4 -1
- synapse/lib/rstorm.py +2 -2
- synapse/lib/schemas.py +13 -1
- synapse/lib/slabseqn.py +28 -0
- synapse/lib/storm.py +40 -2
- synapse/lib/stormhttp.py +7 -1
- synapse/lib/stormlib/imap.py +12 -4
- synapse/lib/stormlib/task.py +0 -1
- synapse/lib/stormtypes.py +19 -1
- synapse/lib/version.py +2 -2
- synapse/models/inet.py +29 -0
- synapse/models/media.py +4 -0
- synapse/models/proj.py +3 -0
- synapse/models/risk.py +9 -0
- synapse/models/syn.py +8 -0
- synapse/tests/test_common.py +4 -0
- synapse/tests/test_cortex.py +53 -2
- synapse/tests/test_lib_agenda.py +1 -1
- synapse/tests/test_lib_cell.py +1 -1
- synapse/tests/test_lib_certdir.py +1 -1
- synapse/tests/test_lib_httpapi.py +1 -1
- synapse/tests/test_lib_layer.py +1 -1
- synapse/tests/test_lib_multislabseqn.py +22 -0
- synapse/tests/test_lib_nexus.py +42 -1
- synapse/tests/test_lib_slabseqn.py +30 -1
- synapse/tests/test_lib_storm.py +156 -1
- synapse/tests/test_lib_stormhttp.py +16 -0
- synapse/tests/test_lib_stormlib_imap.py +14 -0
- synapse/tests/test_lib_stormlib_oauth.py +1 -1
- synapse/tests/test_lib_stormsvc.py +1 -1
- synapse/tests/test_lib_stormtypes.py +12 -0
- synapse/tests/test_lib_trigger.py +1 -1
- synapse/tests/test_model_inet.py +29 -0
- synapse/tests/test_model_media.py +4 -1
- synapse/tests/test_model_proj.py +3 -1
- synapse/tests/test_model_risk.py +12 -0
- synapse/tests/test_model_syn.py +54 -2
- synapse/tests/{test_tools_axon2axon.py → test_tools_axon_copy.py} +4 -4
- synapse/tests/{test_tools_pullfile.py → test_tools_axon_get.py} +4 -4
- synapse/tests/{test_tools_pushfile.py → test_tools_axon_put.py} +7 -7
- synapse/tests/{test_tools_csvtool.py → test_tools_cortex_csv.py} +12 -3
- synapse/tests/{test_tools_feed.py → test_tools_cortex_feed.py} +2 -2
- synapse/tests/{test_tools_apikey.py → test_tools_service_apikey.py} +1 -4
- synapse/tests/{test_tools_backup.py → test_tools_service_backup.py} +5 -5
- synapse/tests/{test_tools_demote.py → test_tools_service_demote.py} +1 -1
- synapse/tests/{test_tools_healthcheck.py → test_tools_service_healthcheck.py} +1 -1
- synapse/tests/{test_tools_livebackup.py → test_tools_service_livebackup.py} +1 -1
- synapse/tests/{test_tools_modrole.py → test_tools_service_modrole.py} +1 -1
- synapse/tests/{test_tools_moduser.py → test_tools_service_moduser.py} +1 -1
- synapse/tests/{test_tools_promote.py → test_tools_service_promote.py} +1 -1
- synapse/tests/{test_tools_reload.py → test_tools_service_reload.py} +1 -1
- synapse/tests/{test_tools_shutdown.py → test_tools_service_shutdown.py} +1 -1
- synapse/tests/{test_tools_snapshot.py → test_tools_service_snapshot.py} +1 -1
- synapse/tests/{test_tools_storm.py → test_tools_storm_cli.py} +1 -1
- synapse/tests/{test_tools_pkgs_gendocs.py → test_tools_storm_pkg_doc.py} +12 -3
- synapse/tests/{test_tools_genpkg.py → test_tools_storm_pkg_gen.py} +1 -1
- synapse/tests/{test_tools_autodoc.py → test_tools_utils_autodoc.py} +1 -1
- synapse/tests/test_tools_utils_changelog.py +454 -0
- synapse/tests/{test_tools_easycert.py → test_tools_utils_easycert.py} +48 -46
- synapse/tests/{test_tools_guid.py → test_tools_utils_guid.py} +3 -3
- synapse/tests/{test_tools_json2mpk.py → test_tools_utils_json2mpk.py} +3 -3
- synapse/tests/{test_tools_rstorm.py → test_tools_utils_rstorm.py} +6 -1
- synapse/tests/utils.py +3 -1
- synapse/tools/apikey.py +4 -83
- synapse/tools/autodoc.py +3 -1031
- synapse/tools/axon/copy.py +44 -0
- synapse/tools/axon/get.py +64 -0
- synapse/tools/axon/put.py +122 -0
- synapse/tools/axon2axon.py +3 -36
- synapse/tools/backup.py +6 -176
- synapse/tools/changelog.py +3 -1098
- synapse/tools/cortex/csv.py +236 -0
- synapse/tools/cortex/feed.py +151 -0
- synapse/tools/csvtool.py +3 -227
- synapse/tools/demote.py +4 -40
- synapse/tools/docker/validate.py +3 -3
- synapse/tools/easycert.py +4 -129
- synapse/tools/feed.py +3 -140
- synapse/tools/genpkg.py +3 -307
- synapse/tools/guid.py +7 -6
- synapse/tools/healthcheck.py +3 -101
- synapse/tools/json2mpk.py +6 -38
- synapse/tools/livebackup.py +4 -27
- synapse/tools/modrole.py +3 -108
- synapse/tools/moduser.py +3 -179
- synapse/tools/pkgs/gendocs.py +3 -164
- synapse/tools/promote.py +4 -41
- synapse/tools/pullfile.py +3 -56
- synapse/tools/pushfile.py +3 -114
- synapse/tools/reload.py +4 -61
- synapse/tools/rstorm.py +3 -26
- synapse/tools/service/__init__.py +0 -0
- synapse/tools/service/apikey.py +90 -0
- synapse/tools/service/backup.py +181 -0
- synapse/tools/service/demote.py +47 -0
- synapse/tools/service/healthcheck.py +109 -0
- synapse/tools/service/livebackup.py +34 -0
- synapse/tools/service/modrole.py +116 -0
- synapse/tools/service/moduser.py +184 -0
- synapse/tools/service/promote.py +48 -0
- synapse/tools/service/reload.py +68 -0
- synapse/tools/service/shutdown.py +51 -0
- synapse/tools/service/snapshot.py +64 -0
- synapse/tools/shutdown.py +5 -45
- synapse/tools/snapshot.py +4 -57
- synapse/tools/storm/__init__.py +0 -0
- synapse/tools/storm/__main__.py +5 -0
- synapse/tools/{storm.py → storm/_cli.py} +0 -3
- synapse/tools/storm/pkg/__init__.py +0 -0
- synapse/tools/{pkgs/pandoc_filter.py → storm/pkg/_pandoc_filter.py} +1 -1
- synapse/tools/storm/pkg/doc.py +176 -0
- synapse/tools/storm/pkg/gen.py +315 -0
- synapse/tools/utils/__init__.py +0 -0
- synapse/tools/utils/autodoc.py +1040 -0
- synapse/tools/utils/changelog.py +1124 -0
- synapse/tools/utils/easycert.py +136 -0
- synapse/tools/utils/guid.py +11 -0
- synapse/tools/utils/json2mpk.py +46 -0
- synapse/tools/utils/rstorm.py +35 -0
- {synapse-2.223.0.dist-info → synapse-2.225.0.dist-info}/METADATA +1 -1
- {synapse-2.223.0.dist-info → synapse-2.225.0.dist-info}/RECORD +134 -105
- synapse/tests/test_tools_changelog.py +0 -196
- /synapse/tests/{test_tools_axon.py → test_tools_axon_dump_load.py} +0 -0
- {synapse-2.223.0.dist-info → synapse-2.225.0.dist-info}/WHEEL +0 -0
- {synapse-2.223.0.dist-info → synapse-2.225.0.dist-info}/licenses/LICENSE +0 -0
- {synapse-2.223.0.dist-info → synapse-2.225.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import synapse.common as s_common
|
|
4
|
+
import synapse.telepath as s_telepath
|
|
5
|
+
|
|
6
|
+
import synapse.exc as s_exc
|
|
7
|
+
import synapse.lib.cmd as s_cmd
|
|
8
|
+
import synapse.lib.base as s_base
|
|
9
|
+
import synapse.lib.output as s_output
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
async def main(argv, outp=s_output.stdout):
|
|
14
|
+
|
|
15
|
+
pars = s_cmd.Parser(prog='synapse.tools.axon.copy', outp=outp)
|
|
16
|
+
pars.add_argument('--offset', type=int, default=0, help='An offset within the source axon to start from.')
|
|
17
|
+
pars.add_argument('src_axon', help='The telepath URL of the source axon.')
|
|
18
|
+
pars.add_argument('dst_axon', help='The telepath URL of the destination axon.')
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
opts = pars.parse_args(argv)
|
|
22
|
+
except s_exc.ParserExit:
|
|
23
|
+
return 1
|
|
24
|
+
|
|
25
|
+
async with s_telepath.withTeleEnv():
|
|
26
|
+
async with await s_base.Base.anit() as base:
|
|
27
|
+
|
|
28
|
+
srcaxon = await base.enter_context(await s_telepath.openurl(opts.src_axon))
|
|
29
|
+
dstaxon = await base.enter_context(await s_telepath.openurl(opts.dst_axon))
|
|
30
|
+
|
|
31
|
+
outp.printf(f'Starting transfer at offset: {opts.offset}')
|
|
32
|
+
|
|
33
|
+
async for (offs, (sha256, size)) in srcaxon.hashes(opts.offset):
|
|
34
|
+
offstext = str(offs).rjust(10)
|
|
35
|
+
sha2text = s_common.ehex(sha256)
|
|
36
|
+
outp.printf(f'[{offstext}] - {sha2text} ({size})')
|
|
37
|
+
async with await dstaxon.upload() as fd:
|
|
38
|
+
async for byts in srcaxon.get(sha256):
|
|
39
|
+
await fd.write(byts)
|
|
40
|
+
await fd.save()
|
|
41
|
+
return 0
|
|
42
|
+
|
|
43
|
+
if __name__ == '__main__': # pragma: no cover
|
|
44
|
+
s_cmd.exitmain(main)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import pathlib
|
|
2
|
+
|
|
3
|
+
import synapse.common as s_common
|
|
4
|
+
import synapse.telepath as s_telepath
|
|
5
|
+
|
|
6
|
+
import synapse.lib.cmd as s_cmd
|
|
7
|
+
import synapse.lib.output as s_output
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def main(argv, outp=s_output.stdout):
|
|
11
|
+
pars = getArgParser(outp)
|
|
12
|
+
opts = pars.parse_args(argv)
|
|
13
|
+
|
|
14
|
+
if opts.output is None:
|
|
15
|
+
opts.output = '.'
|
|
16
|
+
|
|
17
|
+
outdir = pathlib.Path(opts.output)
|
|
18
|
+
|
|
19
|
+
s_common.gendir(opts.output)
|
|
20
|
+
|
|
21
|
+
async with s_telepath.withTeleEnv():
|
|
22
|
+
|
|
23
|
+
async with await s_telepath.openurl(opts.axon) as axon:
|
|
24
|
+
|
|
25
|
+
# reminder: these are the hashes *not* available
|
|
26
|
+
|
|
27
|
+
awants = await axon.wants([s_common.uhex(h) for h in opts.hashes])
|
|
28
|
+
for a in awants:
|
|
29
|
+
outp.printf(f'{s_common.ehex(a)} not in axon store')
|
|
30
|
+
|
|
31
|
+
exists = [h for h in opts.hashes if s_common.uhex(h) not in awants]
|
|
32
|
+
|
|
33
|
+
for h in exists:
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
outp.printf(f'Fetching {h} to file')
|
|
37
|
+
|
|
38
|
+
with open(outdir.joinpath(h), 'wb') as fd:
|
|
39
|
+
async for b in axon.get(s_common.uhex(h)):
|
|
40
|
+
fd.write(b)
|
|
41
|
+
|
|
42
|
+
outp.printf(f'Fetched {h} to file')
|
|
43
|
+
|
|
44
|
+
except Exception as e:
|
|
45
|
+
outp.printf('Error: Hit Exception: %s' % (str(e),))
|
|
46
|
+
continue
|
|
47
|
+
|
|
48
|
+
return 0
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def getArgParser(outp):
|
|
52
|
+
desc = 'Fetches file from the given axon'
|
|
53
|
+
pars = s_cmd.Parser(prog='synapse.tools.axon.get', outp=outp, description=desc)
|
|
54
|
+
pars.add_argument('-a', '--axon', type=str, dest='axon', required=True,
|
|
55
|
+
help='URL to the axon blob store')
|
|
56
|
+
pars.add_argument('-o', '--output', type=str, dest='output',
|
|
57
|
+
help='Directory to output files to')
|
|
58
|
+
pars.add_argument('-l', '--list-hashes', dest='hashes', action='append', default=[],
|
|
59
|
+
help='List of hashes to pull from axon')
|
|
60
|
+
|
|
61
|
+
return pars
|
|
62
|
+
|
|
63
|
+
if __name__ == '__main__': # pragma: no cover
|
|
64
|
+
s_cmd.exitmain(main)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import glob
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
import synapse.exc as s_exc
|
|
6
|
+
import synapse.common as s_common
|
|
7
|
+
import synapse.telepath as s_telepath
|
|
8
|
+
|
|
9
|
+
import synapse.lib.cmd as s_cmd
|
|
10
|
+
import synapse.lib.output as s_output
|
|
11
|
+
import synapse.lib.hashset as s_hashset
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def main(argv, outp=s_output.stdout):
|
|
17
|
+
pars = getArgParser(outp)
|
|
18
|
+
opts = pars.parse_args(argv)
|
|
19
|
+
|
|
20
|
+
async with s_telepath.withTeleEnv():
|
|
21
|
+
|
|
22
|
+
axon = await s_telepath.openurl(opts.axon)
|
|
23
|
+
|
|
24
|
+
core = None
|
|
25
|
+
if opts.cortex:
|
|
26
|
+
core = await s_telepath.openurl(opts.cortex)
|
|
27
|
+
|
|
28
|
+
tags = set()
|
|
29
|
+
if opts.tags:
|
|
30
|
+
for tag in opts.tags.split(','):
|
|
31
|
+
tags.add(tag)
|
|
32
|
+
|
|
33
|
+
tags = tuple(tags)
|
|
34
|
+
if tags:
|
|
35
|
+
outp.printf(f'adding tags: {tags}')
|
|
36
|
+
|
|
37
|
+
filepaths = set()
|
|
38
|
+
for item in opts.filenames:
|
|
39
|
+
paths = glob.glob(item, recursive=opts.recursive)
|
|
40
|
+
|
|
41
|
+
if not paths:
|
|
42
|
+
outp.printf(f'filepath does not contain any files: {item}')
|
|
43
|
+
continue
|
|
44
|
+
|
|
45
|
+
filepaths.update([path for path in paths if os.path.isfile(path)])
|
|
46
|
+
|
|
47
|
+
for path in filepaths:
|
|
48
|
+
|
|
49
|
+
bname = os.path.basename(path)
|
|
50
|
+
|
|
51
|
+
hset = s_hashset.HashSet()
|
|
52
|
+
with s_common.reqfile(path) as fd:
|
|
53
|
+
hset.eatfd(fd)
|
|
54
|
+
|
|
55
|
+
fhashes = {htyp: hasher.hexdigest() for htyp, hasher in hset.hashes}
|
|
56
|
+
|
|
57
|
+
sha256 = fhashes.get('sha256')
|
|
58
|
+
bsha256 = s_common.uhex(sha256)
|
|
59
|
+
|
|
60
|
+
if not await axon.has(bsha256):
|
|
61
|
+
|
|
62
|
+
async with await axon.upload() as upfd:
|
|
63
|
+
|
|
64
|
+
with s_common.genfile(path) as fd:
|
|
65
|
+
for byts in s_common.iterfd(fd):
|
|
66
|
+
await upfd.write(byts)
|
|
67
|
+
|
|
68
|
+
size, hashval = await upfd.save()
|
|
69
|
+
|
|
70
|
+
if hashval != bsha256: # pragma: no cover
|
|
71
|
+
raise s_exc.SynErr(mesg='hashes do not match',
|
|
72
|
+
ehash=s_common.ehex(hashval),
|
|
73
|
+
ahash=hashval)
|
|
74
|
+
|
|
75
|
+
outp.printf(f'Uploaded [{bname}] to axon')
|
|
76
|
+
else:
|
|
77
|
+
outp.printf(f'Axon already had [{bname}]')
|
|
78
|
+
|
|
79
|
+
if core:
|
|
80
|
+
opts = {'vars': {
|
|
81
|
+
'md5': fhashes.get('md5'),
|
|
82
|
+
'sha1': fhashes.get('sha1'),
|
|
83
|
+
'sha256': fhashes.get('sha256'),
|
|
84
|
+
'size': hset.size,
|
|
85
|
+
'name': bname,
|
|
86
|
+
'tags': tags,
|
|
87
|
+
}}
|
|
88
|
+
|
|
89
|
+
q = '[file:bytes=$sha256 :md5=$md5 :sha1=$sha1 :size=$size :name=$name] ' \
|
|
90
|
+
'{ for $tag in $tags { [+#$tag] } }'
|
|
91
|
+
|
|
92
|
+
msgs = await core.storm(q, opts=opts).list()
|
|
93
|
+
node = [m[1] for m in msgs if m[0] == 'node'][0]
|
|
94
|
+
|
|
95
|
+
iden = node[0][1]
|
|
96
|
+
size = node[1]['props']['size']
|
|
97
|
+
name = node[1]['props']['name']
|
|
98
|
+
mesg = f'file: {bname} ({size}) added to core ({iden}) as {name}'
|
|
99
|
+
outp.printf(mesg)
|
|
100
|
+
|
|
101
|
+
await axon.fini()
|
|
102
|
+
if core:
|
|
103
|
+
await core.fini()
|
|
104
|
+
|
|
105
|
+
return 0
|
|
106
|
+
|
|
107
|
+
def getArgParser(outp):
|
|
108
|
+
desc = 'Command line tool for uploading files to an Axon and making ' \
|
|
109
|
+
'file:bytes in a Cortex.'
|
|
110
|
+
pars = s_cmd.Parser(prog='synapse.tools.axon.put', outp=outp, description=desc)
|
|
111
|
+
pars.add_argument('-a', '--axon', required=True, type=str, dest='axon',
|
|
112
|
+
help='URL for a target Axon to store files at.')
|
|
113
|
+
pars.add_argument('-c', '--cortex', default=None, type=str, dest='cortex',
|
|
114
|
+
help='URL for a target Cortex to make file:bytes nodes.')
|
|
115
|
+
pars.add_argument('filenames', nargs='+', help='File names (or glob patterns) to upload')
|
|
116
|
+
pars.add_argument('-r', '--recursive', action='store_true',
|
|
117
|
+
help='Recursively search paths to upload files.')
|
|
118
|
+
pars.add_argument('-t', '--tags', help='comma separated list of tags to add to the nodes')
|
|
119
|
+
return pars
|
|
120
|
+
|
|
121
|
+
if __name__ == '__main__': # pragma: no cover
|
|
122
|
+
s_cmd.exitmain(main)
|
synapse/tools/axon2axon.py
CHANGED
|
@@ -1,44 +1,11 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
|
|
3
1
|
import synapse.common as s_common
|
|
4
|
-
import synapse.telepath as s_telepath
|
|
5
2
|
|
|
6
|
-
import synapse.exc as s_exc
|
|
7
3
|
import synapse.lib.cmd as s_cmd
|
|
8
|
-
import synapse.lib.base as s_base
|
|
9
|
-
import synapse.lib.output as s_output
|
|
10
|
-
|
|
11
|
-
logger = logging.getLogger(__name__)
|
|
12
|
-
|
|
13
|
-
async def main(argv, outp=s_output.stdout):
|
|
14
|
-
|
|
15
|
-
pars = s_cmd.Parser(prog='synapse.tools.axon2axon', outp=outp)
|
|
16
|
-
pars.add_argument('--offset', type=int, default=0, help='An offset within the source axon to start from.')
|
|
17
|
-
pars.add_argument('src_axon', help='The telepath URL of the source axon.')
|
|
18
|
-
pars.add_argument('dst_axon', help='The telepath URL of the destination axon.')
|
|
19
|
-
|
|
20
|
-
try:
|
|
21
|
-
opts = pars.parse_args(argv)
|
|
22
|
-
except s_exc.ParserExit:
|
|
23
|
-
return 1
|
|
24
|
-
|
|
25
|
-
async with s_telepath.withTeleEnv():
|
|
26
|
-
async with await s_base.Base.anit() as base:
|
|
27
|
-
|
|
28
|
-
srcaxon = await base.enter_context(await s_telepath.openurl(opts.src_axon))
|
|
29
|
-
dstaxon = await base.enter_context(await s_telepath.openurl(opts.dst_axon))
|
|
30
4
|
|
|
31
|
-
|
|
5
|
+
from synapse.tools.axon.copy import main
|
|
32
6
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
sha2text = s_common.ehex(sha256)
|
|
36
|
-
outp.printf(f'[{offstext}] - {sha2text} ({size})')
|
|
37
|
-
async with await dstaxon.upload() as fd:
|
|
38
|
-
async for byts in srcaxon.get(sha256):
|
|
39
|
-
await fd.write(byts)
|
|
40
|
-
await fd.save()
|
|
41
|
-
return 0
|
|
7
|
+
s_common.deprecated('synapse.tools.axon2axon is deprecated. Please use synapse.tools.axon.copy instead.',
|
|
8
|
+
curv='v2.225.0')
|
|
42
9
|
|
|
43
10
|
if __name__ == '__main__': # pragma: no cover
|
|
44
11
|
s_cmd.exitmain(main)
|
synapse/tools/backup.py
CHANGED
|
@@ -1,182 +1,12 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import sys
|
|
3
|
-
import glob
|
|
4
|
-
import time
|
|
5
|
-
import shutil
|
|
6
|
-
import fnmatch
|
|
7
|
-
import logging
|
|
8
|
-
import argparse
|
|
9
|
-
import contextlib
|
|
10
|
-
|
|
11
|
-
import lmdb
|
|
12
|
-
|
|
13
1
|
import synapse.common as s_common
|
|
14
2
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def backup(srcdir, dstdir, skipdirs=None):
|
|
18
|
-
'''
|
|
19
|
-
Create a backup of a Synapse application.
|
|
20
|
-
|
|
21
|
-
Args:
|
|
22
|
-
srcdir (str): Path to the directory to backup.
|
|
23
|
-
dstdir (str): Path to backup target directory.
|
|
24
|
-
skipdirs (list or None): Optional list of relative directory name glob patterns to exclude from the backup.
|
|
25
|
-
|
|
26
|
-
Note:
|
|
27
|
-
Running this method from the same process as a running user of the directory may lead to a segmentation fault
|
|
28
|
-
'''
|
|
29
|
-
with capturelmdbs(srcdir, skipdirs=skipdirs) as lmdbinfo:
|
|
30
|
-
txnbackup(lmdbinfo, srcdir, dstdir, skipdirs=skipdirs)
|
|
31
|
-
|
|
32
|
-
@contextlib.contextmanager
|
|
33
|
-
def capturelmdbs(srcdir, skipdirs=None, onlydirs=None):
|
|
34
|
-
'''
|
|
35
|
-
A context manager that opens all the lmdb files under a srcdir and makes a read transaction. All transactions are
|
|
36
|
-
aborted and environments closed when the context is exited.
|
|
37
|
-
|
|
38
|
-
Yields:
|
|
39
|
-
Dict[str, Tuple[lmdb.Environment, lmdb.Transaction]]: Maps path to environment, transaction
|
|
40
|
-
'''
|
|
41
|
-
if onlydirs:
|
|
42
|
-
lmdbpaths = onlydirs
|
|
43
|
-
|
|
44
|
-
else:
|
|
45
|
-
if skipdirs is None:
|
|
46
|
-
skipdirs = []
|
|
47
|
-
|
|
48
|
-
srcdir = glob.escape(os.path.abspath(srcdir))
|
|
49
|
-
skipdirs.append(os.path.join(srcdir, 'tmp/*'))
|
|
50
|
-
skipdirs.append(os.path.join(srcdir, '*/tmp/*'))
|
|
51
|
-
|
|
52
|
-
srcdirglob = s_common.genpath(srcdir, '**/data.mdb')
|
|
53
|
-
fniter = glob.iglob(srcdirglob, recursive=True)
|
|
54
|
-
lmdbpaths = [os.path.dirname(fn) for fn in fniter if not
|
|
55
|
-
any([fnmatch.fnmatch(fn, pattern) for pattern in skipdirs])]
|
|
56
|
-
|
|
57
|
-
lmdbinfo = {}
|
|
58
|
-
|
|
59
|
-
with contextlib.ExitStack() as stack:
|
|
60
|
-
for path in lmdbpaths:
|
|
61
|
-
logger.debug(f'Capturing txn for {path}')
|
|
62
|
-
datafile = os.path.join(path, 'data.mdb')
|
|
63
|
-
stat = os.stat(datafile)
|
|
64
|
-
map_size = stat.st_size
|
|
65
|
-
env = stack.enter_context(
|
|
66
|
-
lmdb.open(path, map_size=map_size, max_dbs=16384, create=False, readonly=True))
|
|
67
|
-
txn = stack.enter_context(env.begin())
|
|
68
|
-
assert path not in lmdbinfo
|
|
69
|
-
lmdbinfo[path] = (env, txn)
|
|
70
|
-
|
|
71
|
-
yield lmdbinfo
|
|
72
|
-
|
|
73
|
-
def txnbackup(lmdbinfo, srcdir, dstdir, skipdirs=None):
|
|
74
|
-
'''
|
|
75
|
-
Create a backup of a Synapse application under a (hopefully consistent) set of transactions.
|
|
76
|
-
|
|
77
|
-
Args:
|
|
78
|
-
lmdbinfo(Dict[str, Tuple[lmdb.Environment, lmdb.Transaction]]): Maps of path to environment, transaction
|
|
79
|
-
srcdir (str): Path to the directory to backup.
|
|
80
|
-
dstdir (str): Path to backup target directory.
|
|
81
|
-
skipdirs (list or None): Optional list of relative directory name glob patterns to exclude from the backup.
|
|
82
|
-
|
|
83
|
-
Note:
|
|
84
|
-
Running this method from the same process as a running user of the directory may lead to a segmentation fault
|
|
85
|
-
'''
|
|
86
|
-
tick = s_common.now()
|
|
87
|
-
|
|
88
|
-
srcdir = s_common.reqdir(srcdir)
|
|
89
|
-
dstdir = s_common.gendir(dstdir)
|
|
90
|
-
|
|
91
|
-
if skipdirs is None:
|
|
92
|
-
skipdirs = []
|
|
93
|
-
|
|
94
|
-
# Always avoid backing up temporary and backup directories
|
|
95
|
-
skipdirs.append('**/tmp')
|
|
96
|
-
skipdirs.append('**/backups')
|
|
97
|
-
|
|
98
|
-
logger.debug(f'Starting backup of [{srcdir}]')
|
|
99
|
-
logger.debug(f'Destination dir: [{dstdir}]')
|
|
100
|
-
|
|
101
|
-
for root, dnames, fnames in os.walk(srcdir, topdown=True):
|
|
102
|
-
|
|
103
|
-
relpath = os.path.relpath(root, start=srcdir)
|
|
3
|
+
import synapse.lib.cmd as s_cmd
|
|
104
4
|
|
|
105
|
-
|
|
5
|
+
from synapse.tools.service.backup import logger, main, backup
|
|
106
6
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
relname = os.path.join(relpath, name)
|
|
110
|
-
|
|
111
|
-
if any([fnmatch.fnmatch(relname, pattern) for pattern in skipdirs]):
|
|
112
|
-
logger.debug(f'skipping dir:{srcpath}')
|
|
113
|
-
dnames.remove(name)
|
|
114
|
-
continue
|
|
115
|
-
|
|
116
|
-
dstpath = s_common.genpath(dstdir, relname)
|
|
117
|
-
|
|
118
|
-
info = lmdbinfo.get(os.path.abspath(srcpath))
|
|
119
|
-
|
|
120
|
-
if info is not None:
|
|
121
|
-
logger.debug('backing up lmdb file: %s', srcpath)
|
|
122
|
-
dnames.remove(name)
|
|
123
|
-
env, txn = info
|
|
124
|
-
backup_lmdb(env, dstpath, txn=txn)
|
|
125
|
-
continue
|
|
126
|
-
|
|
127
|
-
if name.endswith('.lmdb'):
|
|
128
|
-
logger.warning('lmdb file %s not copied', srcpath)
|
|
129
|
-
dnames.remove(name)
|
|
130
|
-
continue
|
|
131
|
-
|
|
132
|
-
logger.debug(f'making dir:{dstpath}')
|
|
133
|
-
s_common.gendir(dstpath)
|
|
134
|
-
|
|
135
|
-
for name in fnames:
|
|
136
|
-
|
|
137
|
-
srcpath = s_common.genpath(root, name)
|
|
138
|
-
# skip unix sockets etc...
|
|
139
|
-
if not os.path.isfile(srcpath):
|
|
140
|
-
continue
|
|
141
|
-
|
|
142
|
-
dstpath = s_common.genpath(dstdir, relpath, name)
|
|
143
|
-
logger.debug(f'copying: {srcpath} -> {dstpath}')
|
|
144
|
-
shutil.copy(srcpath, dstpath)
|
|
145
|
-
|
|
146
|
-
tock = s_common.now()
|
|
147
|
-
|
|
148
|
-
logger.debug(f'Backup complete. Took [{tock-tick:.2f}] for [{srcdir}]')
|
|
149
|
-
return
|
|
150
|
-
|
|
151
|
-
def backup_lmdb(env: lmdb.Environment, dstdir: str, txn=None):
|
|
152
|
-
|
|
153
|
-
tick = time.time()
|
|
154
|
-
|
|
155
|
-
s_common.gendir(dstdir)
|
|
156
|
-
|
|
157
|
-
env.copy(dstdir, compact=True, txn=txn)
|
|
158
|
-
|
|
159
|
-
tock = time.time()
|
|
160
|
-
logger.info(f'backup of: {env.path()} took: {tock-tick:.2f} seconds')
|
|
161
|
-
|
|
162
|
-
def main(argv):
|
|
163
|
-
args = parse_args(argv)
|
|
164
|
-
backup(args.srcdir, args.dstdir, args.skipdirs)
|
|
165
|
-
return 0
|
|
166
|
-
|
|
167
|
-
def parse_args(argv):
|
|
168
|
-
desc = 'Create an optimized backup of a Synapse directory.'
|
|
169
|
-
parser = argparse.ArgumentParser('synapse.tools.backup', description=desc)
|
|
170
|
-
parser.add_argument('srcdir', help='Path to the Synapse directory to backup.')
|
|
171
|
-
parser.add_argument('dstdir', help='Path to the backup target directory.')
|
|
172
|
-
parser.add_argument('--skipdirs', nargs='+',
|
|
173
|
-
help='Glob patterns of relative directory names to exclude from the backup.')
|
|
174
|
-
args = parser.parse_args(argv)
|
|
175
|
-
return args
|
|
176
|
-
|
|
177
|
-
def _main(argv): # pragma: no cover
|
|
178
|
-
s_common.setlogging(logger, defval='DEBUG')
|
|
179
|
-
return main(argv)
|
|
7
|
+
s_common.deprecated('synapse.tools.backup is deprecated. Please use synapse.tools.service.backup instead.',
|
|
8
|
+
curv='v2.225.0')
|
|
180
9
|
|
|
181
10
|
if __name__ == '__main__': # pragma: no cover
|
|
182
|
-
|
|
11
|
+
s_common.setlogging(logger, defval='DEBUG')
|
|
12
|
+
s_cmd.exitmain(main)
|