synapse 2.167.0__py311-none-any.whl → 2.169.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/cortex.py +35 -19
- synapse/lib/agenda.py +3 -0
- synapse/lib/aha.py +2 -1
- synapse/lib/cell.py +6 -14
- synapse/lib/httpapi.py +3 -0
- synapse/lib/layer.py +2 -0
- synapse/lib/modelrev.py +6 -0
- synapse/lib/modules.py +1 -0
- synapse/lib/snap.py +3 -1
- synapse/lib/stormlib/aha.py +21 -6
- synapse/lib/stormlib/easyperm.py +8 -0
- synapse/lib/stormlib/model.py +120 -0
- synapse/lib/stormtypes.py +17 -2
- synapse/lib/version.py +2 -2
- synapse/lib/view.py +8 -10
- synapse/models/planning.py +161 -0
- synapse/tests/test_cortex.py +1 -1
- synapse/tests/test_lib_agenda.py +17 -3
- synapse/tests/test_lib_aha.py +7 -2
- synapse/tests/test_lib_cell.py +21 -52
- synapse/tests/test_lib_stormlib_aha.py +1 -1
- synapse/tests/test_lib_stormlib_cortex.py +12 -0
- synapse/tests/test_lib_stormlib_model.py +105 -0
- synapse/tests/test_lib_stormtypes.py +26 -0
- synapse/tests/test_lib_trigger.py +3 -3
- synapse/tests/test_model_planning.py +126 -0
- {synapse-2.167.0.dist-info → synapse-2.169.0.dist-info}/METADATA +1 -1
- {synapse-2.167.0.dist-info → synapse-2.169.0.dist-info}/RECORD +31 -29
- {synapse-2.167.0.dist-info → synapse-2.169.0.dist-info}/LICENSE +0 -0
- {synapse-2.167.0.dist-info → synapse-2.169.0.dist-info}/WHEEL +0 -0
- {synapse-2.167.0.dist-info → synapse-2.169.0.dist-info}/top_level.txt +0 -0
synapse/cortex.py
CHANGED
|
@@ -573,14 +573,6 @@ class CoreApi(s_cell.CellApi):
|
|
|
573
573
|
async def delStormDmon(self, iden):
|
|
574
574
|
return await self.cell.delStormDmon(iden)
|
|
575
575
|
|
|
576
|
-
@s_cell.adminapi(log=True)
|
|
577
|
-
async def enableMigrationMode(self): # pragma: no cover
|
|
578
|
-
s_common.deprdate('CoreApi.enableMigrationMode', '2024-05-05')
|
|
579
|
-
|
|
580
|
-
@s_cell.adminapi(log=True)
|
|
581
|
-
async def disableMigrationMode(self): # pragma: no cover
|
|
582
|
-
s_common.deprdate('CoreApi.disableMigrationMode', '2024-05-05')
|
|
583
|
-
|
|
584
576
|
@s_cell.adminapi()
|
|
585
577
|
async def cloneLayer(self, iden, ldef=None):
|
|
586
578
|
|
|
@@ -881,7 +873,9 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
|
|
|
881
873
|
|
|
882
874
|
self.maxnodes = self.conf.get('max:nodes')
|
|
883
875
|
self.nodecount = 0
|
|
876
|
+
|
|
884
877
|
self.migration = False
|
|
878
|
+
self._migration_lock = asyncio.Lock()
|
|
885
879
|
|
|
886
880
|
self.stormmods = {} # name: mdef
|
|
887
881
|
self.stormpkgs = {} # name: pkgdef
|
|
@@ -1348,6 +1342,17 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
|
|
|
1348
1342
|
'ex': 'node.prop.del.inet:ipv4.asn',
|
|
1349
1343
|
'desc': 'Controls removing a specific property from a form of node in a layer.'},
|
|
1350
1344
|
|
|
1345
|
+
{'perm': ('node', 'data', 'set'), 'gate': 'layer',
|
|
1346
|
+
'desc': 'Permits a user to set node data in a given layer.'},
|
|
1347
|
+
{'perm': ('node', 'data', 'set', '<key>'), 'gate': 'layer',
|
|
1348
|
+
'ex': 'node.data.set.hehe',
|
|
1349
|
+
'desc': 'Permits a user to set node data in a given layer for a specific key.'},
|
|
1350
|
+
{'perm': ('node', 'data', 'pop'), 'gate': 'layer',
|
|
1351
|
+
'desc': 'Permits a user to remove node data in a given layer.'},
|
|
1352
|
+
{'perm': ('node', 'data', 'pop', '<key>'), 'gate': 'layer',
|
|
1353
|
+
'ex': 'node.data.pop.hehe',
|
|
1354
|
+
'desc': 'Permits a user to remove node data in a given layer for a specific key.'},
|
|
1355
|
+
|
|
1351
1356
|
{'perm': ('pkg', 'add'), 'gate': 'cortex',
|
|
1352
1357
|
'desc': 'Controls access to adding storm packages.'},
|
|
1353
1358
|
{'perm': ('pkg', 'del'), 'gate': 'cortex',
|
|
@@ -1480,12 +1485,21 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
|
|
|
1480
1485
|
await self.stormdmons.start()
|
|
1481
1486
|
await self.agenda.clearRunningStatus()
|
|
1482
1487
|
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1488
|
+
async def _runMigrations():
|
|
1489
|
+
# Run migrations when this cortex becomes active. This is to prevent
|
|
1490
|
+
# migrations getting skipped in a zero-downtime upgrade path
|
|
1491
|
+
# (upgrade mirror, promote mirror).
|
|
1492
|
+
await self._checkLayerModels()
|
|
1486
1493
|
|
|
1487
|
-
|
|
1488
|
-
|
|
1494
|
+
# Once migrations are complete, start the view and layer tasks.
|
|
1495
|
+
for view in self.views.values():
|
|
1496
|
+
await view.initTrigTask()
|
|
1497
|
+
await view.initMergeTask()
|
|
1498
|
+
|
|
1499
|
+
for layer in self.layers.values():
|
|
1500
|
+
await layer.initLayerActive()
|
|
1501
|
+
|
|
1502
|
+
self.runActiveTask(_runMigrations())
|
|
1489
1503
|
|
|
1490
1504
|
await self.initStormPool()
|
|
1491
1505
|
|
|
@@ -3597,6 +3611,7 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
|
|
|
3597
3611
|
break
|
|
3598
3612
|
|
|
3599
3613
|
yield ioff, layr.iden, SYNC_NODEEDITS, item, meta
|
|
3614
|
+
await asyncio.sleep(0)
|
|
3600
3615
|
|
|
3601
3616
|
if layr.isdeleted:
|
|
3602
3617
|
yield layr.deloffs, layr.iden, SYNC_LAYR_DEL, (), {}
|
|
@@ -4363,7 +4378,7 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
|
|
|
4363
4378
|
return ret
|
|
4364
4379
|
|
|
4365
4380
|
async def _checkLayerModels(self):
|
|
4366
|
-
with self.enterMigrationMode():
|
|
4381
|
+
async with self.enterMigrationMode():
|
|
4367
4382
|
mrev = s_modelrev.ModelRev(self)
|
|
4368
4383
|
await mrev.revCoreLayers()
|
|
4369
4384
|
|
|
@@ -6098,11 +6113,12 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
|
|
|
6098
6113
|
appt = await self.agenda.get(iden)
|
|
6099
6114
|
await appt.edits(edits)
|
|
6100
6115
|
|
|
6101
|
-
@contextlib.
|
|
6102
|
-
def enterMigrationMode(self):
|
|
6103
|
-
self.
|
|
6104
|
-
|
|
6105
|
-
|
|
6116
|
+
@contextlib.asynccontextmanager
|
|
6117
|
+
async def enterMigrationMode(self):
|
|
6118
|
+
async with self._migration_lock:
|
|
6119
|
+
self.migration = True
|
|
6120
|
+
yield
|
|
6121
|
+
self.migration = False
|
|
6106
6122
|
|
|
6107
6123
|
async def iterFormRows(self, layriden, form, stortype=None, startvalu=None):
|
|
6108
6124
|
'''
|
synapse/lib/agenda.py
CHANGED
synapse/lib/aha.py
CHANGED
|
@@ -807,7 +807,8 @@ class AhaCell(s_cell.Cell):
|
|
|
807
807
|
async def _setAhaSvcDown(self, name, linkiden, network=None):
|
|
808
808
|
svcname, svcnetw, svcfull = self._nameAndNetwork(name, network)
|
|
809
809
|
path = ('aha', 'services', svcnetw, svcname)
|
|
810
|
-
await self.jsonstor.cmpDelPathObjProp(path, 'svcinfo/online', linkiden)
|
|
810
|
+
if await self.jsonstor.cmpDelPathObjProp(path, 'svcinfo/online', linkiden):
|
|
811
|
+
await self.jsonstor.setPathObjProp(path, 'svcinfo/ready', False)
|
|
811
812
|
|
|
812
813
|
# Check if we have any links which may need to be removed
|
|
813
814
|
current_sessions = {s_common.guid(iden): sess for iden, sess in self.dmon.sessions.items()}
|
synapse/lib/cell.py
CHANGED
|
@@ -1295,14 +1295,20 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1295
1295
|
|
|
1296
1296
|
async def _onBootOptimize(self):
|
|
1297
1297
|
|
|
1298
|
+
bdir = s_common.genpath(self.dirn, 'backups')
|
|
1298
1299
|
tdir = s_common.gendir(self.dirn, 'tmp')
|
|
1299
1300
|
tdev = os.stat(tdir).st_dev
|
|
1300
1301
|
|
|
1302
|
+
logger.warning('Collecting LMDB files for onboot optimization.')
|
|
1303
|
+
|
|
1301
1304
|
lmdbs = []
|
|
1302
1305
|
for (root, dirs, files) in os.walk(self.dirn):
|
|
1303
1306
|
for dirname in dirs:
|
|
1304
1307
|
filepath = os.path.join(root, dirname, 'data.mdb')
|
|
1305
1308
|
if os.path.isfile(filepath):
|
|
1309
|
+
if filepath.startswith(bdir):
|
|
1310
|
+
logger.debug(f'Skipping backup file {filepath}')
|
|
1311
|
+
continue
|
|
1306
1312
|
if os.stat(filepath).st_dev != tdev:
|
|
1307
1313
|
logger.warning(f'Unable to run onboot:optimize, {filepath} is not on the same volume as {tdir}')
|
|
1308
1314
|
return
|
|
@@ -2891,24 +2897,10 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
2891
2897
|
self.onfini(self.dmon.fini)
|
|
2892
2898
|
|
|
2893
2899
|
async def _initCellHive(self):
|
|
2894
|
-
isnew = not self.slab.dbexists('hive')
|
|
2895
|
-
|
|
2896
2900
|
db = self.slab.initdb('hive')
|
|
2897
2901
|
hive = await s_hive.SlabHive.anit(self.slab, db=db, nexsroot=self.getCellNexsRoot())
|
|
2898
2902
|
self.onfini(hive)
|
|
2899
2903
|
|
|
2900
|
-
if isnew:
|
|
2901
|
-
path = os.path.join(self.dirn, 'hiveboot.yaml')
|
|
2902
|
-
if os.path.isfile(path):
|
|
2903
|
-
s_common.deprdate('Initial hive config from hiveboot.yaml', '2024-05-05')
|
|
2904
|
-
logger.debug(f'Loading cell hive from {path}')
|
|
2905
|
-
tree = s_common.yamlload(path)
|
|
2906
|
-
if tree is not None:
|
|
2907
|
-
# Pack and unpack the tree to avoid tuple/list issues
|
|
2908
|
-
# for in-memory structures.
|
|
2909
|
-
tree = s_common.tuplify(tree)
|
|
2910
|
-
await hive.loadHiveTree(tree)
|
|
2911
|
-
|
|
2912
2904
|
return hive
|
|
2913
2905
|
|
|
2914
2906
|
async def _initCellSlab(self, readonly=False):
|
synapse/lib/httpapi.py
CHANGED
synapse/lib/layer.py
CHANGED
|
@@ -165,11 +165,13 @@ class LayerApi(s_cell.CellApi):
|
|
|
165
165
|
await self._reqUserAllowed(self.liftperm)
|
|
166
166
|
async for item in self.layr.syncNodeEdits(offs, wait=wait):
|
|
167
167
|
yield item
|
|
168
|
+
await asyncio.sleep(0)
|
|
168
169
|
|
|
169
170
|
async def syncNodeEdits2(self, offs, wait=True):
|
|
170
171
|
await self._reqUserAllowed(self.liftperm)
|
|
171
172
|
async for item in self.layr.syncNodeEdits2(offs, wait=wait):
|
|
172
173
|
yield item
|
|
174
|
+
await asyncio.sleep(0)
|
|
173
175
|
|
|
174
176
|
async def getEditIndx(self):
|
|
175
177
|
'''
|
synapse/lib/modelrev.py
CHANGED
|
@@ -768,6 +768,12 @@ class ModelRev:
|
|
|
768
768
|
Returns:
|
|
769
769
|
None
|
|
770
770
|
'''
|
|
771
|
+
if opts is None:
|
|
772
|
+
opts = {}
|
|
773
|
+
|
|
774
|
+
# Migrations only run on leaders
|
|
775
|
+
opts['mirror'] = False
|
|
776
|
+
|
|
771
777
|
async def _runStorm():
|
|
772
778
|
async for mesgtype, mesginfo in self.core.storm(text, opts=opts):
|
|
773
779
|
if mesgtype == 'print':
|
synapse/lib/modules.py
CHANGED
synapse/lib/snap.py
CHANGED
|
@@ -717,7 +717,9 @@ class Snap(s_base.Base):
|
|
|
717
717
|
|
|
718
718
|
show_storage = False
|
|
719
719
|
|
|
720
|
-
|
|
720
|
+
info = opts.get('_loginfo', {})
|
|
721
|
+
info.update({'mode': opts.get('mode', 'storm'), 'view': self.view.iden})
|
|
722
|
+
self.core._logStormQuery(text, user, info=info)
|
|
721
723
|
|
|
722
724
|
# { form: ( embedprop, ... ) }
|
|
723
725
|
embeds = opts.get('embeds')
|
synapse/lib/stormlib/aha.py
CHANGED
|
@@ -190,7 +190,7 @@ class AhaPool(s_stormtypes.StormType):
|
|
|
190
190
|
'desc': 'The name of the AHA service to add. It is easiest to use the relative name of a service, ending with "...".', },
|
|
191
191
|
),
|
|
192
192
|
'returns': {'type': 'null', }}},
|
|
193
|
-
{'name': '
|
|
193
|
+
{'name': 'del', 'desc': '''Remove a service from the AHA pool.
|
|
194
194
|
|
|
195
195
|
Examples:
|
|
196
196
|
Remove a service from a pool with its relative name::
|
|
@@ -203,7 +203,7 @@ class AhaPool(s_stormtypes.StormType):
|
|
|
203
203
|
{'name': 'svcname', 'type': 'str',
|
|
204
204
|
'desc': 'The name of the AHA service to remove. It is easiest to use the relative name of a service, ending with "...".', },
|
|
205
205
|
),
|
|
206
|
-
'returns': {'type': 'null', }}},
|
|
206
|
+
'returns': {'type': ['null', 'str'], 'desc': 'The service removed from the pool or null if a service was not removed.'}}},
|
|
207
207
|
)
|
|
208
208
|
_storm_typename = 'aha:pool'
|
|
209
209
|
|
|
@@ -243,9 +243,20 @@ class AhaPool(s_stormtypes.StormType):
|
|
|
243
243
|
proxy = await self.runt.snap.core.reqAhaProxy()
|
|
244
244
|
|
|
245
245
|
poolname = self.poolinfo.get('name')
|
|
246
|
-
await proxy.delAhaPoolSvc(poolname, svcname)
|
|
246
|
+
newinfo = await proxy.delAhaPoolSvc(poolname, svcname)
|
|
247
|
+
|
|
248
|
+
tname = svcname
|
|
249
|
+
if tname.endswith('...'):
|
|
250
|
+
tname = tname[:-2]
|
|
251
|
+
deleted_service = None
|
|
252
|
+
deleted_services = [svc for svc in self.poolinfo.get('services').keys()
|
|
253
|
+
if svc not in newinfo.get('services') and svc.startswith(tname)]
|
|
254
|
+
if deleted_services:
|
|
255
|
+
deleted_service = deleted_services[0]
|
|
256
|
+
|
|
257
|
+
self.poolinfo = newinfo
|
|
247
258
|
|
|
248
|
-
|
|
259
|
+
return deleted_service
|
|
249
260
|
|
|
250
261
|
stormcmds = (
|
|
251
262
|
{
|
|
@@ -319,8 +330,12 @@ stormcmds = (
|
|
|
319
330
|
$pool = $lib.aha.pool.get($cmdopts.poolname)
|
|
320
331
|
if (not $pool) { $lib.exit(`No AHA service pool named: {$cmdopts.poolname}`) }
|
|
321
332
|
|
|
322
|
-
$pool.del($cmdopts.svcname)
|
|
323
|
-
|
|
333
|
+
$svc = $pool.del($cmdopts.svcname)
|
|
334
|
+
if $svc {
|
|
335
|
+
$lib.print(`AHA service ({$svc}) removed from service pool ({$pool.name})`)
|
|
336
|
+
} else {
|
|
337
|
+
$lib.print(`Did not remove ({$cmdopts.svcname}) from the service pool.`)
|
|
338
|
+
}
|
|
324
339
|
''',
|
|
325
340
|
},
|
|
326
341
|
{
|
synapse/lib/stormlib/easyperm.py
CHANGED
|
@@ -51,6 +51,14 @@ class LibEasyPerm(s_stormtypes.Lib):
|
|
|
51
51
|
'desc': 'Optional error message to present if user does not have required permission level.'},
|
|
52
52
|
),
|
|
53
53
|
'returns': {'type': 'null'}}},
|
|
54
|
+
{'name': 'level.admin', 'desc': 'Constant for admin permission.',
|
|
55
|
+
'type': 'int', },
|
|
56
|
+
{'name': 'level.deny', 'desc': 'Constant for deny permission.',
|
|
57
|
+
'type': 'int', },
|
|
58
|
+
{'name': 'level.edit', 'desc': 'Constant for edit permission.',
|
|
59
|
+
'type': 'int', },
|
|
60
|
+
{'name': 'level.read', 'desc': 'Constant for read permission.',
|
|
61
|
+
'type': 'int', },
|
|
54
62
|
)
|
|
55
63
|
_storm_lib_path = ('auth', 'easyperm')
|
|
56
64
|
|
synapse/lib/stormlib/model.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import synapse.exc as s_exc
|
|
2
2
|
import synapse.common as s_common
|
|
3
3
|
|
|
4
|
+
import synapse.lib.node as s_node
|
|
4
5
|
import synapse.lib.cache as s_cache
|
|
5
6
|
import synapse.lib.stormtypes as s_stormtypes
|
|
6
7
|
|
|
@@ -692,3 +693,122 @@ class LibModelDeprecated(s_stormtypes.Lib):
|
|
|
692
693
|
todo = s_common.todo('setDeprLock', name, locked)
|
|
693
694
|
gatekeys = ((self.runt.user.iden, ('model', 'deprecated', 'lock'), None),)
|
|
694
695
|
await self.runt.dyncall('cortex', todo, gatekeys=gatekeys)
|
|
696
|
+
|
|
697
|
+
@s_stormtypes.registry.registerLib
|
|
698
|
+
class LibModelMigration(s_stormtypes.Lib):
|
|
699
|
+
'''
|
|
700
|
+
A Storm library containing migration tools.
|
|
701
|
+
'''
|
|
702
|
+
_storm_locals = (
|
|
703
|
+
{'name': 'copyData', 'desc': 'Copy node data from the src node to the dst node.',
|
|
704
|
+
'type': {'type': 'function', '_funcname': '_methCopyData',
|
|
705
|
+
'args': (
|
|
706
|
+
{'name': 'src', 'type': 'node', 'desc': 'The node to copy data from.', },
|
|
707
|
+
{'name': 'dst', 'type': 'node', 'desc': 'The node to copy data to.', },
|
|
708
|
+
{'name': 'overwrite', 'type': 'boolean', 'default': False,
|
|
709
|
+
'desc': 'Copy data even if the key exists on the destination node.', },
|
|
710
|
+
),
|
|
711
|
+
'returns': {'type': 'null', }}},
|
|
712
|
+
{'name': 'copyEdges', 'desc': 'Copy edges from the src node to the dst node.',
|
|
713
|
+
'type': {'type': 'function', '_funcname': '_methCopyEdges',
|
|
714
|
+
'args': (
|
|
715
|
+
{'name': 'src', 'type': 'node', 'desc': 'The node to copy edges from.', },
|
|
716
|
+
{'name': 'dst', 'type': 'node', 'desc': 'The node to copy edges to.', },
|
|
717
|
+
),
|
|
718
|
+
'returns': {'type': 'null', }}},
|
|
719
|
+
{'name': 'copyTags', 'desc': 'Copy tags, tag timestamps, and tag props from the src node to the dst node.',
|
|
720
|
+
'type': {'type': 'function', '_funcname': '_methCopyTags',
|
|
721
|
+
'args': (
|
|
722
|
+
{'name': 'src', 'type': 'node', 'desc': 'The node to copy tags from.', },
|
|
723
|
+
{'name': 'dst', 'type': 'node', 'desc': 'The node to copy tags to.', },
|
|
724
|
+
{'name': 'overwrite', 'type': 'boolean', 'default': False,
|
|
725
|
+
'desc': 'Copy tag property value even if the property exists on the destination node.', },
|
|
726
|
+
),
|
|
727
|
+
'returns': {'type': 'null', }}},
|
|
728
|
+
)
|
|
729
|
+
_storm_lib_path = ('model', 'migration')
|
|
730
|
+
|
|
731
|
+
def getObjLocals(self):
|
|
732
|
+
return {
|
|
733
|
+
'copyData': self._methCopyData,
|
|
734
|
+
'copyEdges': self._methCopyEdges,
|
|
735
|
+
'copyTags': self._methCopyTags,
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
async def _methCopyData(self, src, dst, overwrite=False):
|
|
739
|
+
|
|
740
|
+
if not isinstance(src, s_node.Node):
|
|
741
|
+
raise s_exc.BadArg(mesg='$lib.model.migration.copyData() source argument must be a node.')
|
|
742
|
+
if not isinstance(dst, s_node.Node):
|
|
743
|
+
raise s_exc.BadArg(mesg='$lib.model.migration.copyData() dest argument must be a node.')
|
|
744
|
+
|
|
745
|
+
overwrite = await s_stormtypes.tobool(overwrite)
|
|
746
|
+
|
|
747
|
+
async with self.runt.snap.getEditor() as editor:
|
|
748
|
+
|
|
749
|
+
proto = editor.loadNode(dst)
|
|
750
|
+
|
|
751
|
+
async for name in src.iterDataKeys():
|
|
752
|
+
if overwrite or not await dst.hasData(name):
|
|
753
|
+
self.runt.layerConfirm(('node', 'data', 'set', name))
|
|
754
|
+
valu = await src.getData(name)
|
|
755
|
+
await proto.setData(name, valu)
|
|
756
|
+
|
|
757
|
+
async def _methCopyEdges(self, src, dst):
|
|
758
|
+
|
|
759
|
+
if not isinstance(src, s_node.Node):
|
|
760
|
+
raise s_exc.BadArg(mesg='$lib.model.migration.copyEdges() source argument must be a node.')
|
|
761
|
+
if not isinstance(dst, s_node.Node):
|
|
762
|
+
raise s_exc.BadArg(mesg='$lib.model.migration.copyEdges() dest argument must be a node.')
|
|
763
|
+
|
|
764
|
+
snap = self.runt.snap
|
|
765
|
+
|
|
766
|
+
async with snap.getEditor() as editor:
|
|
767
|
+
|
|
768
|
+
proto = editor.loadNode(dst)
|
|
769
|
+
verbs = set()
|
|
770
|
+
|
|
771
|
+
async for (verb, n2iden) in src.iterEdgesN1():
|
|
772
|
+
|
|
773
|
+
if verb not in verbs:
|
|
774
|
+
self.runt.layerConfirm(('node', 'edge', 'add', verb))
|
|
775
|
+
verbs.add(verb)
|
|
776
|
+
|
|
777
|
+
if await snap.getNodeByBuid(s_common.uhex(n2iden)) is not None:
|
|
778
|
+
await proto.addEdge(verb, n2iden)
|
|
779
|
+
|
|
780
|
+
dstiden = s_common.ehex(dst.buid)
|
|
781
|
+
|
|
782
|
+
async for (verb, n1iden) in src.iterEdgesN2():
|
|
783
|
+
|
|
784
|
+
if verb not in verbs:
|
|
785
|
+
self.runt.layerConfirm(('node', 'edge', 'add', verb))
|
|
786
|
+
verbs.add(verb)
|
|
787
|
+
|
|
788
|
+
n1proto = await editor.getNodeByBuid(s_common.uhex(n1iden))
|
|
789
|
+
if n1proto is not None:
|
|
790
|
+
await n1proto.addEdge(verb, dstiden)
|
|
791
|
+
|
|
792
|
+
async def _methCopyTags(self, src, dst, overwrite=False):
|
|
793
|
+
|
|
794
|
+
if not isinstance(src, s_node.Node):
|
|
795
|
+
raise s_exc.BadArg(mesg='$lib.model.migration.copyTags() source argument must be a node.')
|
|
796
|
+
if not isinstance(dst, s_node.Node):
|
|
797
|
+
raise s_exc.BadArg(mesg='$lib.model.migration.copyTags() dest argument must be a node.')
|
|
798
|
+
|
|
799
|
+
overwrite = await s_stormtypes.tobool(overwrite)
|
|
800
|
+
|
|
801
|
+
snap = self.runt.snap
|
|
802
|
+
|
|
803
|
+
async with snap.getEditor() as editor:
|
|
804
|
+
|
|
805
|
+
proto = editor.loadNode(dst)
|
|
806
|
+
|
|
807
|
+
for name, valu in src.tags.items():
|
|
808
|
+
self.runt.layerConfirm(('node', 'tag', 'add', *name.split('.')))
|
|
809
|
+
await proto.addTag(name, valu=valu)
|
|
810
|
+
|
|
811
|
+
for tagname, tagprops in src.tagprops.items():
|
|
812
|
+
for propname, valu in tagprops.items():
|
|
813
|
+
if overwrite or not dst.hasTagProp(tagname, propname):
|
|
814
|
+
await proto.setTagProp(tagname, propname, valu) # use tag perms
|
synapse/lib/stormtypes.py
CHANGED
|
@@ -1604,8 +1604,12 @@ class LibBase(Lib):
|
|
|
1604
1604
|
continue
|
|
1605
1605
|
vals.append(arg)
|
|
1606
1606
|
|
|
1607
|
+
if len(vals) < 1:
|
|
1608
|
+
mesg = '$lib.min() must have at least one argument or a list containing at least one value.'
|
|
1609
|
+
raise s_exc.StormRuntimeError(mesg=mesg)
|
|
1610
|
+
|
|
1607
1611
|
ints = [await toint(x) for x in vals]
|
|
1608
|
-
return min(
|
|
1612
|
+
return min(ints)
|
|
1609
1613
|
|
|
1610
1614
|
@stormfunc(readonly=True)
|
|
1611
1615
|
async def _max(self, *args):
|
|
@@ -1618,8 +1622,12 @@ class LibBase(Lib):
|
|
|
1618
1622
|
continue
|
|
1619
1623
|
vals.append(arg)
|
|
1620
1624
|
|
|
1625
|
+
if len(vals) < 1:
|
|
1626
|
+
mesg = '$lib.max() must have at least one argument or a list containing at least one value.'
|
|
1627
|
+
raise s_exc.StormRuntimeError(mesg=mesg)
|
|
1628
|
+
|
|
1621
1629
|
ints = [await toint(x) for x in vals]
|
|
1622
|
-
return max(
|
|
1630
|
+
return max(ints)
|
|
1623
1631
|
|
|
1624
1632
|
@staticmethod
|
|
1625
1633
|
async def _get_mesg(mesg, **kwargs):
|
|
@@ -1707,6 +1715,13 @@ class LibDict(Lib):
|
|
|
1707
1715
|
A Storm Library for interacting with dictionaries.
|
|
1708
1716
|
'''
|
|
1709
1717
|
_storm_locals = (
|
|
1718
|
+
{'name': 'has', 'desc': 'Check a dictionary has a specific key.',
|
|
1719
|
+
'type': {'type': 'function', '_funcname': '_has',
|
|
1720
|
+
'args': (
|
|
1721
|
+
{'name': 'valu', 'type': 'dict', 'desc': 'The dictionary being checked.'},
|
|
1722
|
+
{'name': 'name', 'type': 'str', 'desc': 'The key name to check.'},
|
|
1723
|
+
),
|
|
1724
|
+
'returns': {'type': 'boolean', 'desc': 'True if the key is present, false if the key is not present.'}}},
|
|
1710
1725
|
{'name': 'keys', 'desc': 'Retrieve a list of keys in the specified dictionary.',
|
|
1711
1726
|
'type': {'type': 'function', '_funcname': '_keys',
|
|
1712
1727
|
'args': (
|
synapse/lib/version.py
CHANGED
|
@@ -223,6 +223,6 @@ def reqVersion(valu, reqver,
|
|
|
223
223
|
##############################################################################
|
|
224
224
|
# The following are touched during the release process by bumpversion.
|
|
225
225
|
# Do not modify these directly.
|
|
226
|
-
version = (2,
|
|
226
|
+
version = (2, 169, 0)
|
|
227
227
|
verstring = '.'.join([str(x) for x in version])
|
|
228
|
-
commit = '
|
|
228
|
+
commit = '41421efcf40bb7655af3f90cb7d6d335247fc726'
|
synapse/lib/view.py
CHANGED
|
@@ -55,6 +55,7 @@ class ViewApi(s_cell.CellApi):
|
|
|
55
55
|
layr = self.view.layers[0]
|
|
56
56
|
async for item in layr.syncNodeEdits2(offs, wait=wait):
|
|
57
57
|
yield item
|
|
58
|
+
await asyncio.sleep(0)
|
|
58
59
|
|
|
59
60
|
@s_cell.adminapi()
|
|
60
61
|
async def saveNodeEdits(self, edits, meta):
|
|
@@ -867,20 +868,16 @@ class View(s_nexus.Pusher): # type: ignore
|
|
|
867
868
|
|
|
868
869
|
self.layers.append(layr)
|
|
869
870
|
|
|
870
|
-
async def eval(self, text, opts=None
|
|
871
|
+
async def eval(self, text, opts=None):
|
|
871
872
|
'''
|
|
872
873
|
Evaluate a storm query and yield Nodes only.
|
|
873
874
|
'''
|
|
874
875
|
opts = self.core._initStormOpts(opts)
|
|
875
876
|
user = self.core._userFromOpts(opts)
|
|
876
877
|
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
log_info['mode'] = opts.get('mode', 'storm')
|
|
881
|
-
log_info['view'] = self.iden
|
|
882
|
-
|
|
883
|
-
self.core._logStormQuery(text, user, info=log_info)
|
|
878
|
+
info = opts.get('_loginfo', {})
|
|
879
|
+
info.update({'mode': opts.get('mode', 'storm'), 'view': self.iden})
|
|
880
|
+
self.core._logStormQuery(text, user, info=info)
|
|
884
881
|
|
|
885
882
|
taskiden = opts.get('task')
|
|
886
883
|
taskinfo = {'query': text, 'view': self.iden}
|
|
@@ -996,8 +993,9 @@ class View(s_nexus.Pusher): # type: ignore
|
|
|
996
993
|
count += 1
|
|
997
994
|
|
|
998
995
|
else:
|
|
999
|
-
|
|
1000
|
-
|
|
996
|
+
info = opts.get('_loginfo', {})
|
|
997
|
+
info.update({'mode': opts.get('mode', 'storm'), 'view': self.iden})
|
|
998
|
+
self.core._logStormQuery(text, user, info=info)
|
|
1001
999
|
async for item in snap.storm(text, opts=opts, user=user):
|
|
1002
1000
|
count += 1
|
|
1003
1001
|
|