synapse 2.165.0__py311-none-any.whl → 2.167.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.

Files changed (77) hide show
  1. synapse/axon.py +4 -10
  2. synapse/cmds/cortex.py +1 -6
  3. synapse/common.py +6 -0
  4. synapse/cortex.py +104 -57
  5. synapse/datamodel.py +32 -0
  6. synapse/exc.py +1 -0
  7. synapse/lib/agenda.py +81 -51
  8. synapse/lib/aha.py +2 -0
  9. synapse/lib/ast.py +21 -23
  10. synapse/lib/base.py +11 -10
  11. synapse/lib/cell.py +24 -24
  12. synapse/lib/hive.py +11 -0
  13. synapse/lib/httpapi.py +1 -0
  14. synapse/lib/nexus.py +3 -2
  15. synapse/lib/node.py +4 -2
  16. synapse/lib/schemas.py +3 -1
  17. synapse/lib/snap.py +50 -0
  18. synapse/lib/storm.py +19 -17
  19. synapse/lib/stormlib/aha.py +370 -17
  20. synapse/lib/stormlib/auth.py +11 -4
  21. synapse/lib/stormlib/cache.py +202 -0
  22. synapse/lib/stormlib/cortex.py +69 -7
  23. synapse/lib/stormlib/macro.py +11 -18
  24. synapse/lib/stormlib/spooled.py +109 -0
  25. synapse/lib/stormlib/stix.py +1 -1
  26. synapse/lib/stormtypes.py +61 -17
  27. synapse/lib/trigger.py +10 -12
  28. synapse/lib/types.py +3 -1
  29. synapse/lib/version.py +2 -2
  30. synapse/lib/view.py +16 -3
  31. synapse/models/base.py +8 -0
  32. synapse/models/files.py +3 -0
  33. synapse/models/inet.py +74 -2
  34. synapse/models/orgs.py +52 -8
  35. synapse/models/person.py +30 -11
  36. synapse/models/risk.py +44 -3
  37. synapse/telepath.py +115 -32
  38. synapse/tests/files/stormpkg/dotstorm/dotstorm.yaml +3 -0
  39. synapse/tests/test_cortex.py +79 -8
  40. synapse/tests/test_datamodel.py +22 -0
  41. synapse/tests/test_lib_agenda.py +8 -1
  42. synapse/tests/test_lib_aha.py +19 -6
  43. synapse/tests/test_lib_cell.py +6 -2
  44. synapse/tests/test_lib_grammar.py +62 -64
  45. synapse/tests/test_lib_httpapi.py +1 -1
  46. synapse/tests/test_lib_rstorm.py +4 -4
  47. synapse/tests/test_lib_storm.py +98 -7
  48. synapse/tests/test_lib_stormlib_aha.py +196 -0
  49. synapse/tests/test_lib_stormlib_cache.py +272 -0
  50. synapse/tests/test_lib_stormlib_compression.py +12 -12
  51. synapse/tests/test_lib_stormlib_cortex.py +71 -0
  52. synapse/tests/test_lib_stormlib_macro.py +94 -0
  53. synapse/tests/test_lib_stormlib_spooled.py +190 -0
  54. synapse/tests/test_lib_stormtypes.py +71 -37
  55. synapse/tests/test_lib_view.py +50 -3
  56. synapse/tests/test_model_files.py +3 -0
  57. synapse/tests/test_model_inet.py +67 -0
  58. synapse/tests/test_model_risk.py +6 -0
  59. synapse/tests/test_telepath.py +30 -7
  60. synapse/tests/test_tools_genpkg.py +26 -0
  61. synapse/tests/test_tools_hiveload.py +1 -0
  62. synapse/tests/test_tools_hivesave.py +1 -0
  63. synapse/tests/test_tools_modrole.py +81 -0
  64. synapse/tests/test_tools_moduser.py +105 -0
  65. synapse/tests/utils.py +22 -3
  66. synapse/tools/autodoc.py +1 -1
  67. synapse/tools/hive/load.py +3 -0
  68. synapse/tools/hive/save.py +3 -0
  69. synapse/tools/modrole.py +59 -7
  70. synapse/tools/moduser.py +78 -10
  71. {synapse-2.165.0.dist-info → synapse-2.167.0.dist-info}/METADATA +3 -3
  72. {synapse-2.165.0.dist-info → synapse-2.167.0.dist-info}/RECORD +75 -72
  73. synapse/lib/provenance.py +0 -111
  74. synapse/tests/test_lib_provenance.py +0 -37
  75. {synapse-2.165.0.dist-info → synapse-2.167.0.dist-info}/LICENSE +0 -0
  76. {synapse-2.165.0.dist-info → synapse-2.167.0.dist-info}/WHEEL +0 -0
  77. {synapse-2.165.0.dist-info → synapse-2.167.0.dist-info}/top_level.txt +0 -0
synapse/lib/agenda.py CHANGED
@@ -16,7 +16,6 @@ import synapse.common as s_common
16
16
 
17
17
  import synapse.lib.base as s_base
18
18
  import synapse.lib.coro as s_coro
19
- import synapse.lib.provenance as s_provenance
20
19
 
21
20
  # Agenda: manages running one-shot and periodic tasks in the future ("appointments")
22
21
 
@@ -259,6 +258,7 @@ class _Appt:
259
258
  _synced_attrs = {
260
259
  'doc',
261
260
  'name',
261
+ 'pool',
262
262
  'created',
263
263
  'enabled',
264
264
  'errcount',
@@ -271,10 +271,11 @@ class _Appt:
271
271
  'lastfinishtime',
272
272
  }
273
273
 
274
- def __init__(self, stor, iden, recur, indx, query, creator, recs, nexttime=None, view=None, created=None):
274
+ def __init__(self, stor, iden, recur, indx, query, creator, recs, nexttime=None, view=None, created=None, pool=False):
275
275
  self.doc = ''
276
276
  self.name = ''
277
277
  self.stor = stor
278
+ self.pool = pool
278
279
  self.iden = iden
279
280
  self.recur = recur # does this appointment repeat
280
281
  self.indx = indx # incremented for each appt added ever. Used for nexttime tiebreaking for stable ordering
@@ -339,6 +340,7 @@ class _Appt:
339
340
  'ver': 1,
340
341
  'doc': self.doc,
341
342
  'name': self.name,
343
+ 'pool': self.pool,
342
344
  'enabled': self.enabled,
343
345
  'recur': self.recur,
344
346
  'iden': self.iden,
@@ -366,6 +368,7 @@ class _Appt:
366
368
  appt = cls(stor, val['iden'], val['recur'], val['indx'], val['query'], val['creator'], recs, nexttime=val['nexttime'], view=val.get('view'))
367
369
  appt.doc = val.get('doc', '')
368
370
  appt.name = val.get('name', '')
371
+ appt.pool = val.get('pool', False)
369
372
  appt.created = val.get('created', None)
370
373
  appt.laststarttime = val['laststarttime']
371
374
  appt.lastfinishtime = val['lastfinishtime']
@@ -562,6 +565,8 @@ class Agenda(s_base.Base):
562
565
  view = cdef.get('view')
563
566
  created = cdef.get('created')
564
567
 
568
+ pool = cdef.get('pool', False)
569
+
565
570
  recur = incunit is not None
566
571
  indx = self._next_indx
567
572
  self._next_indx += 1
@@ -602,7 +607,7 @@ class Agenda(s_base.Base):
602
607
  incvals = (incvals, )
603
608
  recs.extend(ApptRec(rd, incunit, v) for (rd, v) in itertools.product(reqdicts, incvals))
604
609
 
605
- appt = _Appt(self, iden, recur, indx, query, creator, recs, nexttime=nexttime, view=view, created=created)
610
+ appt = _Appt(self, iden, recur, indx, query, creator, recs, nexttime=nexttime, view=view, created=created, pool=pool)
606
611
  self._addappt(iden, appt)
607
612
 
608
613
  appt.doc = cdef.get('doc', '')
@@ -698,6 +703,13 @@ class Agenda(s_base.Base):
698
703
  self.tickoff += offs
699
704
  self._wake_event.set()
700
705
 
706
+ async def clearRunningStatus(self):
707
+ '''Used for clearing the running state at startup or change of leadership.'''
708
+ for appt in list(self.appts.values()):
709
+ if appt.isrunning:
710
+ logger.debug(f'Clearing the isrunning flag for {appt.iden}')
711
+ await self.core.addCronEdits(appt.iden, {'isrunning': False})
712
+
701
713
  async def runloop(self):
702
714
  '''
703
715
  Task loop to issue query tasks at the right times.
@@ -808,57 +820,75 @@ class Agenda(s_base.Base):
808
820
  }
809
821
  await self.core.addCronEdits(appt.iden, edits)
810
822
 
811
- with s_provenance.claim('cron', iden=appt.iden):
812
- logger.info(f'Agenda executing for iden={appt.iden}, name={appt.name} user={user.name}, view={appt.view}, query={appt.query}',
813
- extra={'synapse': {'iden': appt.iden, 'name': appt.name, 'user': user.iden, 'text': appt.query,
814
- 'username': user.name, 'view': appt.view}})
815
- starttime = self._getNowTick()
816
- success = False
817
- try:
818
- opts = {'user': user.iden, 'view': appt.view, 'vars': {'auto': {'iden': appt.iden, 'type': 'cron'}}}
819
- opts = self.core._initStormOpts(opts)
820
- view = self.core._viewFromOpts(opts)
821
- # Yes, this isn't technically on the bottom half of a nexus transaction
822
- # But because the scheduling loop only runs on non-mirrors, we can kinda skirt by all that
823
- # and be relatively okay. The only catch is that the nexus offset will correspond to the
824
- # last nexus transaction, and not the start/stop
825
- await self.core.feedBeholder('cron:start', {'iden': appt.iden})
826
- async for node in view.eval(appt.query, opts=opts, log_info={'cron': appt.iden}):
823
+ logger.info(f'Agenda executing for iden={appt.iden}, name={appt.name} user={user.name}, view={appt.view}, query={appt.query}',
824
+ extra={'synapse': {'iden': appt.iden, 'name': appt.name, 'user': user.iden, 'text': appt.query,
825
+ 'username': user.name, 'view': appt.view}})
826
+ starttime = self._getNowTick()
827
+ success = False
828
+ try:
829
+ opts = {
830
+ 'user': user.iden,
831
+ 'view': appt.view,
832
+ 'mirror': appt.pool,
833
+ 'vars': {'auto': {'iden': appt.iden, 'type': 'cron'}},
834
+ }
835
+ opts = self.core._initStormOpts(opts)
836
+
837
+ await self.core.feedBeholder('cron:start', {'iden': appt.iden})
838
+
839
+ async for mesg in self.core.storm(appt.query, opts=opts):
840
+
841
+ if mesg[0] == 'node':
827
842
  count += 1
828
- except asyncio.CancelledError:
829
- result = 'cancelled'
830
- raise
831
- except Exception as e:
832
- result = f'raised exception {e}'
833
- logger.exception(f'Agenda job {appt.iden} {appt.name} raised exception',
834
- extra={'synapse': {'iden': appt.iden, 'name': appt.name}}
835
- )
836
- else:
837
- success = True
838
- result = f'finished successfully with {count} nodes'
839
- finally:
840
- finishtime = self._getNowTick()
841
- if not success:
842
- appt.lasterrs.append(result)
843
- edits = {
844
- 'errcount': appt.errcount + 1,
845
- # we only care about the last five errors
846
- 'lasterrs': list(appt.lasterrs[-5:]),
847
- }
848
- await self.core.addCronEdits(appt.iden, edits)
849
843
 
850
- took = finishtime - starttime
851
- mesg = f'Agenda completed query for iden={appt.iden} name={appt.name} with result "{result}" ' \
852
- f'took {took:.3f}s'
853
- logger.info(mesg, extra={'synapse': {'iden': appt.iden, 'name': appt.name, 'user': user.iden,
854
- 'result': result, 'username': user.name, 'took': took}})
844
+ elif mesg[0] == 'err':
845
+ excname, errinfo = mesg[1]
846
+ errinfo.pop('eline', None)
847
+ errinfo.pop('efile', None)
848
+ excctor = getattr(s_exc, excname, s_exc.SynErr)
849
+ raise excctor(**errinfo)
850
+
851
+ except asyncio.CancelledError:
852
+ result = 'cancelled'
853
+ raise
854
+
855
+ except Exception as e:
856
+ result = f'raised exception {e}'
857
+ logger.exception(f'Agenda job {appt.iden} {appt.name} raised exception',
858
+ extra={'synapse': {'iden': appt.iden, 'name': appt.name}}
859
+ )
860
+ else:
861
+ success = True
862
+ result = f'finished successfully with {count} nodes'
863
+
864
+ finally:
865
+ finishtime = self._getNowTick()
866
+ if not success:
867
+ appt.lasterrs.append(result)
855
868
  edits = {
856
- 'lastfinishtime': finishtime,
857
- 'isrunning': False,
858
- 'lastresult': result,
869
+ 'errcount': appt.errcount + 1,
870
+ # we only care about the last five errors
871
+ 'lasterrs': list(appt.lasterrs[-5:]),
859
872
  }
873
+
874
+ if self.core.isactive:
875
+ await self.core.addCronEdits(appt.iden, edits)
876
+
877
+ took = finishtime - starttime
878
+ mesg = f'Agenda completed query for iden={appt.iden} name={appt.name} with result "{result}" ' \
879
+ f'took {took:.3f}s'
880
+ if not self.core.isactive:
881
+ mesg = mesg + ' Agenda status will not be saved since the Cortex is no longer the leader.'
882
+ logger.info(mesg, extra={'synapse': {'iden': appt.iden, 'name': appt.name, 'user': user.iden,
883
+ 'result': result, 'username': user.name, 'took': took}})
884
+ edits = {
885
+ 'lastfinishtime': finishtime,
886
+ 'isrunning': False,
887
+ 'lastresult': result,
888
+ }
889
+ if self.core.isactive:
860
890
  await self.core.addCronEdits(appt.iden, edits)
861
891
 
862
- if not self.isfini:
863
- # fire beholder event before invoking nexus change (in case readonly)
864
- await self.core.feedBeholder('cron:stop', {'iden': appt.iden})
892
+ if not self.isfini:
893
+ # fire beholder event before invoking nexus change (in case readonly)
894
+ await self.core.feedBeholder('cron:stop', {'iden': appt.iden})
synapse/lib/aha.py CHANGED
@@ -782,6 +782,8 @@ class AhaCell(s_cell.Cell):
782
782
 
783
783
  svcname, svcnetw, svcfull = self._nameAndNetwork(name, network)
784
784
 
785
+ logger.info(f'Deleting service [{svcfull}].', extra=await self.getLogExtra(name=svcname, netw=svcnetw))
786
+
785
787
  full = ('aha', 'svcfull', svcfull)
786
788
  path = ('aha', 'services', svcnetw, svcname)
787
789
 
synapse/lib/ast.py CHANGED
@@ -24,7 +24,6 @@ import synapse.lib.scrape as s_scrape
24
24
  import synapse.lib.msgpack as s_msgpack
25
25
  import synapse.lib.spooled as s_spooled
26
26
  import synapse.lib.stormctrl as s_stormctrl
27
- import synapse.lib.provenance as s_provenance
28
27
  import synapse.lib.stormtypes as s_stormtypes
29
28
 
30
29
  from synapse.lib.stormtypes import tobool, toint, toprim, tostr, tonumber, tocmprvalu, undef
@@ -1180,33 +1179,32 @@ class CmdOper(Oper):
1180
1179
  mesg = f'Command ({name}) is not marked safe for readonly use.'
1181
1180
  raise self.addExcInfo(s_exc.IsReadOnly(mesg=mesg))
1182
1181
 
1183
- with s_provenance.claim('stormcmd', name=name):
1184
- async def genx():
1182
+ async def genx():
1185
1183
 
1186
- async for node, path in genr:
1187
- argv = await self.kids[1].compute(runt, path)
1188
- if not await scmd.setArgv(argv):
1189
- raise s_stormctrl.StormExit()
1184
+ async for node, path in genr:
1185
+ argv = await self.kids[1].compute(runt, path)
1186
+ if not await scmd.setArgv(argv):
1187
+ raise s_stormctrl.StormExit()
1190
1188
 
1191
- yield node, path
1189
+ yield node, path
1192
1190
 
1193
- # must pull through the genr to get opts set
1194
- # ( many commands expect self.opts is set at run() )
1195
- genr, empty = await pullone(genx())
1191
+ # must pull through the genr to get opts set
1192
+ # ( many commands expect self.opts is set at run() )
1193
+ genr, empty = await pullone(genx())
1196
1194
 
1197
- try:
1198
- if runtsafe:
1199
- argv = await self.kids[1].compute(runt, None)
1200
- if not await scmd.setArgv(argv):
1201
- raise s_stormctrl.StormExit()
1202
-
1203
- if runtsafe or not empty:
1204
- async with contextlib.aclosing(scmd.execStormCmd(runt, genr)) as agen:
1205
- async for item in agen:
1206
- yield item
1195
+ try:
1196
+ if runtsafe:
1197
+ argv = await self.kids[1].compute(runt, None)
1198
+ if not await scmd.setArgv(argv):
1199
+ raise s_stormctrl.StormExit()
1200
+
1201
+ if runtsafe or not empty:
1202
+ async with contextlib.aclosing(scmd.execStormCmd(runt, genr)) as agen:
1203
+ async for item in agen:
1204
+ yield item
1207
1205
 
1208
- finally:
1209
- await genr.aclose()
1206
+ finally:
1207
+ await genr.aclose()
1210
1208
 
1211
1209
  class SetVarOper(Oper):
1212
1210
 
synapse/lib/base.py CHANGED
@@ -143,6 +143,7 @@ class Base:
143
143
  self._fini_atexit = False
144
144
  self._active_tasks = None # the set of free running tasks associated with me
145
145
  self._context_managers = None # the set of context managers i must fini
146
+ self._syn_signal_tasks = None # initialized as a Set when addSignalHandlers is called.
146
147
 
147
148
  async def postAnit(self):
148
149
  '''
@@ -477,8 +478,6 @@ class Base:
477
478
  asyncio.Task: An asyncio.Task object.
478
479
 
479
480
  '''
480
- import synapse.lib.provenance as s_provenance # avoid import cycle
481
-
482
481
  if __debug__:
483
482
  assert inspect.isawaitable(coro)
484
483
  import synapse.lib.threads as s_threads # avoid import cycle
@@ -491,10 +490,6 @@ class Base:
491
490
 
492
491
  s_scope.clone(task)
493
492
 
494
- # In rare cases, (Like this function being triggered from call_soon_threadsafe), there's no task context
495
- if asyncio.current_task():
496
- s_provenance.dupstack(task)
497
-
498
493
  def taskDone(task):
499
494
  self._active_tasks.remove(task)
500
495
  try:
@@ -565,14 +560,20 @@ class Base:
565
560
  '''
566
561
  Register SIGTERM/SIGINT signal handlers with the ioloop to fini this object.
567
562
  '''
563
+ if self._syn_signal_tasks is None:
564
+ self._syn_signal_tasks = set()
568
565
 
569
566
  def sigterm():
570
- print('Caught SIGTERM, shutting down.')
571
- asyncio.create_task(self.fini())
567
+ logger.warning('Caught SIGTERM, shutting down.')
568
+ task = asyncio.create_task(self.fini())
569
+ self._syn_signal_tasks.add(task)
570
+ task.add_done_callback(self._syn_signal_tasks.discard)
572
571
 
573
572
  def sigint():
574
- print('Caught SIGINT, shutting down.')
575
- asyncio.create_task(self.fini())
573
+ logger.warning('Caught SIGINT, shutting down.')
574
+ task = asyncio.create_task(self.fini())
575
+ self._syn_signal_tasks.add(task)
576
+ task.add_done_callback(self._syn_signal_tasks.discard)
576
577
 
577
578
  loop = asyncio.get_running_loop()
578
579
  loop.add_signal_handler(signal.SIGINT, sigint)
synapse/lib/cell.py CHANGED
@@ -668,26 +668,32 @@ class CellApi(s_base.Base):
668
668
 
669
669
  @adminapi()
670
670
  async def listHiveKey(self, path=None):
671
+ s_common.deprecated('CellApi.listHiveKey', curv='2.167.0')
671
672
  return await self.cell.listHiveKey(path=path)
672
673
 
673
674
  @adminapi(log=True)
674
675
  async def getHiveKeys(self, path):
676
+ s_common.deprecated('CellApi.getHiveKeys', curv='2.167.0')
675
677
  return await self.cell.getHiveKeys(path)
676
678
 
677
679
  @adminapi(log=True)
678
680
  async def getHiveKey(self, path):
681
+ s_common.deprecated('CellApi.getHiveKey', curv='2.167.0')
679
682
  return await self.cell.getHiveKey(path)
680
683
 
681
684
  @adminapi(log=True)
682
685
  async def setHiveKey(self, path, valu):
686
+ s_common.deprecated('CellApi.setHiveKey', curv='2.167.0')
683
687
  return await self.cell.setHiveKey(path, valu)
684
688
 
685
689
  @adminapi(log=True)
686
690
  async def popHiveKey(self, path):
691
+ s_common.deprecated('CellApi.popHiveKey', curv='2.167.0')
687
692
  return await self.cell.popHiveKey(path)
688
693
 
689
694
  @adminapi(log=True)
690
695
  async def saveHiveTree(self, path=()):
696
+ s_common.deprecated('CellApi.saveHiveTree', curv='2.167.0')
691
697
  return await self.cell.saveHiveTree(path=path)
692
698
 
693
699
  @adminapi()
@@ -1419,7 +1425,6 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1419
1425
  if ahaurl is not None:
1420
1426
 
1421
1427
  info = await s_telepath.addAhaUrl(ahaurl)
1422
- self.ahaclient = info.get('client')
1423
1428
  if self.ahaclient is None:
1424
1429
  self.ahaclient = await s_telepath.Client.anit(info.get('url'))
1425
1430
  self.ahaclient._fini_atexit = True
@@ -1560,32 +1565,22 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1560
1565
 
1561
1566
  self.ahasvcname = f'{ahaname}.{ahanetw}'
1562
1567
 
1563
- async def onlink(proxy):
1564
- while not proxy.isfini:
1565
- info = await self.getAhaInfo()
1568
+ async def _runAhaRegLoop():
1569
+
1570
+ while not self.isfini:
1566
1571
  try:
1572
+ proxy = await self.ahaclient.proxy()
1573
+ info = await self.getAhaInfo()
1567
1574
  await proxy.addAhaSvc(ahaname, info, network=ahanetw)
1568
1575
  if self.isactive and ahalead is not None:
1569
1576
  await proxy.addAhaSvc(ahalead, info, network=ahanetw)
1577
+ except Exception as e:
1578
+ logger.exception(f'Error registering service {self.ahasvcname} with AHA: {e}')
1579
+ await self.waitfini(1)
1580
+ else:
1581
+ await proxy.waitfini()
1570
1582
 
1571
- return
1572
-
1573
- except asyncio.CancelledError: # pragma: no cover
1574
- raise
1575
-
1576
- except Exception:
1577
- logger.exception('Error in _initAhaService() onlink')
1578
-
1579
- await proxy.waitfini(1)
1580
-
1581
- async def fini():
1582
- await self.ahaclient.offlink(onlink)
1583
-
1584
- async def init():
1585
- await self.ahaclient.onlink(onlink)
1586
- self.onfini(fini)
1587
-
1588
- self.schedCoro(init())
1583
+ self.schedCoro(_runAhaRegLoop())
1589
1584
 
1590
1585
  async def initServiceRuntime(self):
1591
1586
  pass
@@ -2905,6 +2900,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
2905
2900
  if isnew:
2906
2901
  path = os.path.join(self.dirn, 'hiveboot.yaml')
2907
2902
  if os.path.isfile(path):
2903
+ s_common.deprdate('Initial hive config from hiveboot.yaml', '2024-05-05')
2908
2904
  logger.debug(f'Loading cell hive from {path}')
2909
2905
  tree = s_common.yamlload(path)
2910
2906
  if tree is not None:
@@ -3694,7 +3690,8 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
3694
3690
 
3695
3691
  await self.ahaclient.waitready()
3696
3692
 
3697
- mirrors = await self.ahaclient.getAhaSvcMirrors(self.ahasvcname)
3693
+ proxy = await self.ahaclient.proxy(timeout=5)
3694
+ mirrors = await proxy.getAhaSvcMirrors(self.ahasvcname)
3698
3695
  if mirrors is None:
3699
3696
  mesg = 'Service must be configured with AHA to enumerate mirror URLs'
3700
3697
  raise s_exc.NoSuchName(mesg=mesg, name=self.ahasvcname)
@@ -4088,7 +4085,10 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
4088
4085
  await s_base.Base.addSignalHandlers(self)
4089
4086
 
4090
4087
  def sighup():
4091
- asyncio.create_task(self.reload())
4088
+ logger.info('Caught SIGHUP, running reloadable subsystems.')
4089
+ task = asyncio.create_task(self.reload())
4090
+ self._syn_signal_tasks.add(task)
4091
+ task.add_done_callback(self._syn_signal_tasks.discard)
4092
4092
 
4093
4093
  loop = asyncio.get_running_loop()
4094
4094
  loop.add_signal_handler(signal.SIGHUP, sighup)
synapse/lib/hive.py CHANGED
@@ -473,12 +473,15 @@ class HiveApi(s_base.Base):
473
473
  self.onfini(self._onHapiFini)
474
474
 
475
475
  async def loadHiveTree(self, tree, path=(), trim=False):
476
+ s_common.deprecated('HiveApi.loadHiveTree', curv='2.167.0')
476
477
  return await self.hive.loadHiveTree(tree, path=path, trim=trim)
477
478
 
478
479
  async def saveHiveTree(self, path=()):
480
+ s_common.deprecated('HiveApi.saveHiveTree', curv='2.167.0')
479
481
  return await self.hive.saveHiveTree(path=path)
480
482
 
481
483
  async def treeAndSync(self, path, iden):
484
+ s_common.deprecated('HiveApi.treeAndSync', curv='2.167.0')
482
485
 
483
486
  node = await self.hive.open(path)
484
487
 
@@ -510,16 +513,22 @@ class HiveApi(s_base.Base):
510
513
  return
511
514
 
512
515
  async def setAndSync(self, path, valu, iden, nexs=False):
516
+ s_common.deprecated('HiveApi.setAndSync', curv='2.167.0')
517
+
513
518
  valu = await self.hive.set(path, valu, nexs=nexs)
514
519
  await self.msgq.put(('hive:sync', {'iden': iden}))
515
520
  return valu
516
521
 
517
522
  async def addAndSync(self, path, valu, iden):
523
+ s_common.deprecated('HiveApi.addAndSync', curv='2.167.0')
524
+
518
525
  valu = await self.hive.add(path, valu)
519
526
  await self.msgq.put(('hive:sync', {'iden': iden}))
520
527
  return valu
521
528
 
522
529
  async def popAndSync(self, path, iden, nexs=False):
530
+ s_common.deprecated('HiveApi.popAndSync', curv='2.167.0')
531
+
523
532
  valu = await self.hive.pop(path, nexs=nexs)
524
533
  await self.msgq.put(('hive:sync', {'iden': iden}))
525
534
  return valu
@@ -531,9 +540,11 @@ class HiveApi(s_base.Base):
531
540
  self.msgq.put_nowait(mesg)
532
541
 
533
542
  async def get(self, full):
543
+ s_common.deprecated('HiveApi.get', curv='2.167.0')
534
544
  return await self.hive.get(full)
535
545
 
536
546
  async def edits(self):
547
+ s_common.deprecated('HiveApi.edits', curv='2.167.0')
537
548
 
538
549
  while not self.isfini:
539
550
 
synapse/lib/httpapi.py CHANGED
@@ -1344,6 +1344,7 @@ class ExtApiHandler(StormHandler):
1344
1344
  varz['_http_request_info'] = info
1345
1345
 
1346
1346
  opts = {
1347
+ 'mirror': adef.get('pool', False),
1347
1348
  'readonly': adef.get('readonly'),
1348
1349
  'show': (
1349
1350
  'http:resp:body',
synapse/lib/nexus.py CHANGED
@@ -589,10 +589,11 @@ class NexsRoot(s_base.Base):
589
589
 
590
590
  try:
591
591
  await self.cell.ahaclient.waitready(timeout=5)
592
- ahainfo = await self.cell.ahaclient.getCellInfo()
592
+ proxy = await self.cell.ahaclient.proxy(timeout=5)
593
+ ahainfo = await proxy.getCellInfo()
593
594
  ahavers = ahainfo['synapse']['version']
594
595
  if self.cell.ahasvcname is not None and ahavers >= (2, 95, 0):
595
- await self.cell.ahaclient.modAhaSvcInfo(self.cell.ahasvcname, {'ready': status})
596
+ await proxy.modAhaSvcInfo(self.cell.ahasvcname, {'ready': status})
596
597
 
597
598
  except asyncio.CancelledError: # pragma: no cover TODO: remove once >= py 3.8 only
598
599
  raise
synapse/lib/node.py CHANGED
@@ -472,10 +472,12 @@ class Node:
472
472
  form=self.form.full, tag=tag)
473
473
 
474
474
  pref = name + '.'
475
+ exists = self.tags.get(name, s_common.novalu) is not s_common.novalu
475
476
 
476
477
  todel = [(len(t), t) for t in self.tags.keys() if t.startswith(pref)]
477
478
 
478
- if len(path) > 1:
479
+ # only prune when we're actually deleting a tag
480
+ if len(path) > 1 and exists:
479
481
 
480
482
  parent = '.'.join(path[:-1])
481
483
 
@@ -511,7 +513,7 @@ class Node:
511
513
  edits.append((s_layer.EDIT_TAG_DEL, (subtag, None), ()))
512
514
 
513
515
  edits.extend(self._getTagPropDel(name))
514
- if self.getTag(name, defval=s_common.novalu) is not s_common.novalu:
516
+ if exists:
515
517
  edits.append((s_layer.EDIT_TAG_DEL, (name, None), ()))
516
518
 
517
519
  return edits
synapse/lib/schemas.py CHANGED
@@ -40,6 +40,7 @@ _HttpExtAPIConfSchema = {
40
40
  'name': {'type': 'string', 'default': ''},
41
41
  'desc': {'type': 'string', 'default': ''},
42
42
  'path': {'type': 'string', 'minlen': 1},
43
+ 'pool': {'type': 'boolean', 'default': False},
43
44
  'view': {'type': 'string', 'pattern': s_config.re_iden},
44
45
  'runas': {'type': 'string', 'pattern': '^(owner|user)$'},
45
46
  'owner': {'type': 'string', 'pattern': s_config.re_iden},
@@ -93,6 +94,7 @@ _CronJobSchema = {
93
94
  'iden': {'type': 'string', 'pattern': s_config.re_iden},
94
95
  'view': {'type': 'string', 'pattern': s_config.re_iden},
95
96
  'name': {'type': 'string'},
97
+ 'pool': {'type': 'boolean'},
96
98
  'doc': {'type': 'string'},
97
99
  'incunit': {
98
100
  'oneOf': [
@@ -170,7 +172,7 @@ reqValidView = s_config.getJsValidator({
170
172
  'parent': {'type': ['string', 'null'], 'pattern': s_config.re_iden},
171
173
  'creator': {'type': 'string', 'pattern': s_config.re_iden},
172
174
  'created': {'type': 'integer', 'minimum': 0},
173
- 'nomerge': {'type': 'boolean'},
175
+ 'protected': {'type': 'boolean', 'default': False},
174
176
  'merging': {'type': 'boolean'},
175
177
  'layers': {
176
178
  'type': 'array',
synapse/lib/snap.py CHANGED
@@ -209,6 +209,27 @@ class ProtoNode:
209
209
  if tagnode is not s_common.novalu:
210
210
  return self.ctx.loadNode(tagnode)
211
211
 
212
+ # check for an :isnow tag redirection in our hierarchy...
213
+ toks = info.get('toks')
214
+ for i in range(len(toks)):
215
+
216
+ toktag = '.'.join(toks[:i + 1])
217
+ toknode = await self.ctx.snap.getTagNode(toktag)
218
+ if toknode is s_common.novalu:
219
+ continue
220
+
221
+ tokvalu = toknode.ndef[1]
222
+ if tokvalu == toktag:
223
+ continue
224
+
225
+ realnow = tokvalu + norm[len(toktag):]
226
+ tagnode = await self.ctx.snap.getTagNode(realnow)
227
+ if tagnode is not s_common.novalu:
228
+ return self.ctx.loadNode(tagnode)
229
+
230
+ norm, info = await self.ctx.snap.getTagNorm(realnow)
231
+ break
232
+
212
233
  return await self.ctx.addNode('syn:tag', norm, norminfo=info)
213
234
 
214
235
  def getTag(self, tag):
@@ -657,6 +678,33 @@ class Snap(s_base.Base):
657
678
  self.onfini(runt)
658
679
  return runt
659
680
 
681
+ async def _joinEmbedStor(self, storage, embeds):
682
+ for nodePath, relProps in embeds.items():
683
+ await asyncio.sleep(0)
684
+ iden = relProps.get('*')
685
+ if not iden:
686
+ continue
687
+
688
+ stor = await self.view.getStorNodes(s_common.uhex(iden))
689
+ for relProp in relProps.keys():
690
+ await asyncio.sleep(0)
691
+ if relProp == '*':
692
+ continue
693
+
694
+ for idx, layrstor in enumerate(stor):
695
+ await asyncio.sleep(0)
696
+ props = layrstor.get('props')
697
+ if not props:
698
+ continue
699
+
700
+ if relProp not in props:
701
+ continue
702
+
703
+ if 'embeds' not in storage[idx]:
704
+ storage[idx]['embeds'] = {}
705
+
706
+ storage[idx]['embeds'][f'{nodePath}::{relProp}'] = props[relProp]
707
+
660
708
  async def iterStormPodes(self, text, opts, user=None):
661
709
  '''
662
710
  Yield packed node tuples for the given storm query text.
@@ -699,6 +747,8 @@ class Snap(s_base.Base):
699
747
  embdef = embeds.get(node.form.name)
700
748
  if embdef is not None:
701
749
  pode[1]['embeds'] = await node.getEmbeds(embdef)
750
+ if show_storage:
751
+ await self._joinEmbedStor(pode[1]['storage'], pode[1]['embeds'])
702
752
 
703
753
  yield pode
704
754