synapse 2.176.0__py311-none-any.whl → 2.178.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 (95) hide show
  1. synapse/axon.py +24 -9
  2. synapse/cortex.py +337 -172
  3. synapse/cryotank.py +46 -37
  4. synapse/datamodel.py +17 -4
  5. synapse/exc.py +19 -0
  6. synapse/lib/agenda.py +7 -13
  7. synapse/lib/aha.py +361 -88
  8. synapse/lib/auth.py +1520 -0
  9. synapse/lib/base.py +27 -9
  10. synapse/lib/cell.py +422 -163
  11. synapse/lib/config.py +15 -11
  12. synapse/lib/coro.py +13 -0
  13. synapse/lib/grammar.py +5 -0
  14. synapse/lib/hive.py +24 -3
  15. synapse/lib/hiveauth.py +6 -32
  16. synapse/lib/layer.py +7 -9
  17. synapse/lib/link.py +22 -18
  18. synapse/lib/lmdbslab.py +152 -3
  19. synapse/lib/modelrev.py +1 -1
  20. synapse/lib/nexus.py +24 -12
  21. synapse/lib/schemas.py +136 -0
  22. synapse/lib/storm.py +61 -29
  23. synapse/lib/stormlib/aha.py +1 -1
  24. synapse/lib/stormlib/auth.py +185 -10
  25. synapse/lib/stormlib/cortex.py +16 -5
  26. synapse/lib/stormlib/gen.py +80 -0
  27. synapse/lib/stormlib/imap.py +6 -2
  28. synapse/lib/stormlib/model.py +55 -0
  29. synapse/lib/stormlib/modelext.py +60 -0
  30. synapse/lib/stormlib/smtp.py +12 -2
  31. synapse/lib/stormlib/tabular.py +212 -0
  32. synapse/lib/stormtypes.py +14 -1
  33. synapse/lib/trigger.py +1 -1
  34. synapse/lib/version.py +2 -2
  35. synapse/lib/view.py +55 -28
  36. synapse/models/base.py +7 -0
  37. synapse/models/biz.py +4 -0
  38. synapse/models/files.py +8 -1
  39. synapse/models/inet.py +8 -0
  40. synapse/telepath.py +32 -17
  41. synapse/tests/files/aha/certs/cas/synapse.crt +28 -0
  42. synapse/tests/files/aha/certs/cas/synapse.key +51 -0
  43. synapse/tests/files/aha/certs/hosts/00.aha.loop.vertex.link.crt +30 -0
  44. synapse/tests/files/aha/certs/hosts/00.aha.loop.vertex.link.key +51 -0
  45. synapse/tests/files/aha/certs/users/root@synapse.crt +29 -0
  46. synapse/tests/files/aha/certs/users/root@synapse.key +51 -0
  47. synapse/tests/files/changelog/model_2.176.0_16ee721a6b7221344eaf946c3ab4602dda546b1a.yaml.gz +0 -0
  48. synapse/tests/files/changelog/model_2.176.0_2a25c58bbd344716cd7cbc3f4304d8925b0f4ef2.yaml.gz +0 -0
  49. synapse/tests/files/rstorm/testsvc.py +1 -1
  50. synapse/tests/test_axon.py +8 -5
  51. synapse/tests/test_cortex.py +149 -141
  52. synapse/tests/test_cryotank.py +4 -4
  53. synapse/tests/test_datamodel.py +7 -0
  54. synapse/tests/test_lib_agenda.py +10 -3
  55. synapse/tests/test_lib_aha.py +336 -490
  56. synapse/tests/{test_lib_hiveauth.py → test_lib_auth.py} +314 -11
  57. synapse/tests/test_lib_base.py +20 -0
  58. synapse/tests/test_lib_cell.py +210 -30
  59. synapse/tests/test_lib_config.py +4 -3
  60. synapse/tests/test_lib_httpapi.py +18 -14
  61. synapse/tests/test_lib_layer.py +33 -33
  62. synapse/tests/test_lib_link.py +42 -1
  63. synapse/tests/test_lib_lmdbslab.py +68 -0
  64. synapse/tests/test_lib_nexus.py +12 -4
  65. synapse/tests/test_lib_node.py +0 -7
  66. synapse/tests/test_lib_storm.py +45 -0
  67. synapse/tests/test_lib_stormlib_aha.py +35 -36
  68. synapse/tests/test_lib_stormlib_auth.py +21 -0
  69. synapse/tests/test_lib_stormlib_cell.py +4 -15
  70. synapse/tests/test_lib_stormlib_cortex.py +12 -12
  71. synapse/tests/test_lib_stormlib_gen.py +99 -0
  72. synapse/tests/test_lib_stormlib_imap.py +14 -3
  73. synapse/tests/test_lib_stormlib_model.py +108 -0
  74. synapse/tests/test_lib_stormlib_modelext.py +64 -0
  75. synapse/tests/test_lib_stormlib_smtp.py +51 -0
  76. synapse/tests/test_lib_stormlib_tabular.py +226 -0
  77. synapse/tests/test_lib_stormsvc.py +4 -1
  78. synapse/tests/test_lib_stormtypes.py +10 -0
  79. synapse/tests/test_model_base.py +3 -0
  80. synapse/tests/test_model_biz.py +3 -0
  81. synapse/tests/test_model_files.py +12 -2
  82. synapse/tests/test_model_inet.py +24 -0
  83. synapse/tests/test_tools_aha.py +78 -101
  84. synapse/tests/test_tools_changelog.py +196 -0
  85. synapse/tests/test_tools_healthcheck.py +4 -3
  86. synapse/tests/utils.py +87 -121
  87. synapse/tools/aha/clone.py +50 -0
  88. synapse/tools/aha/enroll.py +2 -1
  89. synapse/tools/backup.py +2 -2
  90. synapse/tools/changelog.py +776 -15
  91. {synapse-2.176.0.dist-info → synapse-2.178.0.dist-info}/METADATA +48 -48
  92. {synapse-2.176.0.dist-info → synapse-2.178.0.dist-info}/RECORD +95 -82
  93. {synapse-2.176.0.dist-info → synapse-2.178.0.dist-info}/WHEEL +1 -1
  94. {synapse-2.176.0.dist-info → synapse-2.178.0.dist-info}/LICENSE +0 -0
  95. {synapse-2.176.0.dist-info → synapse-2.178.0.dist-info}/top_level.txt +0 -0
synapse/lib/config.py CHANGED
@@ -392,10 +392,16 @@ class Config(c_abc.MutableMapping):
392
392
  else:
393
393
  return
394
394
 
395
- def reqConfValu(self, key):
395
+ def reqConfValu(self, key): # pragma: no cover
396
396
  '''
397
- Get a configuration value. If that value is not present in the schema
398
- or is not set, then raise an exception.
397
+ Deprecated. Use ``req(key)`` API instead.
398
+ '''
399
+ s_common.deprecated('Config.reqConfValu(), use req() instead.')
400
+ return self.req(key)
401
+
402
+ def req(self, key):
403
+ '''
404
+ Get a configuration value. If that value is not present in the schema or is not set, then raise an exception.
399
405
 
400
406
  Args:
401
407
  key (str): The key to require.
@@ -403,17 +409,15 @@ class Config(c_abc.MutableMapping):
403
409
  Returns:
404
410
  The requested value.
405
411
  '''
406
- # Ensure that the key is in self.json_schema
407
412
  if key not in self.json_schema.get('properties', {}):
408
- raise s_exc.BadArg(mesg='Required key is not present in the configuration schema.',
409
- key=key)
413
+ raise s_exc.BadArg(mesg=f'The {key} configuration option is not present in the configuration schema.',
414
+ name=key)
410
415
 
411
- # Ensure that the key is present in self.conf
412
- if key not in self.conf:
413
- raise s_exc.NeedConfValu(mesg='Required key is not present in configuration data.',
414
- key=key)
416
+ valu = self.conf.get(key, s_common.novalu)
417
+ if valu is not s_common.novalu:
418
+ return valu
415
419
 
416
- return self.conf.get(key)
420
+ raise s_exc.NeedConfValu(mesg=f'The {key} configuration option is required.', name=key)
417
421
 
418
422
  def reqKeyValid(self, key, value):
419
423
  '''
synapse/lib/coro.py CHANGED
@@ -148,6 +148,19 @@ async def ornot(func, *args, **kwargs):
148
148
  return await retn
149
149
  return retn
150
150
 
151
+ bgtasks = set()
152
+ def create_task(coro):
153
+
154
+ task = asyncio.get_running_loop().create_task(coro)
155
+ bgtasks.add(task)
156
+
157
+ def done(t):
158
+ bgtasks.remove(t)
159
+
160
+ task.add_done_callback(done)
161
+
162
+ return task
163
+
151
164
  class GenrHelp:
152
165
 
153
166
  def __init__(self, genr):
synapse/lib/grammar.py CHANGED
@@ -15,6 +15,8 @@ formrestr = r'[a-z_][a-z0-9_]*(:[a-z0-9_]+)+'
15
15
  formre = regex.compile(formrestr)
16
16
  tagrestr = r'(\w+\.)*\w+'
17
17
  tagre = regex.compile(tagrestr)
18
+ edgerestr = r'[\w\.:]{1,200}'
19
+ edgere = regex.compile(edgerestr)
18
20
  basepropnopivpropstr = r'[a-z_][a-z0-9_]*(?:(\:|\.)[a-z_][a-z0-9_]*)*'
19
21
  basepropnopivpropre = regex.compile(basepropnopivpropstr)
20
22
 
@@ -38,6 +40,9 @@ def isFormName(name):
38
40
  def isBasePropNoPivprop(name):
39
41
  return basepropnopivpropre.fullmatch(name) is not None
40
42
 
43
+ def isEdgeVerb(verb):
44
+ return edgere.fullmatch(verb) is not None
45
+
41
46
  floatre = regex.compile(r'\s*-?\d+(\.\d+)?([eE][-+]\d+)?')
42
47
 
43
48
  def parse_float(text, off):
synapse/lib/hive.py CHANGED
@@ -91,7 +91,7 @@ class Hive(s_nexus.Pusher, s_telepath.Aware):
91
91
  An optionally persistent atomically accessed tree which implements
92
92
  primitives for use in making distributed/clustered services.
93
93
  '''
94
- async def __anit__(self, conf=None, nexsroot=None):
94
+ async def __anit__(self, conf=None, nexsroot=None, cell=None):
95
95
 
96
96
  await s_nexus.Pusher.__anit__(self, 'hive', nexsroot=nexsroot)
97
97
 
@@ -100,6 +100,7 @@ class Hive(s_nexus.Pusher, s_telepath.Aware):
100
100
  if conf is None:
101
101
  conf = {}
102
102
 
103
+ self.cell = cell
103
104
  self.conf = conf
104
105
  self.nodes = {} # full=Node()
105
106
 
@@ -340,6 +341,18 @@ class Hive(s_nexus.Pusher, s_telepath.Aware):
340
341
 
341
342
  @s_nexus.Pusher.onPush('hive:set')
342
343
  async def _set(self, full, valu):
344
+ if self.cell is not None:
345
+ if full[0] == 'auth':
346
+ if len(full) == 5:
347
+ _, _, iden, dtyp, name = full
348
+ if dtyp == 'vars':
349
+ await self.cell.auth._hndlsetUserVarValu(iden, name, valu)
350
+ elif dtyp == 'profile':
351
+ await self.cell.auth._hndlsetUserProfileValu(iden, name, valu)
352
+
353
+ elif full[0] == 'cellvers':
354
+ self.cell.setCellVers(full[-1], valu, nexs=False)
355
+
343
356
  node = await self._getHiveNode(full)
344
357
 
345
358
  oldv = node.valu
@@ -377,6 +390,14 @@ class Hive(s_nexus.Pusher, s_telepath.Aware):
377
390
  @s_nexus.Pusher.onPush('hive:pop')
378
391
  async def _pop(self, full):
379
392
 
393
+ if self.cell is not None and full[0] == 'auth':
394
+ if len(full) == 5:
395
+ _, _, iden, dtyp, name = full
396
+ if dtyp == 'vars':
397
+ await self.cell.auth._hndlpopUserVarValu(iden, name)
398
+ elif dtyp == 'profile':
399
+ await self.cell.auth._hndlpopUserProfileValu(iden, name)
400
+
380
401
  node = self.nodes.get(full)
381
402
  if node is None:
382
403
  return
@@ -434,10 +455,10 @@ class Hive(s_nexus.Pusher, s_telepath.Aware):
434
455
 
435
456
  class SlabHive(Hive):
436
457
 
437
- async def __anit__(self, slab, db=None, conf=None, nexsroot=None):
458
+ async def __anit__(self, slab, db=None, conf=None, nexsroot=None, cell=None):
438
459
  self.db = db
439
460
  self.slab = slab
440
- await Hive.__anit__(self, conf=conf, nexsroot=nexsroot)
461
+ await Hive.__anit__(self, conf=conf, nexsroot=nexsroot, cell=cell)
441
462
  self.slab.onfini(self.fini)
442
463
 
443
464
  async def _storLoadHive(self):
synapse/lib/hiveauth.py CHANGED
@@ -277,7 +277,6 @@ class Auth(s_nexus.Pusher):
277
277
 
278
278
  return user
279
279
 
280
- @s_nexus.Pusher.onPushAuto('user:name')
281
280
  async def setUserName(self, iden, name):
282
281
  if not isinstance(name, str):
283
282
  raise s_exc.BadArg(mesg='setUserName() name must be a string')
@@ -302,7 +301,6 @@ class Auth(s_nexus.Pusher):
302
301
  }
303
302
  await self.feedBeholder('user:name', beheld)
304
303
 
305
- @s_nexus.Pusher.onPushAuto('role:name')
306
304
  async def setRoleName(self, iden, name):
307
305
  if not isinstance(name, str):
308
306
  raise s_exc.BadArg(mesg='setRoleName() name must be a string')
@@ -346,7 +344,6 @@ class Auth(s_nexus.Pusher):
346
344
 
347
345
  await self.fire('cell:beholder', **behold)
348
346
 
349
- @s_nexus.Pusher.onPushAuto('user:info')
350
347
  async def setUserInfo(self, iden, name, valu, gateiden=None, logged=True, mesg=None):
351
348
 
352
349
  user = await self.reqUser(iden)
@@ -373,7 +370,6 @@ class Auth(s_nexus.Pusher):
373
370
  # since any user info *may* effect auth
374
371
  user.clearAuthCache()
375
372
 
376
- @s_nexus.Pusher.onPushAuto('role:info')
377
373
  async def setRoleInfo(self, iden, name, valu, gateiden=None, logged=True, mesg=None):
378
374
  role = await self.reqRole(iden)
379
375
 
@@ -528,7 +524,6 @@ class Auth(s_nexus.Pusher):
528
524
 
529
525
  return user
530
526
 
531
- @s_nexus.Pusher.onPush('user:add')
532
527
  async def _addUser(self, iden, name):
533
528
 
534
529
  user = self.usersbyname.get(name)
@@ -554,7 +549,6 @@ class Auth(s_nexus.Pusher):
554
549
 
555
550
  return self.role(iden)
556
551
 
557
- @s_nexus.Pusher.onPush('role:add')
558
552
  async def _addRole(self, iden, name):
559
553
 
560
554
  role = self.rolesbyname.get(name)
@@ -574,7 +568,6 @@ class Auth(s_nexus.Pusher):
574
568
  await self.reqUser(iden)
575
569
  return await self._push('user:del', iden)
576
570
 
577
- @s_nexus.Pusher.onPush('user:del')
578
571
  async def _delUser(self, iden):
579
572
 
580
573
  if iden == self.rootuser.iden:
@@ -608,7 +601,6 @@ class Auth(s_nexus.Pusher):
608
601
  await self.reqRole(iden)
609
602
  return await self._push('role:del', iden)
610
603
 
611
- @s_nexus.Pusher.onPush('role:del')
612
604
  async def _delRole(self, iden):
613
605
 
614
606
  if iden == self.allrole.iden:
@@ -838,10 +830,7 @@ class HiveRole(HiveRuler):
838
830
  }
839
831
 
840
832
  async def _setRulrInfo(self, name, valu, gateiden=None, nexs=True, mesg=None):
841
- if nexs:
842
- return await self.auth.setRoleInfo(self.iden, name, valu, gateiden=gateiden, mesg=mesg)
843
- else:
844
- return await self.auth._hndlsetRoleInfo(self.iden, name, valu, gateiden=gateiden, logged=nexs, mesg=mesg)
833
+ return await self.auth.setRoleInfo(self.iden, name, valu, gateiden=gateiden, logged=nexs, mesg=mesg)
845
834
 
846
835
  async def setName(self, name):
847
836
  return await self.auth.setRoleName(self.iden, name)
@@ -929,10 +918,7 @@ class HiveUser(HiveRuler):
929
918
  }
930
919
 
931
920
  async def _setRulrInfo(self, name, valu, gateiden=None, nexs=True, mesg=None):
932
- if nexs:
933
- return await self.auth.setUserInfo(self.iden, name, valu, gateiden=gateiden, mesg=mesg)
934
- else:
935
- return await self.auth._hndlsetUserInfo(self.iden, name, valu, gateiden=gateiden, logged=nexs, mesg=mesg)
921
+ return await self.auth.setUserInfo(self.iden, name, valu, gateiden=gateiden, logged=nexs, mesg=mesg)
936
922
 
937
923
  async def setName(self, name):
938
924
  return await self.auth.setUserName(self.iden, name)
@@ -1232,10 +1218,7 @@ class HiveUser(HiveRuler):
1232
1218
 
1233
1219
  roles.remove(role.iden)
1234
1220
  mesg = {'name': 'role:revoke', 'iden': self.iden, 'role': role.pack()}
1235
- if nexs:
1236
- await self.auth.setUserInfo(self.iden, 'roles', roles, mesg=mesg)
1237
- else:
1238
- await self.auth._hndlsetUserInfo(self.iden, 'roles', roles, logged=nexs, mesg=mesg)
1221
+ await self.auth.setUserInfo(self.iden, 'roles', roles, logged=nexs, mesg=mesg)
1239
1222
 
1240
1223
  def isLocked(self):
1241
1224
  return self.info.get('locked')
@@ -1271,18 +1254,12 @@ class HiveUser(HiveRuler):
1271
1254
  async def setAdmin(self, admin, gateiden=None, logged=True):
1272
1255
  if not isinstance(admin, bool):
1273
1256
  raise s_exc.BadArg(mesg='setAdmin requires a boolean')
1274
- if logged:
1275
- await self.auth.setUserInfo(self.iden, 'admin', admin, gateiden=gateiden)
1276
- else:
1277
- await self.auth._hndlsetUserInfo(self.iden, 'admin', admin, gateiden=gateiden, logged=logged)
1257
+ await self.auth.setUserInfo(self.iden, 'admin', admin, gateiden=gateiden, logged=logged)
1278
1258
 
1279
1259
  async def setLocked(self, locked, logged=True):
1280
1260
  if not isinstance(locked, bool):
1281
1261
  raise s_exc.BadArg(mesg='setLocked requires a boolean')
1282
- if logged:
1283
- await self.auth.setUserInfo(self.iden, 'locked', locked)
1284
- else:
1285
- await self.auth._hndlsetUserInfo(self.iden, 'locked', locked, logged=logged)
1262
+ await self.auth.setUserInfo(self.iden, 'locked', locked, logged=logged)
1286
1263
 
1287
1264
  async def setArchived(self, archived):
1288
1265
  if not isinstance(archived, bool):
@@ -1347,7 +1324,4 @@ class HiveUser(HiveRuler):
1347
1324
  shadow = await s_passwd.getShadowV2(passwd=passwd)
1348
1325
  else:
1349
1326
  raise s_exc.BadArg(mesg='Password must be a string')
1350
- if nexs:
1351
- await self.auth.setUserInfo(self.iden, 'passwd', shadow)
1352
- else:
1353
- await self.auth._hndlsetUserInfo(self.iden, 'passwd', shadow, logged=nexs)
1327
+ await self.auth.setUserInfo(self.iden, 'passwd', shadow, logged=nexs)
synapse/lib/layer.py CHANGED
@@ -1431,9 +1431,6 @@ class Layer(s_nexus.Pusher):
1431
1431
  self.growsize = self.layrinfo.get('growsize')
1432
1432
  self.logedits = self.layrinfo.get('logedits')
1433
1433
 
1434
- self.mapasync = core.conf.get('layer:lmdb:map_async')
1435
- self.maxreplaylog = core.conf.get('layer:lmdb:max_replay_log')
1436
-
1437
1434
  # slim hooks to avoid async/fire
1438
1435
  self.nodeAddHook = None
1439
1436
  self.nodeDelHook = None
@@ -2034,7 +2031,7 @@ class Layer(s_nexus.Pusher):
2034
2031
  'stortype': stortype})
2035
2032
 
2036
2033
  async def pack(self):
2037
- ret = self.layrinfo.pack()
2034
+ ret = deepcopy(self.layrinfo)
2038
2035
  if ret.get('mirror'):
2039
2036
  ret['mirror'] = s_urlhelp.sanitizeUrl(ret['mirror'])
2040
2037
  ret['offset'] = await self.getEditIndx()
@@ -2728,8 +2725,6 @@ class Layer(s_nexus.Pusher):
2728
2725
  slabopts = {
2729
2726
  'readahead': True,
2730
2727
  'lockmemory': self.lockmemory,
2731
- 'map_async': self.mapasync,
2732
- 'max_replay_log': self.maxreplaylog,
2733
2728
  }
2734
2729
 
2735
2730
  if self.growsize is not None:
@@ -2810,9 +2805,11 @@ class Layer(s_nexus.Pusher):
2810
2805
 
2811
2806
  # TODO when we can set more props, we may need to parse values.
2812
2807
  if valu is None:
2813
- await self.layrinfo.pop(name)
2808
+ self.layrinfo.pop(name, None)
2814
2809
  else:
2815
- await self.layrinfo.set(name, valu)
2810
+ self.layrinfo[name] = valu
2811
+
2812
+ self.core.layerdefs.set(self.iden, self.layrinfo)
2816
2813
 
2817
2814
  await self.core.feedBeholder('layer:set', {'iden': self.iden, 'name': name, 'valu': valu}, gates=[self.iden])
2818
2815
  return valu
@@ -4453,7 +4450,8 @@ class Layer(s_nexus.Pusher):
4453
4450
 
4454
4451
  @s_nexus.Pusher.onPush('layer:set:modelvers')
4455
4452
  async def _setModelVers(self, vers):
4456
- await self.layrinfo.set('model:version', vers)
4453
+ self.layrinfo['model:version'] = vers
4454
+ self.core.layerdefs.set(self.iden, self.layrinfo)
4457
4455
 
4458
4456
  async def getStorNodes(self):
4459
4457
  '''
synapse/lib/link.py CHANGED
@@ -21,7 +21,7 @@ async def connect(host, port, ssl=None, hostname=None, linkinfo=None):
21
21
  '''
22
22
  Async connect and return a Link().
23
23
  '''
24
- info = {'host': host, 'port': port, 'ssl': ssl, 'hostname': hostname}
24
+ info = {'host': host, 'port': port, 'ssl': ssl, 'hostname': hostname, 'tls': bool(ssl)}
25
25
  if linkinfo is not None:
26
26
  info.update(linkinfo)
27
27
 
@@ -137,6 +137,25 @@ class Link(s_base.Base):
137
137
 
138
138
  self.info = info
139
139
 
140
+ self._addrinfo = {}
141
+ # _addrinfo is populated in this order so that as first hit tls links (prod deployments)
142
+ # then unix links (unit tests with local sockets, container healthchecks, local tools )
143
+ # then tcp links ( unit tests and legacy deployments )
144
+ if self.info.get('tls'):
145
+ self._addrinfo['family'] = 'tls'
146
+ self._addrinfo['addr'] = self.sock.getpeername()
147
+ elif self.info.get('unix'):
148
+ self._addrinfo['family'] = 'unix'
149
+ # Unix sockets don't use getpeername
150
+ self._addrinfo['addr'] = self.sock.getsockname()
151
+ else:
152
+ self._addrinfo['family'] = 'tcp'
153
+ self._addrinfo['addr'] = self.sock.getpeername()
154
+ if self.sock.family == socket.AF_INET:
155
+ self._addrinfo['ipver'] = 'ipv4'
156
+ elif self.sock.family == socket.AF_INET6:
157
+ self._addrinfo['ipver'] = 'ipv6'
158
+
140
159
  self.unpk = s_msgpack.Unpk()
141
160
 
142
161
  async def fini():
@@ -145,7 +164,7 @@ class Link(s_base.Base):
145
164
  self.reader._transport.abort()
146
165
  try:
147
166
  await self.writer.wait_closed()
148
- except (BrokenPipeError, ConnectionResetError) as e:
167
+ except Exception as e:
149
168
  logger.debug('Link error waiting on close: %s', str(e))
150
169
 
151
170
  self.onfini(fini)
@@ -212,22 +231,7 @@ class Link(s_base.Base):
212
231
  '''
213
232
  Get a summary of address information related to the link.
214
233
  '''
215
- ret = {'family': 'tcp',
216
- 'addr': self.sock.getpeername(),
217
- }
218
- # Set family information
219
- if self.info.get('unix'):
220
- ret['family'] = 'unix'
221
- # Unix sockets don't use getpeername
222
- ret['addr'] = self.sock.getsockname()
223
- elif self.info.get('tls'):
224
- ret['family'] = 'tls'
225
- # Set ipver if needed
226
- if self.sock.family == socket.AF_INET:
227
- ret['ipver'] = 'ipv4'
228
- if self.sock.family == socket.AF_INET6:
229
- ret['ipver'] = 'ipv6'
230
- return ret
234
+ return dict(self._addrinfo)
231
235
 
232
236
  async def send(self, byts):
233
237
  self.writer.write(byts)
synapse/lib/lmdbslab.py CHANGED
@@ -166,6 +166,148 @@ class SlabDict:
166
166
  self.set(name, curv)
167
167
  return curv
168
168
 
169
+ class SafeKeyVal:
170
+ '''
171
+ Key/value storage that does not store items in memory and ensures keys are < 512 characters in length.
172
+
173
+ Note:
174
+ The key size limitation includes the length of any prefixes added by
175
+ using getSubKeyVal().
176
+ '''
177
+ def __init__(self, slab, name, prefix=''):
178
+
179
+ self.prefix = prefix
180
+ self._prefix = prefix.encode('utf8')
181
+ self.preflen = len(self._prefix)
182
+
183
+ if self.preflen > 510:
184
+ mesg = 'SafeKeyVal prefix lengths must be < 511 bytes.'
185
+ raise s_exc.BadArg(mesg, prefix=self._prefix[:1024])
186
+
187
+ self.name = name
188
+ self.slab = slab
189
+ self.valudb = slab.initdb(name)
190
+
191
+ def getSubKeyVal(self, prefix):
192
+
193
+ if not prefix or not isinstance(prefix, str):
194
+ mesg = 'SafeKeyVal.getSubKeyVal() requires a string prefix of at least one character.'
195
+ raise s_exc.BadArg(mesg, prefix=prefix)
196
+
197
+ if self.prefix:
198
+ prefix = self.prefix + prefix
199
+
200
+ return SafeKeyVal(self.slab, self.name, prefix=prefix)
201
+
202
+ def reqValidName(self, name):
203
+
204
+ _name = name.encode('utf-8')
205
+
206
+ if self._prefix:
207
+ _name = self._prefix + _name
208
+
209
+ if len(_name) > 511:
210
+ maxlen = 511 - self.preflen
211
+ mesg = f'SafeKeyVal keys with prefix {self.prefix} must be less < {maxlen} bytes in length.'
212
+ raise s_exc.BadArg(mesg, prefix=self.prefix, name=name[:1024])
213
+ return _name
214
+
215
+ def get(self, name, defv=None):
216
+ '''
217
+ Get the value for a key.
218
+
219
+ Note:
220
+ This may only be used for keys < 512 characters in length.
221
+ '''
222
+ name = self.reqValidName(name)
223
+
224
+ if (byts := self.slab.get(name, db=self.valudb)) is None:
225
+ return defv
226
+ return s_msgpack.un(byts)
227
+
228
+ def set(self, name, valu):
229
+
230
+ name = self.reqValidName(name)
231
+
232
+ self.slab.put(name, s_msgpack.en(valu), db=self.valudb)
233
+ return valu
234
+
235
+ def pop(self, name, defv=None):
236
+
237
+ name = self.reqValidName(name)
238
+
239
+ if (byts := self.slab.pop(name, db=self.valudb)) is not None:
240
+ return s_msgpack.un(byts)
241
+ return defv
242
+
243
+ def delete(self, name):
244
+ '''
245
+ Delete a key.
246
+ '''
247
+ name = self.reqValidName(name)
248
+ return self.slab.delete(name, db=self.valudb)
249
+
250
+ async def truncate(self, pref=''):
251
+ '''
252
+ Delete all keys.
253
+ '''
254
+ pref = self.reqValidName(pref)
255
+
256
+ if not pref:
257
+ genr = self.slab.scanKeys(db=self.valudb)
258
+ else:
259
+ genr = self.slab.scanKeysByPref(pref, db=self.valudb)
260
+
261
+ for lkey in genr:
262
+ self.slab.delete(lkey, db=self.valudb)
263
+ await asyncio.sleep(0)
264
+
265
+ def items(self, pref=''):
266
+
267
+ pref = self.reqValidName(pref)
268
+
269
+ if not pref:
270
+ genr = self.slab.scanByFull(db=self.valudb)
271
+ else:
272
+ genr = self.slab.scanByPref(pref, db=self.valudb)
273
+
274
+ if self.prefix:
275
+ for lkey, byts in genr:
276
+ yield lkey[self.preflen:].decode('utf8'), s_msgpack.un(byts)
277
+ return
278
+
279
+ for lkey, byts in genr:
280
+ yield lkey.decode('utf8'), s_msgpack.un(byts)
281
+
282
+ def keys(self, pref=''):
283
+
284
+ pref = self.reqValidName(pref)
285
+
286
+ if not pref:
287
+ genr = self.slab.scanKeys(db=self.valudb)
288
+ else:
289
+ genr = self.slab.scanKeysByPref(pref, db=self.valudb)
290
+
291
+ if self.prefix:
292
+ for lkey in genr:
293
+ yield lkey[self.preflen:].decode('utf8')
294
+ return
295
+
296
+ for lkey in genr:
297
+ yield lkey.decode('utf8')
298
+
299
+ def values(self, pref=''):
300
+
301
+ pref = self.reqValidName(pref)
302
+
303
+ if not pref:
304
+ genr = self.slab.scanByFull(db=self.valudb)
305
+ else:
306
+ genr = self.slab.scanByPref(pref, db=self.valudb)
307
+
308
+ for lkey, byts in genr:
309
+ yield s_msgpack.un(byts)
310
+
169
311
  class SlabAbrv:
170
312
  '''
171
313
  A utility for translating arbitrary bytes into fixed with id bytes
@@ -696,7 +838,7 @@ class Slab(s_base.Base):
696
838
  'recovering': slab.recovering,
697
839
  'maxsize': slab.maxsize,
698
840
  'growsize': slab.growsize,
699
- 'mapasync': slab.mapasync,
841
+ 'mapasync': True,
700
842
 
701
843
  })
702
844
  return retn
@@ -709,6 +851,8 @@ class Slab(s_base.Base):
709
851
  kwargs.setdefault('lockmemory', False)
710
852
  kwargs.setdefault('map_async', True)
711
853
 
854
+ assert kwargs.get('map_async')
855
+
712
856
  opts = kwargs
713
857
 
714
858
  self.path = path
@@ -753,8 +897,6 @@ class Slab(s_base.Base):
753
897
  logger.info(f'SYN_LOCKMEM_DISABLE envar set, skipping lockmem for {self.path}')
754
898
  self.lockmemory = False
755
899
 
756
- self.mapasync = opts.setdefault('map_async', True)
757
-
758
900
  self.mapsize = _mapsizeround(mapsize)
759
901
  if self.maxsize is not None:
760
902
  self.mapsize = min(self.mapsize, self.maxsize)
@@ -845,6 +987,13 @@ class Slab(s_base.Base):
845
987
  self.onfini(mq)
846
988
  return mq
847
989
 
990
+ def getSafeKeyVal(self, name, prefix='', create=True):
991
+ if not create and not self.dbexists(name):
992
+ mesg = f'Database {name=} does not exist.'
993
+ raise s_exc.BadArg(mesg=mesg)
994
+
995
+ return SafeKeyVal(self, name, prefix=prefix)
996
+
848
997
  def statinfo(self):
849
998
  return {
850
999
  'locking_memory': self.locking_memory, # whether the memory lock loop was started and hasn't ended
synapse/lib/modelrev.py CHANGED
@@ -821,7 +821,7 @@ class ModelRev:
821
821
  # that we are not able to rev ourselves and bail...
822
822
 
823
823
  layers = []
824
- for layr in self.core.layers.values():
824
+ for layr in list(self.core.layers.values()):
825
825
 
826
826
  if layr.fresh:
827
827
  await layr.setModelVers(version)