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/cell.py CHANGED
@@ -29,6 +29,7 @@ import synapse.common as s_common
29
29
  import synapse.daemon as s_daemon
30
30
  import synapse.telepath as s_telepath
31
31
 
32
+ import synapse.lib.auth as s_auth
32
33
  import synapse.lib.base as s_base
33
34
  import synapse.lib.boss as s_boss
34
35
  import synapse.lib.coro as s_coro
@@ -50,7 +51,6 @@ import synapse.lib.schemas as s_schemas
50
51
  import synapse.lib.spooled as s_spooled
51
52
  import synapse.lib.urlhelp as s_urlhelp
52
53
  import synapse.lib.version as s_version
53
- import synapse.lib.hiveauth as s_hiveauth
54
54
  import synapse.lib.lmdbslab as s_lmdbslab
55
55
  import synapse.lib.thisplat as s_thisplat
56
56
 
@@ -60,6 +60,8 @@ import synapse.tools.backup as s_t_backup
60
60
 
61
61
  logger = logging.getLogger(__name__)
62
62
 
63
+ NEXUS_VERSION = (2, 177)
64
+
63
65
  SLAB_MAP_SIZE = 128 * s_const.mebibyte
64
66
  SSLCTX_CACHE_SIZE = 64
65
67
 
@@ -891,6 +893,10 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
891
893
  'type': 'object',
892
894
  'hideconf': True,
893
895
  },
896
+ 'auth:passwd:policy': {
897
+ 'description': 'Specify password policy/complexity requirements.',
898
+ 'type': 'object',
899
+ },
894
900
  'max:users': {
895
901
  'default': 0,
896
902
  'description': 'Maximum number of users allowed on system, not including root or locked/archived users (0 is no limit).',
@@ -904,7 +910,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
904
910
  },
905
911
  'nexslog:async': {
906
912
  'default': True,
907
- 'description': 'Set to false to disable async memory mapping of the nexus change log.',
913
+ 'description': 'Deprecated. This option ignored.',
908
914
  'type': 'boolean',
909
915
  'hidedocs': True,
910
916
  'hidecmdl': True,
@@ -961,7 +967,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
961
967
  'type': 'string',
962
968
  },
963
969
  'aha:network': {
964
- 'description': 'The AHA service network. This makes aha:name/aha:leader relative names.',
970
+ 'description': 'The AHA service network.',
965
971
  'type': 'string',
966
972
  },
967
973
  'aha:registry': {
@@ -1105,6 +1111,9 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1105
1111
  self._checkspace = s_coro.Event()
1106
1112
  self._reloadfuncs = {} # name -> func
1107
1113
 
1114
+ self.nexslock = asyncio.Lock()
1115
+ self.netready = asyncio.Event()
1116
+
1108
1117
  self.conf = self._initCellConf(conf)
1109
1118
 
1110
1119
  self.minfree = self.conf.get('limit:disk:free')
@@ -1175,23 +1184,32 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1175
1184
  self.backlastexc = None # err, errmsg, errtrace of last backup
1176
1185
 
1177
1186
  if self.conf.get('mirror') and not self.conf.get('nexslog:en'):
1178
- mesg = 'Mirror mode requires nexslog:en=True'
1179
- raise s_exc.BadConfValu(mesg=mesg)
1187
+ self.modCellConf({'nexslog:en': True})
1180
1188
 
1181
- # construct our nexsroot instance ( but do not start it )
1182
1189
  await s_nexus.Pusher.__anit__(self, self.iden)
1183
1190
 
1184
1191
  self._initCertDir()
1185
1192
 
1186
- root = await self._ctorNexsRoot()
1193
+ await self.enter_context(s_telepath.loadTeleCell(self.dirn))
1194
+
1195
+ await self._initCellSlab(readonly=readonly)
1196
+
1197
+ # initialize network daemons (but do not listen yet)
1198
+ # to allow registration of callbacks and shared objects
1199
+ await self._initCellHttp()
1200
+ await self._initCellDmon()
1201
+
1202
+ await self.initServiceEarly()
1187
1203
 
1188
- # mutually assured destruction with our nexs root
1189
- self.onfini(root.fini)
1190
- root.onfini(self.fini)
1204
+ nexsroot = await self._ctorNexsRoot()
1191
1205
 
1192
- self.setNexsRoot(root)
1206
+ self.setNexsRoot(nexsroot)
1207
+
1208
+ async def fini():
1209
+ await self.nexsroot.fini()
1210
+
1211
+ self.onfini(fini)
1193
1212
 
1194
- await self._initCellSlab(readonly=readonly)
1195
1213
  self.apikeydb = self.slab.initdb('user:apikeys') # apikey -> useriden
1196
1214
  self.usermetadb = self.slab.initdb('user:meta') # useriden + <valu> -> dict valu
1197
1215
  self.rolemetadb = self.slab.initdb('role:meta') # roleiden + <valu> -> dict valu
@@ -1203,10 +1221,15 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1203
1221
 
1204
1222
  self.hive = await self._initCellHive()
1205
1223
 
1206
- # self.cellinfo, a HiveDict for general purpose persistent storage
1207
- node = await self.hive.open(('cellinfo',))
1208
- self.cellinfo = await node.dict()
1209
- self.onfini(node)
1224
+ self.cellinfo = self.slab.getSafeKeyVal('cell:info')
1225
+ self.cellvers = self.slab.getSafeKeyVal('cell:vers')
1226
+
1227
+ await self._bumpCellVers('cell:storage', (
1228
+ (1, self._storCellHiveMigration),
1229
+ ), nexs=False)
1230
+
1231
+ if self.inaugural:
1232
+ self.cellinfo.set('nexus:version', NEXUS_VERSION)
1210
1233
 
1211
1234
  # Check the cell version didn't regress
1212
1235
  if (lastver := self.cellinfo.get('cell:version')) is not None and self.VERSION < lastver:
@@ -1214,7 +1237,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1214
1237
  logger.error(mesg)
1215
1238
  raise s_exc.BadVersion(mesg=mesg, currver=self.VERSION, lastver=lastver)
1216
1239
 
1217
- await self.cellinfo.set('cell:version', self.VERSION)
1240
+ self.cellinfo.set('cell:version', self.VERSION)
1218
1241
 
1219
1242
  # Check the synapse version didn't regress
1220
1243
  if (lastver := self.cellinfo.get('synapse:version')) is not None and s_version.version < lastver:
@@ -1222,10 +1245,14 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1222
1245
  logger.error(mesg)
1223
1246
  raise s_exc.BadVersion(mesg=mesg, currver=s_version.version, lastver=lastver)
1224
1247
 
1225
- await self.cellinfo.set('synapse:version', s_version.version)
1248
+ self.cellinfo.set('synapse:version', s_version.version)
1226
1249
 
1227
- node = await self.hive.open(('cellvers',))
1228
- self.cellvers = await node.dict(nexs=True)
1250
+ self.nexsvers = self.cellinfo.get('nexus:version', (0, 0))
1251
+ self.nexspatches = ()
1252
+
1253
+ await self._bumpCellVers('cell:storage', (
1254
+ (2, self._storCellAuthMigration),
1255
+ ), nexs=False)
1229
1256
 
1230
1257
  self.auth = await self._initCellAuth()
1231
1258
 
@@ -1233,8 +1260,8 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1233
1260
  if auth_passwd is not None:
1234
1261
  user = await self.auth.getUserByName('root')
1235
1262
 
1236
- if not await user.tryPasswd(auth_passwd, nexs=False):
1237
- await user.setPasswd(auth_passwd, nexs=False)
1263
+ if not await user.tryPasswd(auth_passwd, nexs=False, enforce_policy=False):
1264
+ await user.setPasswd(auth_passwd, nexs=False, enforce_policy=False)
1238
1265
 
1239
1266
  self.boss = await s_boss.Boss.anit()
1240
1267
  self.onfini(self.boss)
@@ -1244,11 +1271,6 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1244
1271
  'cell': self
1245
1272
  }
1246
1273
 
1247
- # a tuple of (vers, func) tuples
1248
- # it is expected that this is set by
1249
- # initServiceStorage
1250
- self.cellupdaters = ()
1251
-
1252
1274
  self.permdefs = None
1253
1275
  self.permlook = None
1254
1276
 
@@ -1262,17 +1284,13 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1262
1284
  # initialize network backend infrastructure
1263
1285
  await self._initAhaRegistry()
1264
1286
 
1265
- # initialize network daemons (but do not listen yet)
1266
- # to allow registration of callbacks and shared objects
1267
- # within phase 2/4.
1268
- await self._initCellHttp()
1269
- await self._initCellDmon()
1270
-
1271
1287
  # phase 2 - service storage
1272
1288
  await self.initServiceStorage()
1273
1289
  # phase 3 - nexus subsystem
1274
1290
  await self.initNexusSubsystem()
1275
1291
 
1292
+ await self.configNexsVers()
1293
+
1276
1294
  # We can now do nexus-safe operations
1277
1295
  await self._initInauguralConfig()
1278
1296
 
@@ -1281,6 +1299,128 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1281
1299
  # phase 5 - service networking
1282
1300
  await self.initServiceNetwork()
1283
1301
 
1302
+ async def _storCellHiveMigration(self):
1303
+ logger.warning(f'migrating Cell ({self.getCellType()}) info out of hive')
1304
+
1305
+ async with await self.hive.open(('cellvers',)) as versnode:
1306
+ versdict = await versnode.dict()
1307
+ for key, valu in versdict.items():
1308
+ self.cellvers.set(key, valu)
1309
+
1310
+ async with await self.hive.open(('cellinfo',)) as infonode:
1311
+ infodict = await infonode.dict()
1312
+ for key, valu in infodict.items():
1313
+ self.cellinfo.set(key, valu)
1314
+
1315
+ logger.warning(f'...Cell ({self.getCellType()}) info migration complete!')
1316
+
1317
+ async def _storCellAuthMigration(self):
1318
+ if self.conf.get('auth:ctor') is not None:
1319
+ return
1320
+
1321
+ logger.warning(f'migrating Cell ({self.getCellType()}) auth out of hive')
1322
+
1323
+ authkv = self.slab.getSafeKeyVal('auth')
1324
+
1325
+ async with await self.hive.open(('auth',)) as rootnode:
1326
+
1327
+ rolekv = authkv.getSubKeyVal('role:info:')
1328
+ rolenamekv = authkv.getSubKeyVal('role:name:')
1329
+
1330
+ async with await rootnode.open(('roles',)) as roles:
1331
+ for iden, node in roles:
1332
+ roledict = await node.dict()
1333
+ roleinfo = roledict.pack()
1334
+
1335
+ roleinfo['iden'] = iden
1336
+ roleinfo['name'] = node.valu
1337
+ roleinfo['authgates'] = {}
1338
+ roleinfo.setdefault('admin', False)
1339
+ roleinfo.setdefault('rules', ())
1340
+
1341
+ rolekv.set(iden, roleinfo)
1342
+ rolenamekv.set(node.valu, iden)
1343
+
1344
+ userkv = authkv.getSubKeyVal('user:info:')
1345
+ usernamekv = authkv.getSubKeyVal('user:name:')
1346
+
1347
+ async with await rootnode.open(('users',)) as users:
1348
+ for iden, node in users:
1349
+ userdict = await node.dict()
1350
+ userinfo = userdict.pack()
1351
+
1352
+ userinfo['iden'] = iden
1353
+ userinfo['name'] = node.valu
1354
+ userinfo['authgates'] = {}
1355
+ userinfo.setdefault('admin', False)
1356
+ userinfo.setdefault('rules', ())
1357
+ userinfo.setdefault('locked', False)
1358
+ userinfo.setdefault('passwd', None)
1359
+ userinfo.setdefault('archived', False)
1360
+
1361
+ realroles = []
1362
+ for userrole in userinfo.get('roles', ()):
1363
+ if rolekv.get(userrole) is None:
1364
+ mesg = f'Unknown role {userrole} on user {iden} during migration, ignoring.'
1365
+ logger.warning(mesg)
1366
+ continue
1367
+
1368
+ realroles.append(userrole)
1369
+
1370
+ userinfo['roles'] = tuple(realroles)
1371
+
1372
+ userkv.set(iden, userinfo)
1373
+ usernamekv.set(node.valu, iden)
1374
+
1375
+ varskv = authkv.getSubKeyVal(f'user:{iden}:vars:')
1376
+ async with await node.open(('vars',)) as varnodes:
1377
+ for name, varnode in varnodes:
1378
+ varskv.set(name, varnode.valu)
1379
+
1380
+ profkv = authkv.getSubKeyVal(f'user:{iden}:profile:')
1381
+ async with await node.open(('profile',)) as profnodes:
1382
+ for name, profnode in profnodes:
1383
+ profkv.set(name, profnode.valu)
1384
+
1385
+ gatekv = authkv.getSubKeyVal('gate:info:')
1386
+ async with await rootnode.open(('authgates',)) as authgates:
1387
+ for gateiden, node in authgates:
1388
+ gateinfo = {
1389
+ 'iden': gateiden,
1390
+ 'type': node.valu
1391
+ }
1392
+ gatekv.set(gateiden, gateinfo)
1393
+
1394
+ async with await node.open(('users',)) as usernodes:
1395
+ for useriden, usernode in usernodes:
1396
+ if (user := userkv.get(useriden)) is None:
1397
+ mesg = f'Unknown user {useriden} on gate {gateiden} during migration, ignoring.'
1398
+ logger.warning(mesg)
1399
+ continue
1400
+
1401
+ userinfo = await usernode.dict()
1402
+ userdict = userinfo.pack()
1403
+ authkv.set(f'gate:{gateiden}:user:{useriden}', userdict)
1404
+
1405
+ user['authgates'][gateiden] = userdict
1406
+ userkv.set(useriden, user)
1407
+
1408
+ async with await node.open(('roles',)) as rolenodes:
1409
+ for roleiden, rolenode in rolenodes:
1410
+ if (role := rolekv.get(roleiden)) is None:
1411
+ mesg = f'Unknown role {roleiden} on gate {gateiden} during migration, ignoring.'
1412
+ logger.warning(mesg)
1413
+ continue
1414
+
1415
+ roleinfo = await rolenode.dict()
1416
+ roledict = roleinfo.pack()
1417
+ authkv.set(f'gate:{gateiden}:role:{roleiden}', roledict)
1418
+
1419
+ role['authgates'][gateiden] = roledict
1420
+ rolekv.set(roleiden, role)
1421
+
1422
+ logger.warning(f'...Cell ({self.getCellType()}) auth migration complete!')
1423
+
1284
1424
  def getPermDef(self, perm):
1285
1425
  perm = tuple(perm)
1286
1426
  if self.permlook is None:
@@ -1412,10 +1552,52 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1412
1552
  # ( and do so using _bumpCellVers )
1413
1553
  pass
1414
1554
 
1555
+ async def setNexsVers(self, vers):
1556
+ if self.nexsvers < NEXUS_VERSION:
1557
+ await self._push('nexs:vers:set', NEXUS_VERSION)
1558
+
1559
+ @s_nexus.Pusher.onPush('nexs:vers:set')
1560
+ async def _setNexsVers(self, vers):
1561
+ if vers > self.nexsvers:
1562
+ self.cellvers.set('nexus:version', vers)
1563
+ self.nexsvers = vers
1564
+ await self.configNexsVers()
1565
+
1566
+ async def configNexsVers(self):
1567
+ for meth, orig in self.nexspatches:
1568
+ setattr(self, meth, orig)
1569
+
1570
+ if self.nexsvers == NEXUS_VERSION:
1571
+ return
1572
+
1573
+ patches = []
1574
+ if self.nexsvers < (2, 177):
1575
+ patches.extend([
1576
+ ('popUserVarValu', self._popUserVarValuV0),
1577
+ ('setUserVarValu', self._setUserVarValuV0),
1578
+ ('popUserProfInfo', self._popUserProfInfoV0),
1579
+ ('setUserProfInfo', self._setUserProfInfoV0),
1580
+ ])
1581
+
1582
+ self.nexspatches = []
1583
+ for meth, repl in patches:
1584
+ self.nexspatches.append((meth, getattr(self, meth)))
1585
+ setattr(self, meth, repl)
1586
+
1587
+ async def setCellVers(self, name, vers, nexs=True):
1588
+ if nexs:
1589
+ await self._push('cell:vers:set', name, vers)
1590
+ else:
1591
+ await self._setCellVers(name, vers)
1592
+
1593
+ @s_nexus.Pusher.onPush('cell:vers:set')
1594
+ async def _setCellVers(self, name, vers):
1595
+ self.cellvers.set(name, vers)
1596
+
1415
1597
  async def _bumpCellVers(self, name, updates, nexs=True):
1416
1598
 
1417
1599
  if self.inaugural:
1418
- await self.cellvers.set(name, updates[-1][0], nexs=nexs)
1600
+ await self.setCellVers(name, updates[-1][0], nexs=nexs)
1419
1601
  return
1420
1602
 
1421
1603
  curv = self.cellvers.get(name, 0)
@@ -1427,7 +1609,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1427
1609
 
1428
1610
  await callback()
1429
1611
 
1430
- await self.cellvers.set(name, vers, nexs=nexs)
1612
+ await self.setCellVers(name, vers, nexs=nexs)
1431
1613
 
1432
1614
  curv = vers
1433
1615
 
@@ -1436,10 +1618,10 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1436
1618
 
1437
1619
  async def _runFreeSpaceLoop(self):
1438
1620
 
1439
- nexsroot = self.getCellNexsRoot()
1440
-
1441
1621
  while not self.isfini:
1442
1622
 
1623
+ nexsroot = self.getCellNexsRoot()
1624
+
1443
1625
  self._checkspace.clear()
1444
1626
 
1445
1627
  disk = shutil.disk_usage(self.dirn)
@@ -1486,37 +1668,61 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1486
1668
 
1487
1669
  await self.waitfini(self.SYSCTL_CHECK_FREQ)
1488
1670
 
1489
- def _getAhaAdmin(self):
1490
- name = self.conf.get('aha:admin')
1491
- if name is not None:
1492
- return name
1493
-
1494
1671
  async def _initAhaRegistry(self):
1495
1672
 
1496
- ahaurl = self.conf.get('aha:registry')
1497
- if ahaurl is not None:
1673
+ ahaurls = self.conf.get('aha:registry')
1674
+ if ahaurls is not None:
1675
+
1676
+ await s_telepath.addAhaUrl(ahaurls)
1677
+ if self.ahaclient is not None:
1678
+ await self.ahaclient.fini()
1679
+
1680
+ async def onlink(proxy):
1681
+ ahauser = self.conf.get('aha:user', 'root')
1682
+ newurls = await proxy.getAhaUrls(user=ahauser)
1683
+ oldurls = self.conf.get('aha:registry')
1684
+ if isinstance(oldurls, str):
1685
+ oldurls = (oldurls,)
1686
+ elif isinstance(oldurls, list):
1687
+ oldurls = tuple(oldurls)
1688
+ if newurls and newurls != oldurls:
1689
+ if oldurls[0].startswith('tcp://'):
1690
+ s_common.deprecated('aha:registry: tcp:// client values.')
1691
+ logger.warning('tcp:// based aha:registry options are deprecated and will be removed in Synapse v3.0.0')
1692
+ return
1498
1693
 
1499
- info = await s_telepath.addAhaUrl(ahaurl)
1500
- if self.ahaclient is None:
1501
- self.ahaclient = await s_telepath.Client.anit(info.get('url'))
1502
- self.ahaclient._fini_atexit = True
1503
- self.onfini(self.ahaclient)
1694
+ self.modCellConf({'aha:registry': newurls})
1695
+ self.ahaclient.setBootUrls(newurls)
1504
1696
 
1505
- async def finiaha():
1506
- await s_telepath.delAhaUrl(ahaurl)
1697
+ self.ahaclient = await s_telepath.Client.anit(ahaurls, onlink=onlink)
1698
+ self.onfini(self.ahaclient)
1507
1699
 
1508
- self.onfini(finiaha)
1700
+ async def fini():
1701
+ await s_telepath.delAhaUrl(ahaurls)
1509
1702
 
1703
+ self.ahaclient.onfini(fini)
1704
+
1705
+ ahaadmin = self.conf.get('aha:admin')
1510
1706
  ahauser = self.conf.get('aha:user')
1511
- ahanetw = self.conf.get('aha:network')
1512
1707
 
1513
- ahaadmin = self._getAhaAdmin()
1514
1708
  if ahaadmin is not None:
1515
1709
  await self._addAdminUser(ahaadmin)
1516
1710
 
1517
1711
  if ahauser is not None:
1518
1712
  await self._addAdminUser(ahauser)
1519
1713
 
1714
+ def _getDmonListen(self):
1715
+
1716
+ lisn = self.conf.get('dmon:listen', s_common.novalu)
1717
+ if lisn is not s_common.novalu:
1718
+ return lisn
1719
+
1720
+ ahaname = self.conf.get('aha:name')
1721
+ ahanetw = self.conf.get('aha:network')
1722
+ if ahaname is not None and ahanetw is not None:
1723
+ hostname = f'{ahaname}.{ahanetw}'
1724
+ return f'ssl://0.0.0.0:0?hostname={hostname}&ca={ahanetw}'
1725
+
1520
1726
  async def _addAdminUser(self, username):
1521
1727
  # add the user in a pre-nexus compatible way
1522
1728
  user = await self.auth.getUserByName(username)
@@ -1532,6 +1738,9 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1532
1738
  if user.isLocked():
1533
1739
  await user.setLocked(False, logged=False)
1534
1740
 
1741
+ async def initServiceEarly(self):
1742
+ pass
1743
+
1535
1744
  async def initServiceStorage(self):
1536
1745
  pass
1537
1746
 
@@ -1544,7 +1753,11 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1544
1753
  if self.minfree is not None:
1545
1754
  self.schedCoro(self._runFreeSpaceLoop())
1546
1755
 
1547
- async def initServiceNetwork(self):
1756
+ async def _bindDmonListen(self):
1757
+
1758
+ # functionalized so downstream code can bind early.
1759
+ if self.sockaddr is not None:
1760
+ return
1548
1761
 
1549
1762
  # start a unix local socket daemon listener
1550
1763
  sockpath = os.path.join(self.dirn, 'sock')
@@ -1552,24 +1765,25 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1552
1765
 
1553
1766
  try:
1554
1767
  await self.dmon.listen(sockurl)
1555
- except asyncio.CancelledError: # pragma: no cover TODO: remove once >= py 3.8 only
1556
- raise
1557
1768
  except OSError as e:
1558
1769
  logger.error(f'Failed to listen on unix socket at: [{sockpath}][{e}]')
1559
1770
  logger.error('LOCAL UNIX SOCKET WILL BE UNAVAILABLE')
1560
1771
  except Exception: # pragma: no cover
1561
1772
  logging.exception('Unknown dmon listen error.')
1562
- raise
1563
1773
 
1564
- self.sockaddr = None
1565
-
1566
- turl = self.conf.get('dmon:listen')
1774
+ turl = self._getDmonListen()
1567
1775
  if turl is not None:
1568
- self.sockaddr = await self.dmon.listen(turl)
1569
1776
  logger.info(f'dmon listening: {turl}')
1777
+ self.sockaddr = await self.dmon.listen(turl)
1778
+
1779
+ async def initServiceNetwork(self):
1780
+
1781
+ await self._bindDmonListen()
1570
1782
 
1571
1783
  await self._initAhaService()
1572
1784
 
1785
+ self.netready.set()
1786
+
1573
1787
  port = self.conf.get('https:port')
1574
1788
  if port is not None:
1575
1789
  await self.addHttpsPort(port)
@@ -1628,27 +1842,34 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1628
1842
  if ahaname is None:
1629
1843
  return
1630
1844
 
1631
- ahalead = self.conf.get('aha:leader')
1632
1845
  ahanetw = self.conf.get('aha:network')
1846
+ if ahanetw is None:
1847
+ return
1633
1848
 
1634
1849
  ahainfo = await self.getAhaInfo()
1635
1850
  if ahainfo is None:
1636
1851
  return
1637
1852
 
1853
+ ahalead = self.conf.get('aha:leader')
1854
+
1638
1855
  self.ahasvcname = f'{ahaname}.{ahanetw}'
1639
1856
 
1640
1857
  async def _runAhaRegLoop():
1641
1858
 
1642
1859
  while not self.isfini:
1860
+
1643
1861
  try:
1644
1862
  proxy = await self.ahaclient.proxy()
1863
+
1645
1864
  info = await self.getAhaInfo()
1646
1865
  await proxy.addAhaSvc(ahaname, info, network=ahanetw)
1647
1866
  if self.isactive and ahalead is not None:
1648
1867
  await proxy.addAhaSvc(ahalead, info, network=ahanetw)
1868
+
1649
1869
  except Exception as e:
1650
1870
  logger.exception(f'Error registering service {self.ahasvcname} with AHA: {e}')
1651
1871
  await self.waitfini(1)
1872
+
1652
1873
  else:
1653
1874
  await proxy.waitfini()
1654
1875
 
@@ -1729,6 +1950,11 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1729
1950
  async def setNexsIndx(self, indx):
1730
1951
  return await self.nexsroot.setindex(indx)
1731
1952
 
1953
+ def getMyUrl(self, user='root'):
1954
+ host = self.conf.req('aha:name')
1955
+ network = self.conf.req('aha:network')
1956
+ return f'aha://{host}.{network}'
1957
+
1732
1958
  async def promote(self, graceful=False):
1733
1959
  '''
1734
1960
  Transform this cell from a passive follower to
@@ -1739,48 +1965,39 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1739
1965
  mesg = 'promote() called on non-mirror'
1740
1966
  raise s_exc.BadConfValu(mesg=mesg)
1741
1967
 
1742
- ahaname = self.conf.get('aha:name')
1743
- logger.warning(f'PROMOTION: Performing leadership promotion graceful={graceful} ahaname={ahaname}')
1968
+ _dispname = f' ahaname={self.conf.get("aha:name")}' if self.conf.get('aha:name') else ''
1969
+ logger.warning(f'PROMOTION: Performing leadership promotion graceful={graceful}{_dispname}.')
1744
1970
 
1745
1971
  if graceful:
1746
1972
 
1747
- if ahaname is None: # pragma: no cover
1748
- mesg = 'Cannot gracefully promote without aha:name configured.'
1749
- raise s_exc.BadArg(mesg=mesg)
1750
-
1751
- ahanetw = self.conf.get('aha:network')
1752
- if ahanetw is None: # pragma: no cover
1753
- mesg = 'Cannot gracefully promote without aha:network configured.'
1754
- raise s_exc.BadArg(mesg=mesg)
1973
+ myurl = self.getMyUrl()
1755
1974
 
1756
- myurl = f'aha://{ahaname}.{ahanetw}'
1757
- logger.debug(f'PROMOTION: Connecting to {mirurl} to request leadership handoff to ahaname={ahaname}')
1975
+ logger.debug(f'PROMOTION: Connecting to {mirurl} to request leadership handoff{_dispname}.')
1758
1976
  async with await s_telepath.openurl(mirurl) as lead:
1759
- logger.debug(f'PROMOTION: Requesting leadership handoff to ahaname={ahaname}')
1760
1977
  await lead.handoff(myurl)
1761
- logger.warning(f'PROMOTION: Completed leadership handoff to ahaname={ahaname}')
1978
+ logger.warning(f'PROMOTION: Completed leadership handoff to {myurl}{_dispname}')
1762
1979
  return
1763
1980
 
1764
- logger.debug(f'PROMOTION: Clearing mirror configuration for ahaname={ahaname}')
1981
+ logger.debug(f'PROMOTION: Clearing mirror configuration{_dispname}.')
1765
1982
  self.modCellConf({'mirror': None})
1766
1983
 
1767
- logger.debug(f'PROMOTION: Promoting the nexus root for ahaname={ahaname}')
1984
+ logger.debug(f'PROMOTION: Promoting the nexus root{_dispname}.')
1768
1985
  await self.nexsroot.promote()
1769
1986
 
1770
- logger.debug(f'PROMOTION: Setting the cell as active ahaname={ahaname}')
1987
+ logger.debug(f'PROMOTION: Setting the cell as active{_dispname}.')
1771
1988
  await self.setCellActive(True)
1772
1989
 
1773
- logger.warning(f'PROMOTION: Finished leadership promotion ahaname={ahaname}')
1990
+ logger.warning(f'PROMOTION: Finished leadership promotion{_dispname}.')
1774
1991
 
1775
1992
  async def handoff(self, turl, timeout=30):
1776
1993
  '''
1777
1994
  Hand off leadership to a mirror in a transactional fashion.
1778
1995
  '''
1779
- ahaname = self.conf.get("aha:name")
1780
- logger.warning(f'HANDOFF: Performing leadership handoff to {s_urlhelp.sanitizeUrl(turl)} from ahaname={ahaname}')
1996
+ _dispname = f' ahaname={self.conf.get("aha:name")}' if self.conf.get('aha:name') else ''
1997
+ logger.warning(f'HANDOFF: Performing leadership handoff to {s_urlhelp.sanitizeUrl(turl)}{_dispname}.')
1781
1998
  async with await s_telepath.openurl(turl) as cell:
1782
1999
 
1783
- logger.debug(f'HANDOFF: Connected to {s_urlhelp.sanitizeUrl(turl)} from ahaname={ahaname}')
2000
+ logger.debug(f'HANDOFF: Connected to {s_urlhelp.sanitizeUrl(turl)}{_dispname}.')
1784
2001
 
1785
2002
  if self.iden != await cell.getCellIden(): # pragma: no cover
1786
2003
  mesg = 'Mirror handoff remote cell iden does not match!'
@@ -1790,33 +2007,34 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1790
2007
  mesg = 'Cannot handoff mirror leadership to myself!'
1791
2008
  raise s_exc.BadArg(mesg=mesg)
1792
2009
 
1793
- logger.debug(f'HANDOFF: Obtaining nexus applylock ahaname={ahaname}')
2010
+ logger.debug(f'HANDOFF: Obtaining nexus lock{_dispname}.')
1794
2011
 
1795
- async with self.nexsroot.applylock:
2012
+ async with self.nexslock:
1796
2013
 
1797
- logger.debug(f'HANDOFF: Obtained nexus applylock ahaname={ahaname}')
2014
+ logger.debug(f'HANDOFF: Obtained nexus lock{_dispname}.')
1798
2015
  indx = await self.getNexsIndx()
1799
2016
 
1800
- logger.debug(f'HANDOFF: Waiting {timeout} seconds for mirror to reach {indx=}, ahaname={ahaname}')
2017
+ logger.debug(f'HANDOFF: Waiting {timeout} seconds for mirror to reach {indx=}{_dispname}.')
1801
2018
  if not await cell.waitNexsOffs(indx - 1, timeout=timeout): # pragma: no cover
1802
2019
  mndx = await cell.getNexsIndx()
1803
2020
  mesg = f'Remote mirror did not catch up in time: {mndx}/{indx}.'
1804
2021
  raise s_exc.NotReady(mesg=mesg)
1805
2022
 
1806
- logger.debug(f'HANDOFF: Mirror has caught up to the current leader, performing promotion ahaname={ahaname}')
2023
+ logger.debug(f'HANDOFF: Mirror has caught up to the current leader, performing promotion{_dispname}.')
1807
2024
  await cell.promote()
1808
2025
 
1809
- logger.debug(f'HANDOFF: Setting the service as inactive ahaname={ahaname}')
2026
+ logger.debug(f'HANDOFF: Setting the service as inactive{_dispname}.')
1810
2027
  await self.setCellActive(False)
1811
2028
 
1812
- logger.debug(f'HANDOFF: Configuring service to use the new leader as its mirror ahaname={ahaname}')
2029
+ logger.debug(f'HANDOFF: Configuring service to sync from new leader{_dispname}.')
1813
2030
  self.modCellConf({'mirror': turl})
1814
2031
 
1815
- logger.debug(f'HANDOFF: Restarting the nexus ahaname={ahaname}')
2032
+ logger.debug(f'HANDOFF: Restarting the nexus{_dispname}.')
1816
2033
  await self.nexsroot.startup()
1817
2034
 
1818
- logger.debug(f'HANDOFF: Released nexus applylock ahaname={ahaname}')
1819
- logger.warning(f'HANDOFF: Done performing the leadership handoff with {s_urlhelp.sanitizeUrl(turl)} ahaname={self.conf.get("aha:name")}')
2035
+ logger.debug(f'HANDOFF: Released nexus lock{_dispname}.')
2036
+
2037
+ logger.warning(f'HANDOFF: Done performing the leadership handoff with {s_urlhelp.sanitizeUrl(turl)}{_dispname}.')
1820
2038
 
1821
2039
  async def reqAhaProxy(self, timeout=None):
1822
2040
  if self.ahaclient is None:
@@ -1849,7 +2067,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1849
2067
  await proxy.fini()
1850
2068
  return
1851
2069
 
1852
- ahanetw = self.conf.get('aha:network')
2070
+ ahanetw = self.conf.req('aha:network')
1853
2071
  try:
1854
2072
  await proxy.addAhaSvc(ahalead, ahainfo, network=ahanetw)
1855
2073
  except asyncio.CancelledError: # pragma: no cover
@@ -1959,6 +2177,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
1959
2177
  self.onfini(self.activebase)
1960
2178
  self._fireActiveCoros()
1961
2179
  await self._execCellUpdates()
2180
+ await self.setNexsVers(NEXUS_VERSION)
1962
2181
  await self.initServiceActive()
1963
2182
  else:
1964
2183
  await self._killActiveCoros()
@@ -2115,7 +2334,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
2115
2334
 
2116
2335
  try:
2117
2336
 
2118
- async with self.nexsroot.applylock:
2337
+ async with self.nexslock:
2119
2338
 
2120
2339
  logger.debug('Syncing LMDB Slabs')
2121
2340
 
@@ -2361,7 +2580,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
2361
2580
  self.backupstreaming = False
2362
2581
 
2363
2582
  async def isUserAllowed(self, iden, perm, gateiden=None, default=False):
2364
- user = self.auth.user(iden) # type: s_hiveauth.HiveUser
2583
+ user = self.auth.user(iden) # type: s_auth.User
2365
2584
  if user is None:
2366
2585
  return False
2367
2586
 
@@ -2386,36 +2605,59 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
2386
2605
 
2387
2606
  async def getUserProfile(self, iden):
2388
2607
  user = await self.auth.reqUser(iden)
2389
- return user.profile.pack()
2608
+ return dict(user.profile.items())
2390
2609
 
2391
- async def getUserProfInfo(self, iden, name):
2610
+ async def getUserProfInfo(self, iden, name, default=None):
2392
2611
  user = await self.auth.reqUser(iden)
2393
- return user.profile.get(name)
2612
+ return user.profile.get(name, defv=default)
2613
+
2614
+ async def _setUserProfInfoV0(self, iden, name, valu):
2615
+ path = ('auth', 'users', iden, 'profile', name)
2616
+ return await self.hive._push('hive:set', path, valu)
2394
2617
 
2395
2618
  async def setUserProfInfo(self, iden, name, valu):
2396
2619
  user = await self.auth.reqUser(iden)
2397
- return await user.profile.set(name, valu)
2620
+ return await user.setProfileValu(name, valu)
2621
+
2622
+ async def _popUserProfInfoV0(self, iden, name, default=None):
2623
+ path = ('auth', 'users', iden, 'profile', name)
2624
+ return await self.hive._push('hive:pop', path)
2398
2625
 
2399
2626
  async def popUserProfInfo(self, iden, name, default=None):
2400
2627
  user = await self.auth.reqUser(iden)
2401
- return await user.profile.pop(name, default=default)
2628
+ return await user.popProfileValu(name, default=default)
2402
2629
 
2403
2630
  async def iterUserVars(self, iden):
2404
2631
  user = await self.auth.reqUser(iden)
2405
2632
  for item in user.vars.items():
2406
2633
  yield item
2634
+ await asyncio.sleep(0)
2407
2635
 
2408
- async def getUserVarValu(self, iden, name):
2636
+ async def iterUserProfInfo(self, iden):
2409
2637
  user = await self.auth.reqUser(iden)
2410
- return user.vars.get(name)
2638
+ for item in user.profile.items():
2639
+ yield item
2640
+ await asyncio.sleep(0)
2641
+
2642
+ async def getUserVarValu(self, iden, name, default=None):
2643
+ user = await self.auth.reqUser(iden)
2644
+ return user.vars.get(name, defv=default)
2645
+
2646
+ async def _setUserVarValuV0(self, iden, name, valu):
2647
+ path = ('auth', 'users', iden, 'vars', name)
2648
+ return await self.hive._push('hive:set', path, valu)
2411
2649
 
2412
2650
  async def setUserVarValu(self, iden, name, valu):
2413
2651
  user = await self.auth.reqUser(iden)
2414
- return await user.vars.set(name, valu)
2652
+ return await user.setVarValu(name, valu)
2653
+
2654
+ async def _popUserVarValuV0(self, iden, name, default=None):
2655
+ path = ('auth', 'users', iden, 'vars', name)
2656
+ return await self.hive._push('hive:pop', path)
2415
2657
 
2416
2658
  async def popUserVarValu(self, iden, name, default=None):
2417
2659
  user = await self.auth.reqUser(iden)
2418
- return await user.vars.pop(name, default=default)
2660
+ return await user.popVarValu(name, default=default)
2419
2661
 
2420
2662
  async def addUserRule(self, iden, rule, indx=None, gateiden=None):
2421
2663
  user = await self.auth.reqUser(iden)
@@ -2807,7 +3049,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
2807
3049
  sslctx = self.initSslCtx(certpath, pkeypath)
2808
3050
 
2809
3051
  kwargs = {
2810
- 'xheaders': self.conf.reqConfValu('https:parse:proxy:remoteip')
3052
+ 'xheaders': self.conf.req('https:parse:proxy:remoteip')
2811
3053
  }
2812
3054
  serv = self.wapp.listen(port, address=addr, ssl_options=sslctx, **kwargs)
2813
3055
  self.httpds.append(serv)
@@ -2958,9 +3200,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
2958
3200
 
2959
3201
  async def _initCellDmon(self):
2960
3202
 
2961
- ahainfo = {
2962
- 'name': self.ahasvcname
2963
- }
3203
+ ahainfo = {'name': self.ahasvcname}
2964
3204
 
2965
3205
  self.dmon = await s_daemon.Daemon.anit(ahainfo=ahainfo)
2966
3206
  self.dmon.share('*', self)
@@ -2969,11 +3209,17 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
2969
3209
 
2970
3210
  async def _initCellHive(self):
2971
3211
  db = self.slab.initdb('hive')
2972
- hive = await s_hive.SlabHive.anit(self.slab, db=db, nexsroot=self.getCellNexsRoot())
3212
+ hive = await s_hive.SlabHive.anit(self.slab, db=db, nexsroot=self.getCellNexsRoot(), cell=self)
2973
3213
  self.onfini(hive)
2974
3214
 
2975
3215
  return hive
2976
3216
 
3217
+ async def _initSlabFile(self, path, readonly=False):
3218
+ slab = await s_lmdbslab.Slab.anit(path, map_size=SLAB_MAP_SIZE, readonly=readonly)
3219
+ slab.addResizeCallback(self.checkFreeSpace)
3220
+ self.onfini(slab)
3221
+ return slab
3222
+
2977
3223
  async def _initCellSlab(self, readonly=False):
2978
3224
 
2979
3225
  s_common.gendir(self.dirn, 'slabs')
@@ -2985,44 +3231,31 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
2985
3231
  _slab.initdb('hive')
2986
3232
  await _slab.fini()
2987
3233
 
2988
- self.slab = await s_lmdbslab.Slab.anit(path, map_size=SLAB_MAP_SIZE, readonly=readonly)
2989
- self.slab.addResizeCallback(self.checkFreeSpace)
2990
-
2991
- self.onfini(self.slab.fini)
3234
+ self.slab = await self._initSlabFile(path)
2992
3235
 
2993
3236
  async def _initCellAuth(self):
2994
3237
 
3238
+ # Add callbacks
3239
+ self.on('user:del', self._onUserDelEvnt)
3240
+
2995
3241
  authctor = self.conf.get('auth:ctor')
2996
3242
  if authctor is not None:
2997
3243
  s_common.deprecated('auth:ctor cell config option', curv='2.157.0')
2998
3244
  ctor = s_dyndeps.getDynLocal(authctor)
2999
- auth = await ctor(self)
3000
- else:
3001
- auth = await self._initCellHiveAuth()
3002
-
3003
- # Add callbacks
3004
- self.on('user:del', self._onUserDelEvnt)
3005
-
3006
- return auth
3007
-
3008
- def getCellNexsRoot(self):
3009
- # the "cell scope" nexusroot only exists if we are *not* embedded
3010
- # (aka we dont have a self.cellparent)
3011
- if self.cellparent is None:
3012
- return self.nexsroot
3013
-
3014
- async def _initCellHiveAuth(self):
3245
+ return await ctor(self)
3015
3246
 
3016
3247
  maxusers = self.conf.get('max:users')
3248
+ policy = self.conf.get('auth:passwd:policy')
3017
3249
 
3018
3250
  seed = s_common.guid((self.iden, 'hive', 'auth'))
3019
3251
 
3020
- node = await self.hive.open(('auth',))
3021
- auth = await s_hiveauth.Auth.anit(
3022
- node,
3252
+ auth = await s_auth.Auth.anit(
3253
+ self.slab,
3254
+ 'auth',
3023
3255
  seed=seed,
3024
3256
  nexsroot=self.getCellNexsRoot(),
3025
- maxusers=maxusers
3257
+ maxusers=maxusers,
3258
+ policy=policy
3026
3259
  )
3027
3260
 
3028
3261
  auth.link(self.dist)
@@ -3035,6 +3268,12 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
3035
3268
  self.onfini(auth.fini)
3036
3269
  return auth
3037
3270
 
3271
+ def getCellNexsRoot(self):
3272
+ # the "cell scope" nexusroot only exists if we are *not* embedded
3273
+ # (aka we dont have a self.cellparent)
3274
+ if self.cellparent is None:
3275
+ return self.nexsroot
3276
+
3038
3277
  async def _initInauguralConfig(self):
3039
3278
  if self.inaugural:
3040
3279
  icfg = self.conf.get('inaugural')
@@ -3044,7 +3283,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
3044
3283
  name = rnfo.get('name')
3045
3284
  logger.debug(f'Adding inaugural role {name}')
3046
3285
  iden = s_common.guid((self.iden, 'auth', 'role', name))
3047
- role = await self.auth.addRole(name, iden) # type: s_hiveauth.HiveRole
3286
+ role = await self.auth.addRole(name, iden) # type: s_auth.Role
3048
3287
 
3049
3288
  for rule in rnfo.get('rules', ()):
3050
3289
  await role.addRule(rule)
@@ -3054,7 +3293,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
3054
3293
  email = unfo.get('email')
3055
3294
  iden = s_common.guid((self.iden, 'auth', 'user', name))
3056
3295
  logger.debug(f'Adding inaugural user {name}')
3057
- user = await self.auth.addUser(name, email=email, iden=iden) # type: s_hiveauth.HiveUser
3296
+ user = await self.auth.addUser(name, email=email, iden=iden) # type: s_auth.User
3058
3297
 
3059
3298
  if unfo.get('admin'):
3060
3299
  await user.setAdmin(True)
@@ -3221,7 +3460,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
3221
3460
 
3222
3461
  Args:
3223
3462
  link (s_link.Link): The link object.
3224
- user (s_hive.HiveUser): The heavy user object.
3463
+ user (s_auth.User): The heavy user object.
3225
3464
  path (str): The path requested.
3226
3465
 
3227
3466
  Notes:
@@ -3245,7 +3484,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
3245
3484
  '''
3246
3485
  extra = {**kwargs}
3247
3486
  sess = s_scope.get('sess') # type: s_daemon.Sess
3248
- user = s_scope.get('user') # type: s_hiveauth.HiveUser
3487
+ user = s_scope.get('user') # type: s_auth.User
3249
3488
  if user:
3250
3489
  extra['user'] = user.iden
3251
3490
  extra['username'] = user.name
@@ -3402,13 +3641,24 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
3402
3641
  return pars
3403
3642
 
3404
3643
  async def _initCellBoot(self):
3644
+ # NOTE: best hook point for custom provisioning
3405
3645
 
3406
- pnfo = await self._bootCellProv()
3646
+ isok, pnfo = await self._bootCellProv()
3407
3647
 
3408
3648
  # check this before we setup loadTeleCell()
3409
3649
  if not self._mustBootMirror():
3410
3650
  return
3411
3651
 
3652
+ if not isok:
3653
+ # The way that we get to this requires the following states to be true:
3654
+ # 1. self.dirn/cell.guid file is NOT present in the service directory.
3655
+ # 2. mirror config is present.
3656
+ # 3. aha:provision config is not set OR the aha:provision guid matches the self.dirn/prov.done file.
3657
+ mesg = 'Service has been configured to boot from an upstream mirror, but has entered into an invalid ' \
3658
+ 'state. This may have been caused by manipulation of the service storage or an error during a ' \
3659
+ f'backup / restore operation. {pnfo.get("mesg")}'
3660
+ raise s_exc.FatalErr(mesg=mesg)
3661
+
3412
3662
  async with s_telepath.loadTeleCell(self.dirn):
3413
3663
  await self._bootCellMirror(pnfo)
3414
3664
 
@@ -3540,7 +3790,8 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
3540
3790
 
3541
3791
  provurl = self.conf.get('aha:provision')
3542
3792
  if provurl is None:
3543
- return
3793
+ return False, {'mesg': 'No aha:provision configuration has been provided to allow the service to '
3794
+ 'bootstrap via AHA.'}
3544
3795
 
3545
3796
  doneiden = None
3546
3797
 
@@ -3553,7 +3804,8 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
3553
3804
  providen = urlinfo.get('path').strip('/')
3554
3805
 
3555
3806
  if doneiden == providen:
3556
- return
3807
+ return False, {'mesg': f'The aha:provision URL guid matches the service prov.done guid, '
3808
+ f'aha:provision={provurl}'}
3557
3809
 
3558
3810
  logger.info(f'Provisioning {self.getCellType()} from AHA service.')
3559
3811
 
@@ -3613,7 +3865,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
3613
3865
 
3614
3866
  logger.info(f'Done provisioning {self.getCellType()} AHA service.')
3615
3867
 
3616
- return provconf, providen
3868
+ return True, {'conf': provconf, 'iden': providen}
3617
3869
 
3618
3870
  async def _bootProvConf(self, provconf):
3619
3871
  '''
@@ -3695,23 +3947,14 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
3695
3947
  await self.nexsroot.enNexsLog()
3696
3948
  await self.sync()
3697
3949
 
3698
- async def _bootCellMirror(self, pnfo):
3699
- # this function must assume almost nothing is initialized
3700
- # but that's ok since it will only run rarely.
3701
- # It assumes it has a tuple of (provisioning configuration, provisioning iden) available
3702
- murl = self.conf.reqConfValu('mirror')
3703
- provconf, providen = pnfo
3704
-
3705
- logger.warning(f'Bootstrap mirror from: {murl} (this could take a while!)')
3950
+ async def _initCloneCell(self, proxy):
3706
3951
 
3707
3952
  tarpath = s_common.genpath(self.dirn, 'tmp', 'bootstrap.tgz')
3708
-
3709
3953
  try:
3710
3954
 
3711
- async with await s_telepath.openurl(murl) as cell:
3712
- await cell.readyToMirror()
3955
+ await proxy.readyToMirror()
3713
3956
  with s_common.genfile(tarpath) as fd:
3714
- async for byts in cell.iterNewBackupArchive(remove=True):
3957
+ async for byts in proxy.iterNewBackupArchive(remove=True):
3715
3958
  fd.write(byts)
3716
3959
 
3717
3960
  with tarfile.open(tarpath) as tgz:
@@ -3726,6 +3969,18 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
3726
3969
  if os.path.isfile(tarpath):
3727
3970
  os.unlink(tarpath)
3728
3971
 
3972
+ async def _bootCellMirror(self, pnfo):
3973
+ # this function must assume almost nothing is initialized
3974
+ # but that's ok since it will only run rarely.
3975
+ # It assumes it has a tuple of (provisioning configuration, provisioning iden) available
3976
+ murl = self.conf.req('mirror')
3977
+ provconf, providen = pnfo.get('conf'), pnfo.get('iden')
3978
+
3979
+ logger.warning(f'Bootstrap mirror from: {murl} (this could take a while!)')
3980
+
3981
+ async with await s_telepath.openurl(murl) as proxy:
3982
+ await self._initCloneCell(proxy)
3983
+
3729
3984
  # Remove aha:provision from cell.yaml if it exists and the iden differs.
3730
3985
  mnfo = s_common.yamlload(self.dirn, 'cell.yaml')
3731
3986
  if mnfo:
@@ -3821,15 +4076,12 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
3821
4076
 
3822
4077
  try:
3823
4078
 
3824
- if 'dmon:listen' not in cell.conf:
3825
- await cell.dmon.listen(opts.telepath)
3826
- logger.info(f'...{cell.getCellType()} API (telepath): {opts.telepath}')
3827
- else:
3828
- lisn = cell.conf.get('dmon:listen')
3829
- if lisn is None:
3830
- lisn = cell.getLocalUrl()
4079
+ turl = cell._getDmonListen()
4080
+ if turl is None:
4081
+ turl = opts.telepath
4082
+ await cell.dmon.listen(turl)
3831
4083
 
3832
- logger.info(f'...{cell.getCellType()} API (telepath): {lisn}')
4084
+ logger.info(f'...{cell.getCellType()} API (telepath): {turl}')
3833
4085
 
3834
4086
  if 'https:port' not in cell.conf:
3835
4087
  await cell.addHttpsPort(opts.https)
@@ -3997,6 +4249,12 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
3997
4249
  async def _onLoadHiveTree(self, tree, path, trim):
3998
4250
  return await self.hive.loadHiveTree(tree, path=path, trim=trim)
3999
4251
 
4252
+ async def iterSlabData(self, name, prefix=''):
4253
+ slabkv = self.slab.getSafeKeyVal(name, prefix=prefix, create=False)
4254
+ for key, valu in slabkv.items():
4255
+ yield key, valu
4256
+ await asyncio.sleep(0)
4257
+
4000
4258
  @s_nexus.Pusher.onPushAuto('sync')
4001
4259
  async def sync(self):
4002
4260
  '''
@@ -4065,6 +4323,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
4065
4323
  'type': self.getCellType(),
4066
4324
  'iden': self.getCellIden(),
4067
4325
  'active': self.isactive,
4326
+ 'started': self.startms,
4068
4327
  'ready': self.nexsroot.ready.is_set(),
4069
4328
  'commit': self.COMMIT,
4070
4329
  'version': self.VERSION,