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.
- synapse/axon.py +24 -9
- synapse/cortex.py +337 -172
- synapse/cryotank.py +46 -37
- synapse/datamodel.py +17 -4
- synapse/exc.py +19 -0
- synapse/lib/agenda.py +7 -13
- synapse/lib/aha.py +361 -88
- synapse/lib/auth.py +1520 -0
- synapse/lib/base.py +27 -9
- synapse/lib/cell.py +422 -163
- synapse/lib/config.py +15 -11
- synapse/lib/coro.py +13 -0
- synapse/lib/grammar.py +5 -0
- synapse/lib/hive.py +24 -3
- synapse/lib/hiveauth.py +6 -32
- synapse/lib/layer.py +7 -9
- synapse/lib/link.py +22 -18
- synapse/lib/lmdbslab.py +152 -3
- synapse/lib/modelrev.py +1 -1
- synapse/lib/nexus.py +24 -12
- synapse/lib/schemas.py +136 -0
- synapse/lib/storm.py +61 -29
- synapse/lib/stormlib/aha.py +1 -1
- synapse/lib/stormlib/auth.py +185 -10
- synapse/lib/stormlib/cortex.py +16 -5
- synapse/lib/stormlib/gen.py +80 -0
- synapse/lib/stormlib/imap.py +6 -2
- synapse/lib/stormlib/model.py +55 -0
- synapse/lib/stormlib/modelext.py +60 -0
- synapse/lib/stormlib/smtp.py +12 -2
- synapse/lib/stormlib/tabular.py +212 -0
- synapse/lib/stormtypes.py +14 -1
- synapse/lib/trigger.py +1 -1
- synapse/lib/version.py +2 -2
- synapse/lib/view.py +55 -28
- synapse/models/base.py +7 -0
- synapse/models/biz.py +4 -0
- synapse/models/files.py +8 -1
- synapse/models/inet.py +8 -0
- synapse/telepath.py +32 -17
- synapse/tests/files/aha/certs/cas/synapse.crt +28 -0
- synapse/tests/files/aha/certs/cas/synapse.key +51 -0
- synapse/tests/files/aha/certs/hosts/00.aha.loop.vertex.link.crt +30 -0
- synapse/tests/files/aha/certs/hosts/00.aha.loop.vertex.link.key +51 -0
- synapse/tests/files/aha/certs/users/root@synapse.crt +29 -0
- synapse/tests/files/aha/certs/users/root@synapse.key +51 -0
- synapse/tests/files/changelog/model_2.176.0_16ee721a6b7221344eaf946c3ab4602dda546b1a.yaml.gz +0 -0
- synapse/tests/files/changelog/model_2.176.0_2a25c58bbd344716cd7cbc3f4304d8925b0f4ef2.yaml.gz +0 -0
- synapse/tests/files/rstorm/testsvc.py +1 -1
- synapse/tests/test_axon.py +8 -5
- synapse/tests/test_cortex.py +149 -141
- synapse/tests/test_cryotank.py +4 -4
- synapse/tests/test_datamodel.py +7 -0
- synapse/tests/test_lib_agenda.py +10 -3
- synapse/tests/test_lib_aha.py +336 -490
- synapse/tests/{test_lib_hiveauth.py → test_lib_auth.py} +314 -11
- synapse/tests/test_lib_base.py +20 -0
- synapse/tests/test_lib_cell.py +210 -30
- synapse/tests/test_lib_config.py +4 -3
- synapse/tests/test_lib_httpapi.py +18 -14
- synapse/tests/test_lib_layer.py +33 -33
- synapse/tests/test_lib_link.py +42 -1
- synapse/tests/test_lib_lmdbslab.py +68 -0
- synapse/tests/test_lib_nexus.py +12 -4
- synapse/tests/test_lib_node.py +0 -7
- synapse/tests/test_lib_storm.py +45 -0
- synapse/tests/test_lib_stormlib_aha.py +35 -36
- synapse/tests/test_lib_stormlib_auth.py +21 -0
- synapse/tests/test_lib_stormlib_cell.py +4 -15
- synapse/tests/test_lib_stormlib_cortex.py +12 -12
- synapse/tests/test_lib_stormlib_gen.py +99 -0
- synapse/tests/test_lib_stormlib_imap.py +14 -3
- synapse/tests/test_lib_stormlib_model.py +108 -0
- synapse/tests/test_lib_stormlib_modelext.py +64 -0
- synapse/tests/test_lib_stormlib_smtp.py +51 -0
- synapse/tests/test_lib_stormlib_tabular.py +226 -0
- synapse/tests/test_lib_stormsvc.py +4 -1
- synapse/tests/test_lib_stormtypes.py +10 -0
- synapse/tests/test_model_base.py +3 -0
- synapse/tests/test_model_biz.py +3 -0
- synapse/tests/test_model_files.py +12 -2
- synapse/tests/test_model_inet.py +24 -0
- synapse/tests/test_tools_aha.py +78 -101
- synapse/tests/test_tools_changelog.py +196 -0
- synapse/tests/test_tools_healthcheck.py +4 -3
- synapse/tests/utils.py +87 -121
- synapse/tools/aha/clone.py +50 -0
- synapse/tools/aha/enroll.py +2 -1
- synapse/tools/backup.py +2 -2
- synapse/tools/changelog.py +776 -15
- {synapse-2.176.0.dist-info → synapse-2.178.0.dist-info}/METADATA +48 -48
- {synapse-2.176.0.dist-info → synapse-2.178.0.dist-info}/RECORD +95 -82
- {synapse-2.176.0.dist-info → synapse-2.178.0.dist-info}/WHEEL +1 -1
- {synapse-2.176.0.dist-info → synapse-2.178.0.dist-info}/LICENSE +0 -0
- {synapse-2.176.0.dist-info → synapse-2.178.0.dist-info}/top_level.txt +0 -0
synapse/lib/aha.py
CHANGED
|
@@ -12,6 +12,7 @@ import synapse.common as s_common
|
|
|
12
12
|
import synapse.daemon as s_daemon
|
|
13
13
|
import synapse.telepath as s_telepath
|
|
14
14
|
|
|
15
|
+
import synapse.lib.base as s_base
|
|
15
16
|
import synapse.lib.cell as s_cell
|
|
16
17
|
import synapse.lib.coro as s_coro
|
|
17
18
|
import synapse.lib.nexus as s_nexus
|
|
@@ -125,11 +126,15 @@ class AhaServicesV1(s_httpapi.Handler):
|
|
|
125
126
|
|
|
126
127
|
class AhaApi(s_cell.CellApi):
|
|
127
128
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
@s_cell.adminapi()
|
|
130
|
+
async def addAhaClone(self, host, port=27492, conf=None):
|
|
131
|
+
return await self.cell.addAhaClone(host, port=port, conf=conf)
|
|
132
|
+
|
|
133
|
+
async def getAhaUrls(self, user='root'):
|
|
134
|
+
ahaurls = await self.cell.getAhaUrls(user=user)
|
|
135
|
+
if ahaurls is None:
|
|
136
|
+
return ()
|
|
137
|
+
return ahaurls
|
|
133
138
|
|
|
134
139
|
async def getAhaSvc(self, name, filters=None):
|
|
135
140
|
'''
|
|
@@ -260,7 +265,6 @@ class AhaApi(s_cell.CellApi):
|
|
|
260
265
|
return await self.cell.signHostCsr(csrtext, signas=signas, sans=sans)
|
|
261
266
|
|
|
262
267
|
async def signUserCsr(self, csrtext, signas=None):
|
|
263
|
-
|
|
264
268
|
await self._reqUserAllowed(('aha', 'csr', 'user'))
|
|
265
269
|
return await self.cell.signUserCsr(csrtext, signas=signas)
|
|
266
270
|
|
|
@@ -299,6 +303,20 @@ class AhaApi(s_cell.CellApi):
|
|
|
299
303
|
async for item in self.cell.getAhaPools():
|
|
300
304
|
yield item
|
|
301
305
|
|
|
306
|
+
async def getAhaServers(self):
|
|
307
|
+
return await self.cell.getAhaServers()
|
|
308
|
+
|
|
309
|
+
async def getAhaServer(self, host, port):
|
|
310
|
+
return await self.cell.getAhaServer(host, port)
|
|
311
|
+
|
|
312
|
+
@s_cell.adminapi()
|
|
313
|
+
async def addAhaServer(self, server):
|
|
314
|
+
return await self.cell.addAhaServer(server)
|
|
315
|
+
|
|
316
|
+
@s_cell.adminapi()
|
|
317
|
+
async def delAhaServer(self, host, port):
|
|
318
|
+
return await self.cell.delAhaServer(host, port)
|
|
319
|
+
|
|
302
320
|
@s_cell.adminapi()
|
|
303
321
|
async def addAhaSvcProv(self, name, provinfo=None):
|
|
304
322
|
'''
|
|
@@ -341,6 +359,14 @@ class AhaApi(s_cell.CellApi):
|
|
|
341
359
|
'''
|
|
342
360
|
return await self.cell.clearAhaUserEnrolls()
|
|
343
361
|
|
|
362
|
+
@s_cell.adminapi()
|
|
363
|
+
async def clearAhaClones(self):
|
|
364
|
+
'''
|
|
365
|
+
Remove all unused AHA clone provisioning values.
|
|
366
|
+
'''
|
|
367
|
+
return await self.cell.clearAhaClones()
|
|
368
|
+
|
|
369
|
+
|
|
344
370
|
class ProvDmon(s_daemon.Daemon):
|
|
345
371
|
|
|
346
372
|
async def __anit__(self, aha):
|
|
@@ -366,10 +392,34 @@ class ProvDmon(s_daemon.Daemon):
|
|
|
366
392
|
await self.aha.delAhaUserEnroll(name)
|
|
367
393
|
return EnrollApi(self.aha, userinfo)
|
|
368
394
|
|
|
395
|
+
clone = await self.aha.getAhaClone(name)
|
|
396
|
+
if clone is not None:
|
|
397
|
+
host = clone.get('host')
|
|
398
|
+
mesg = f'Retrieved AHA clone info for {host} iden {name}'
|
|
399
|
+
logger.info(mesg, extra=await self.aha.getLogExtra(iden=name, host=host))
|
|
400
|
+
return CloneApi(self.aha, clone)
|
|
401
|
+
|
|
369
402
|
mesg = f'Invalid provisioning identifier name={name}. This could be' \
|
|
370
403
|
f' caused by the re-use of a provisioning URL.'
|
|
371
404
|
raise s_exc.NoSuchName(mesg=mesg, name=name)
|
|
372
405
|
|
|
406
|
+
class CloneApi:
|
|
407
|
+
|
|
408
|
+
def __init__(self, aha, clone):
|
|
409
|
+
self.aha = aha
|
|
410
|
+
self.clone = clone
|
|
411
|
+
|
|
412
|
+
async def getCloneDef(self):
|
|
413
|
+
return self.clone
|
|
414
|
+
|
|
415
|
+
async def readyToMirror(self):
|
|
416
|
+
return await self.aha.readyToMirror()
|
|
417
|
+
|
|
418
|
+
async def iterNewBackupArchive(self, name=None, remove=False):
|
|
419
|
+
async with self.aha.getLocalProxy() as proxy:
|
|
420
|
+
async for byts in proxy.iterNewBackupArchive(name=name, remove=remove):
|
|
421
|
+
yield byts
|
|
422
|
+
|
|
373
423
|
class EnrollApi:
|
|
374
424
|
|
|
375
425
|
def __init__(self, aha, userinfo):
|
|
@@ -377,20 +427,21 @@ class EnrollApi:
|
|
|
377
427
|
self.userinfo = userinfo
|
|
378
428
|
|
|
379
429
|
async def getUserInfo(self):
|
|
430
|
+
user = self.userinfo.get('name')
|
|
380
431
|
return {
|
|
381
|
-
'aha:urls': self.aha.
|
|
382
|
-
'aha:user':
|
|
383
|
-
'aha:network': self.aha.conf.
|
|
432
|
+
'aha:urls': await self.aha.getAhaUrls(user=user),
|
|
433
|
+
'aha:user': user,
|
|
434
|
+
'aha:network': self.aha.conf.req('aha:network'),
|
|
384
435
|
}
|
|
385
436
|
|
|
386
437
|
async def getCaCert(self):
|
|
387
|
-
ahanetw = self.aha.conf.
|
|
438
|
+
ahanetw = self.aha.conf.req('aha:network')
|
|
388
439
|
return self.aha.certdir.getCaCertBytes(ahanetw)
|
|
389
440
|
|
|
390
441
|
async def signUserCsr(self, byts):
|
|
391
442
|
|
|
392
443
|
ahauser = self.userinfo.get('name')
|
|
393
|
-
ahanetw = self.aha.conf.
|
|
444
|
+
ahanetw = self.aha.conf.req('aha:network')
|
|
394
445
|
|
|
395
446
|
username = f'{ahauser}@{ahanetw}'
|
|
396
447
|
|
|
@@ -416,7 +467,7 @@ class ProvApi:
|
|
|
416
467
|
return self.provinfo
|
|
417
468
|
|
|
418
469
|
async def getCaCert(self):
|
|
419
|
-
ahanetw = self.aha.conf.
|
|
470
|
+
ahanetw = self.aha.conf.req('aha:network')
|
|
420
471
|
return self.aha.certdir.getCaCertBytes(ahanetw)
|
|
421
472
|
|
|
422
473
|
async def signHostCsr(self, byts):
|
|
@@ -464,8 +515,17 @@ class AhaCell(s_cell.Cell):
|
|
|
464
515
|
confbase['mirror']['hidedocs'] = False # type: ignore
|
|
465
516
|
confbase['mirror']['hidecmdl'] = False # type: ignore
|
|
466
517
|
confdefs = {
|
|
518
|
+
'clone': {
|
|
519
|
+
'hidecmdl': True,
|
|
520
|
+
'description': 'Bootstrap a clone from the AHA clone URL.',
|
|
521
|
+
'type': ['string', 'null'],
|
|
522
|
+
},
|
|
523
|
+
'dns:name': {
|
|
524
|
+
'description': 'The registered DNS name used to reach the AHA service.',
|
|
525
|
+
'type': ['string', 'null'],
|
|
526
|
+
},
|
|
467
527
|
'aha:urls': {
|
|
468
|
-
'description': '
|
|
528
|
+
'description': 'Deprecated. AHA servers can now manage this automatically.',
|
|
469
529
|
'type': ['string', 'array'],
|
|
470
530
|
'items': {'type': 'string'},
|
|
471
531
|
},
|
|
@@ -480,6 +540,35 @@ class AhaCell(s_cell.Cell):
|
|
|
480
540
|
def getEnvPrefix(cls):
|
|
481
541
|
return (f'SYN_AHA', f'SYN_{cls.__name__.upper()}', )
|
|
482
542
|
|
|
543
|
+
async def _initCellBoot(self):
|
|
544
|
+
|
|
545
|
+
curl = self.conf.get('clone')
|
|
546
|
+
if curl is None:
|
|
547
|
+
return
|
|
548
|
+
|
|
549
|
+
path = s_common.genpath(self.dirn, 'cell.guid')
|
|
550
|
+
if os.path.isfile(path):
|
|
551
|
+
logger.info('Cloning AHA: cell.guid detected. Skipping.')
|
|
552
|
+
return
|
|
553
|
+
|
|
554
|
+
logger.warning(f'Cloning AHA: {curl}')
|
|
555
|
+
|
|
556
|
+
async with await s_telepath.openurl(curl) as proxy:
|
|
557
|
+
clone = await proxy.getCloneDef()
|
|
558
|
+
await self._initCloneCell(proxy)
|
|
559
|
+
|
|
560
|
+
logger.warning('Cloning AHA: done!')
|
|
561
|
+
|
|
562
|
+
conf = s_common.yamlload(self.dirn, 'cell.yaml')
|
|
563
|
+
if conf is None:
|
|
564
|
+
conf = {}
|
|
565
|
+
|
|
566
|
+
conf.update(clone.get('conf', {}))
|
|
567
|
+
|
|
568
|
+
s_common.yamlsave(conf, self.dirn, 'cell.yaml')
|
|
569
|
+
|
|
570
|
+
self.conf.update(conf)
|
|
571
|
+
|
|
483
572
|
async def initServiceStorage(self):
|
|
484
573
|
|
|
485
574
|
# TODO plumb using a remote jsonstor?
|
|
@@ -499,9 +588,66 @@ class AhaCell(s_cell.Cell):
|
|
|
499
588
|
self.slab.initdb('aha:provs')
|
|
500
589
|
self.slab.initdb('aha:enrolls')
|
|
501
590
|
|
|
591
|
+
self.slab.initdb('aha:clones')
|
|
592
|
+
self.slab.initdb('aha:servers')
|
|
593
|
+
|
|
502
594
|
self.slab.initdb('aha:pools')
|
|
595
|
+
|
|
503
596
|
self.poolwindows = collections.defaultdict(list)
|
|
504
597
|
|
|
598
|
+
async def getAhaServer(self, host, port):
|
|
599
|
+
lkey = s_msgpack.en((host, port))
|
|
600
|
+
byts = self.slab.get(lkey, db='aha:servers')
|
|
601
|
+
if byts is not None:
|
|
602
|
+
return s_msgpack.un(byts)
|
|
603
|
+
|
|
604
|
+
async def addAhaServer(self, server):
|
|
605
|
+
|
|
606
|
+
host = server.get('host')
|
|
607
|
+
port = server.setdefault('port', 27492)
|
|
608
|
+
|
|
609
|
+
# avoid a noop nexus change...
|
|
610
|
+
oldv = await self.getAhaServer(host, port)
|
|
611
|
+
if s_common.flatten(server) == s_common.flatten(oldv):
|
|
612
|
+
return False
|
|
613
|
+
|
|
614
|
+
return await self._push('aha:server:add', server)
|
|
615
|
+
|
|
616
|
+
@s_nexus.Pusher.onPush('aha:server:add')
|
|
617
|
+
async def _addAhaServer(self, server):
|
|
618
|
+
# TODO schema
|
|
619
|
+
host = server.get('host')
|
|
620
|
+
port = server.get('port')
|
|
621
|
+
|
|
622
|
+
lkey = s_msgpack.en((host, port))
|
|
623
|
+
|
|
624
|
+
byts = self.slab.get(lkey, db='aha:servers')
|
|
625
|
+
if byts is not None:
|
|
626
|
+
oldv = s_msgpack.un(byts)
|
|
627
|
+
if s_common.flatten(server) == s_common.flatten(oldv):
|
|
628
|
+
return False
|
|
629
|
+
|
|
630
|
+
self.slab.put(lkey, s_msgpack.en(server), db='aha:servers')
|
|
631
|
+
|
|
632
|
+
return True
|
|
633
|
+
|
|
634
|
+
@s_nexus.Pusher.onPushAuto('aha:server:del')
|
|
635
|
+
async def delAhaServer(self, host, port):
|
|
636
|
+
|
|
637
|
+
lkey = s_msgpack.en((host, port))
|
|
638
|
+
|
|
639
|
+
byts = self.slab.pop(lkey, db='aha:servers')
|
|
640
|
+
if byts is None:
|
|
641
|
+
return None
|
|
642
|
+
|
|
643
|
+
return s_msgpack.un(byts)
|
|
644
|
+
|
|
645
|
+
async def getAhaServers(self):
|
|
646
|
+
servers = []
|
|
647
|
+
for _, byts in self.slab.scanByFull(db='aha:servers'):
|
|
648
|
+
servers.append(s_msgpack.un(byts))
|
|
649
|
+
return servers
|
|
650
|
+
|
|
505
651
|
async def iterPoolTopo(self, name):
|
|
506
652
|
|
|
507
653
|
name = self._getAhaName(name)
|
|
@@ -537,41 +683,100 @@ class AhaCell(s_cell.Cell):
|
|
|
537
683
|
self.addHttpApi('/api/v1/aha/provision/service', AhaProvisionServiceV1, {'cell': self})
|
|
538
684
|
|
|
539
685
|
async def initServiceRuntime(self):
|
|
686
|
+
|
|
540
687
|
self.addActiveCoro(self._clearInactiveSessions)
|
|
541
688
|
|
|
542
689
|
if self.isactive:
|
|
543
|
-
# bootstrap a CA for our aha:network
|
|
544
|
-
netw = self.conf.get('aha:network')
|
|
545
|
-
if netw is not None:
|
|
546
690
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
await self.genCaCert(netw)
|
|
691
|
+
# bootstrap a CA for our aha:network
|
|
692
|
+
netw = self.conf.req('aha:network')
|
|
550
693
|
|
|
551
|
-
|
|
694
|
+
if self.certdir.getCaCertPath(netw) is None:
|
|
695
|
+
logger.info(f'Adding CA certificate for {netw}')
|
|
696
|
+
await self.genCaCert(netw)
|
|
552
697
|
|
|
698
|
+
name = self.conf.get('aha:name')
|
|
699
|
+
if name is not None:
|
|
553
700
|
host = f'{name}.{netw}'
|
|
554
701
|
if self.certdir.getHostCertPath(host) is None:
|
|
555
702
|
logger.info(f'Adding server certificate for {host}')
|
|
556
703
|
await self._genHostCert(host, signas=netw)
|
|
557
704
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
705
|
+
root = f'root@{netw}'
|
|
706
|
+
await self._genUserCert(root, signas=netw)
|
|
707
|
+
|
|
708
|
+
user = self.conf.get('aha:admin')
|
|
709
|
+
if user is not None:
|
|
710
|
+
await self._genUserCert(user, signas=netw)
|
|
711
|
+
|
|
712
|
+
def _getDnsName(self):
|
|
713
|
+
# emulate the old aha name.network behavior if the
|
|
714
|
+
# explicit option is not set.
|
|
715
|
+
|
|
716
|
+
hostname = self.conf.get('dns:name')
|
|
717
|
+
if hostname is not None:
|
|
718
|
+
return hostname
|
|
719
|
+
|
|
720
|
+
ahaname = self.conf.get('aha:name')
|
|
721
|
+
ahanetw = self.conf.get('aha:network')
|
|
722
|
+
if ahaname is not None and ahanetw is not None:
|
|
723
|
+
return f'{ahaname}.{ahanetw}'
|
|
724
|
+
|
|
725
|
+
def _getProvListen(self):
|
|
726
|
+
|
|
727
|
+
lisn = self.conf.get('provision:listen')
|
|
728
|
+
if lisn is not None:
|
|
729
|
+
return lisn
|
|
730
|
+
|
|
731
|
+
# this may not use _getDnsName() in order to maintain
|
|
732
|
+
# backward compatibilty with aha name.network configs
|
|
733
|
+
# that do not intend to listen for provisioning.
|
|
734
|
+
hostname = self.conf.get('dns:name')
|
|
735
|
+
if hostname is not None:
|
|
736
|
+
return f'ssl://{hostname}:27272'
|
|
737
|
+
|
|
738
|
+
def _getDmonListen(self):
|
|
739
|
+
|
|
740
|
+
lisn = self.conf.get('dmon:listen', s_common.novalu)
|
|
741
|
+
if lisn is not s_common.novalu:
|
|
742
|
+
return lisn
|
|
743
|
+
|
|
744
|
+
network = self.conf.req('aha:network')
|
|
745
|
+
dnsname = self._getDnsName()
|
|
746
|
+
if dnsname is not None:
|
|
747
|
+
return f'ssl://0.0.0.0?hostname={dnsname}&ca={network}'
|
|
748
|
+
|
|
749
|
+
def _reqProvListen(self):
|
|
750
|
+
lisn = self._getProvListen()
|
|
751
|
+
if lisn is not None:
|
|
752
|
+
return lisn
|
|
753
|
+
|
|
754
|
+
mesg = 'The AHA server is not configured for provisioning.'
|
|
755
|
+
raise s_exc.NeedConfValu(mesg=mesg)
|
|
563
756
|
|
|
564
757
|
async def initServiceNetwork(self):
|
|
565
758
|
|
|
759
|
+
# bootstrap CA/host certs first
|
|
760
|
+
network = self.conf.req('aha:network')
|
|
761
|
+
|
|
762
|
+
hostname = self._getDnsName()
|
|
763
|
+
if hostname is not None and network is not None:
|
|
764
|
+
await self._genHostCert(hostname, signas=network)
|
|
765
|
+
|
|
566
766
|
await s_cell.Cell.initServiceNetwork(self)
|
|
567
767
|
|
|
768
|
+
# all AHA mirrors are registered
|
|
769
|
+
if hostname is not None and self.sockaddr is not None:
|
|
770
|
+
server = {'host': hostname, 'port': self.sockaddr[1]}
|
|
771
|
+
await self.addAhaServer(server)
|
|
772
|
+
|
|
568
773
|
self.provdmon = None
|
|
569
774
|
|
|
570
|
-
provurl = self.
|
|
775
|
+
provurl = self._getProvListen()
|
|
571
776
|
if provurl is not None:
|
|
572
777
|
self.provdmon = await ProvDmon.anit(self)
|
|
573
778
|
self.onfini(self.provdmon)
|
|
574
|
-
await self.provdmon.listen(provurl)
|
|
779
|
+
self.provaddr = await self.provdmon.listen(provurl)
|
|
575
780
|
|
|
576
781
|
async def _clearInactiveSessions(self):
|
|
577
782
|
|
|
@@ -590,6 +795,41 @@ class AhaCell(s_cell.Cell):
|
|
|
590
795
|
# Wait until we are cancelled or the cell is fini.
|
|
591
796
|
await self.waitfini()
|
|
592
797
|
|
|
798
|
+
async def _waitAhaSvcOnline(self, name, timeout=None):
|
|
799
|
+
|
|
800
|
+
name = self._getAhaName(name)
|
|
801
|
+
|
|
802
|
+
while True:
|
|
803
|
+
|
|
804
|
+
async with self.nexslock:
|
|
805
|
+
|
|
806
|
+
retn = await self.getAhaSvc(name)
|
|
807
|
+
if retn['svcinfo'].get('online') is not None:
|
|
808
|
+
return retn
|
|
809
|
+
|
|
810
|
+
waiter = self.waiter(1, f'aha:svcadd:{name}')
|
|
811
|
+
|
|
812
|
+
if await waiter.wait(timeout=timeout) is None:
|
|
813
|
+
raise s_exc.TimeOut(mesg=f'Timeout waiting for aha:svcadd:{name}')
|
|
814
|
+
|
|
815
|
+
async def _waitAhaSvcDown(self, name, timeout=None):
|
|
816
|
+
|
|
817
|
+
name = self._getAhaName(name)
|
|
818
|
+
|
|
819
|
+
while True:
|
|
820
|
+
|
|
821
|
+
async with self.nexslock:
|
|
822
|
+
|
|
823
|
+
retn = await self.getAhaSvc(name)
|
|
824
|
+
online = retn['svcinfo'].get('online')
|
|
825
|
+
if online is None:
|
|
826
|
+
return retn
|
|
827
|
+
|
|
828
|
+
waiter = self.waiter(1, f'aha:svcdown:{name}')
|
|
829
|
+
|
|
830
|
+
if await waiter.wait(timeout=timeout) is None:
|
|
831
|
+
raise s_exc.TimeOut(mesg=f'Timeout waiting for aha:svcdown:{name}')
|
|
832
|
+
|
|
593
833
|
async def getAhaSvcs(self, network=None):
|
|
594
834
|
path = ('aha', 'services')
|
|
595
835
|
if network is not None:
|
|
@@ -654,15 +894,12 @@ class AhaCell(s_cell.Cell):
|
|
|
654
894
|
|
|
655
895
|
# mostly for testing...
|
|
656
896
|
await self.fire('aha:svcadd', svcinfo=svcinfo)
|
|
897
|
+
await self.fire(f'aha:svcadd:{svcfull}', svcinfo=svcinfo)
|
|
657
898
|
|
|
658
899
|
def _getAhaName(self, name):
|
|
659
900
|
# the modern version of names is absolute or ...
|
|
660
901
|
if name.endswith('...'):
|
|
661
|
-
|
|
662
|
-
if netw is None: # pragma: no cover
|
|
663
|
-
mesg = 'AHA Server requires aha:network configuration.'
|
|
664
|
-
raise s_exc.NeedConfValu(mesg=mesg)
|
|
665
|
-
name = name[:-2] + netw
|
|
902
|
+
return name[:-2] + self.conf.req('aha:network')
|
|
666
903
|
return name
|
|
667
904
|
|
|
668
905
|
async def getAhaPool(self, name):
|
|
@@ -780,6 +1017,7 @@ class AhaCell(s_cell.Cell):
|
|
|
780
1017
|
@s_nexus.Pusher.onPushAuto('aha:svc:del')
|
|
781
1018
|
async def delAhaSvc(self, name, network=None):
|
|
782
1019
|
|
|
1020
|
+
name = self._getAhaName(name)
|
|
783
1021
|
svcname, svcnetw, svcfull = self._nameAndNetwork(name, network)
|
|
784
1022
|
|
|
785
1023
|
logger.info(f'Deleting service [{svcfull}].', extra=await self.getLogExtra(name=svcname, netw=svcnetw))
|
|
@@ -794,6 +1032,8 @@ class AhaCell(s_cell.Cell):
|
|
|
794
1032
|
await self.fire('aha:svcdel', svcname=svcname, svcnetw=svcnetw)
|
|
795
1033
|
|
|
796
1034
|
async def setAhaSvcDown(self, name, linkiden, network=None):
|
|
1035
|
+
|
|
1036
|
+
name = self._getAhaName(name)
|
|
797
1037
|
svcname, svcnetw, svcfull = self._nameAndNetwork(name, network)
|
|
798
1038
|
path = ('aha', 'services', svcnetw, svcname)
|
|
799
1039
|
|
|
@@ -805,8 +1045,10 @@ class AhaCell(s_cell.Cell):
|
|
|
805
1045
|
|
|
806
1046
|
@s_nexus.Pusher.onPush('aha:svc:down')
|
|
807
1047
|
async def _setAhaSvcDown(self, name, linkiden, network=None):
|
|
1048
|
+
|
|
808
1049
|
svcname, svcnetw, svcfull = self._nameAndNetwork(name, network)
|
|
809
1050
|
path = ('aha', 'services', svcnetw, svcname)
|
|
1051
|
+
|
|
810
1052
|
if await self.jsonstor.cmpDelPathObjProp(path, 'svcinfo/online', linkiden):
|
|
811
1053
|
await self.jsonstor.setPathObjProp(path, 'svcinfo/ready', False)
|
|
812
1054
|
|
|
@@ -818,6 +1060,7 @@ class AhaCell(s_cell.Cell):
|
|
|
818
1060
|
await link.fini()
|
|
819
1061
|
|
|
820
1062
|
await self.fire('aha:svcdown', svcname=svcname, svcnetw=svcnetw)
|
|
1063
|
+
await self.fire(f'aha:svcdown:{svcfull}', svcname=svcname, svcnetw=svcnetw)
|
|
821
1064
|
|
|
822
1065
|
logger.info(f'Set [{svcfull}] offline.',
|
|
823
1066
|
extra=await self.getLogExtra(name=svcname, netw=svcnetw))
|
|
@@ -918,7 +1161,7 @@ class AhaCell(s_cell.Cell):
|
|
|
918
1161
|
|
|
919
1162
|
async def _genHostCert(self, hostname, signas=None):
|
|
920
1163
|
|
|
921
|
-
if
|
|
1164
|
+
if self.certdir.getHostCertPath(hostname) is not None:
|
|
922
1165
|
return
|
|
923
1166
|
|
|
924
1167
|
pkey, cert = await s_coro.executor(self.certdir.genHostCert, hostname, signas=signas, save=False)
|
|
@@ -927,8 +1170,12 @@ class AhaCell(s_cell.Cell):
|
|
|
927
1170
|
await self.saveHostCert(hostname, pkey, cert)
|
|
928
1171
|
|
|
929
1172
|
async def _genUserCert(self, username, signas=None):
|
|
930
|
-
|
|
1173
|
+
|
|
1174
|
+
if self.certdir.getUserCertPath(username) is not None:
|
|
931
1175
|
return
|
|
1176
|
+
|
|
1177
|
+
logger.info(f'Adding user certificate for {username}')
|
|
1178
|
+
|
|
932
1179
|
pkey, cert = await s_coro.executor(self.certdir.genUserCert, username, signas=signas, save=False)
|
|
933
1180
|
pkey = self.certdir._pkeyToByts(pkey).decode()
|
|
934
1181
|
cert = self.certdir._certToByts(cert).decode()
|
|
@@ -971,8 +1218,8 @@ class AhaCell(s_cell.Cell):
|
|
|
971
1218
|
|
|
972
1219
|
hostname = xcsr.subject.get_attributes_for_oid(c_x509.NameOID.COMMON_NAME)[0].value
|
|
973
1220
|
|
|
974
|
-
hostpath =
|
|
975
|
-
if
|
|
1221
|
+
hostpath = self.certdir.getHostCertPath(hostname)
|
|
1222
|
+
if hostpath is not None:
|
|
976
1223
|
os.unlink(hostpath)
|
|
977
1224
|
|
|
978
1225
|
if signas is None:
|
|
@@ -990,8 +1237,8 @@ class AhaCell(s_cell.Cell):
|
|
|
990
1237
|
|
|
991
1238
|
username = xcsr.subject.get_attributes_for_oid(c_x509.NameOID.COMMON_NAME)[0].value
|
|
992
1239
|
|
|
993
|
-
userpath =
|
|
994
|
-
if
|
|
1240
|
+
userpath = self.certdir.getUserCertPath(username)
|
|
1241
|
+
if userpath is not None:
|
|
995
1242
|
os.unlink(userpath)
|
|
996
1243
|
|
|
997
1244
|
if signas is None:
|
|
@@ -1004,41 +1251,75 @@ class AhaCell(s_cell.Cell):
|
|
|
1004
1251
|
|
|
1005
1252
|
return self.certdir._certToByts(cert).decode()
|
|
1006
1253
|
|
|
1007
|
-
def
|
|
1254
|
+
async def getAhaUrls(self, user='root'):
|
|
1255
|
+
|
|
1256
|
+
# for backward compat...
|
|
1008
1257
|
urls = self.conf.get('aha:urls')
|
|
1009
1258
|
if urls is not None:
|
|
1010
|
-
if isinstance(urls, str):
|
|
1011
|
-
return (urls,)
|
|
1012
1259
|
return urls
|
|
1013
1260
|
|
|
1014
|
-
|
|
1015
|
-
if ahaname is None:
|
|
1016
|
-
return None
|
|
1261
|
+
network = self.conf.req('aha:network')
|
|
1017
1262
|
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1263
|
+
urls = []
|
|
1264
|
+
for server in await self.getAhaServers():
|
|
1265
|
+
host = server.get('host')
|
|
1266
|
+
port = server.get('port')
|
|
1267
|
+
urls.append(f'ssl://{host}:{port}?certname={user}@{network}')
|
|
1021
1268
|
|
|
1022
|
-
|
|
1023
|
-
return None
|
|
1269
|
+
return urls
|
|
1024
1270
|
|
|
1025
|
-
|
|
1026
|
-
|
|
1271
|
+
def getMyUrl(self, user='root'):
|
|
1272
|
+
port = self.sockaddr[1]
|
|
1273
|
+
host = self._getDnsName()
|
|
1274
|
+
network = self.conf.req('aha:network')
|
|
1275
|
+
return f'ssl://{host}:{port}?certname={user}@{network}'
|
|
1027
1276
|
|
|
1028
|
-
async def
|
|
1277
|
+
async def getAhaClone(self, iden):
|
|
1278
|
+
lkey = s_common.uhex(iden)
|
|
1279
|
+
byts = self.slab.get(lkey, db='aha:clones')
|
|
1280
|
+
if byts is not None:
|
|
1281
|
+
return s_msgpack.un(byts)
|
|
1029
1282
|
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1283
|
+
async def addAhaClone(self, host, port=27492, conf=None):
|
|
1284
|
+
|
|
1285
|
+
if conf is None:
|
|
1286
|
+
conf = {}
|
|
1287
|
+
|
|
1288
|
+
network = self.conf.req('aha:network')
|
|
1289
|
+
|
|
1290
|
+
conf['mirror'] = self.getMyUrl()
|
|
1291
|
+
|
|
1292
|
+
conf['dns:name'] = host
|
|
1293
|
+
conf['aha:network'] = network
|
|
1294
|
+
conf['dmon:listen'] = f'ssl://0.0.0.0:{port}?hostname={host}&ca={network}'
|
|
1295
|
+
|
|
1296
|
+
iden = s_common.guid()
|
|
1297
|
+
clone = {
|
|
1298
|
+
'iden': iden,
|
|
1299
|
+
'host': host,
|
|
1300
|
+
'port': port,
|
|
1301
|
+
'conf': conf,
|
|
1302
|
+
}
|
|
1303
|
+
await self._push('aha:clone:add', clone)
|
|
1304
|
+
|
|
1305
|
+
logger.info(f'Created AHA clone provisioning for {host} with iden {iden}',
|
|
1306
|
+
extra=await self.getLogExtra(iden=iden, name=host, netw=network))
|
|
1307
|
+
|
|
1308
|
+
return self._getProvClientUrl(iden)
|
|
1034
1309
|
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1310
|
+
@s_nexus.Pusher.onPush('aha:clone:add')
|
|
1311
|
+
async def _addAhaClone(self, clone):
|
|
1312
|
+
iden = clone.get('iden')
|
|
1313
|
+
lkey = s_common.uhex(iden)
|
|
1314
|
+
self.slab.put(lkey, s_msgpack.en(clone), db='aha:clones')
|
|
1315
|
+
|
|
1316
|
+
async def addAhaSvcProv(self, name, provinfo=None):
|
|
1038
1317
|
|
|
1039
1318
|
if not name:
|
|
1040
1319
|
raise s_exc.BadArg(mesg='Empty name values are not allowed for provisioning.')
|
|
1041
1320
|
|
|
1321
|
+
self._reqProvListen()
|
|
1322
|
+
|
|
1042
1323
|
if provinfo is None:
|
|
1043
1324
|
provinfo = {}
|
|
1044
1325
|
|
|
@@ -1048,23 +1329,16 @@ class AhaCell(s_cell.Cell):
|
|
|
1048
1329
|
|
|
1049
1330
|
conf = provinfo.setdefault('conf', {})
|
|
1050
1331
|
|
|
1051
|
-
|
|
1332
|
+
netw = self.conf.req('aha:network')
|
|
1052
1333
|
|
|
1053
1334
|
ahaadmin = self.conf.get('aha:admin')
|
|
1054
1335
|
if ahaadmin is not None: # pragma: no cover
|
|
1055
1336
|
conf.setdefault('aha:admin', ahaadmin)
|
|
1056
1337
|
|
|
1057
|
-
conf.setdefault('aha:user', 'root')
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
netw = conf.get('aha:network')
|
|
1061
|
-
if netw is None:
|
|
1062
|
-
mesg = 'AHA server has no configured aha:network.'
|
|
1063
|
-
raise s_exc.NeedConfValu(mesg=mesg)
|
|
1338
|
+
ahauser = conf.setdefault('aha:user', 'root')
|
|
1339
|
+
ahaurls = await self.getAhaUrls(user=ahauser)
|
|
1064
1340
|
|
|
1065
|
-
|
|
1066
|
-
mesg = f'Provisioning aha:network must be equal to the Aha servers network. Expected {mynetw}, got {netw}'
|
|
1067
|
-
raise s_exc.BadConfValu(mesg=mesg, name='aha:network', expected=mynetw, got=netw)
|
|
1341
|
+
conf['aha:network'] = netw
|
|
1068
1342
|
|
|
1069
1343
|
hostname = f'{name}.{netw}'
|
|
1070
1344
|
|
|
@@ -1088,15 +1362,11 @@ class AhaCell(s_cell.Cell):
|
|
|
1088
1362
|
if peer:
|
|
1089
1363
|
conf.setdefault('aha:leader', leader)
|
|
1090
1364
|
|
|
1091
|
-
# allow user to win over leader
|
|
1092
|
-
ahauser = conf.get('aha:user')
|
|
1093
|
-
ahaurls = s_telepath.modurl(ahaurls, user=ahauser)
|
|
1094
|
-
|
|
1095
1365
|
conf.setdefault('aha:registry', ahaurls)
|
|
1096
1366
|
|
|
1097
1367
|
mirname = provinfo.get('mirror')
|
|
1098
1368
|
if mirname is not None:
|
|
1099
|
-
conf['mirror'] = f'aha://{ahauser}@{mirname}
|
|
1369
|
+
conf['mirror'] = f'aha://{ahauser}@{mirname}...'
|
|
1100
1370
|
|
|
1101
1371
|
user = await self.auth.getUserByName(ahauser)
|
|
1102
1372
|
if user is None:
|
|
@@ -1122,7 +1392,10 @@ class AhaCell(s_cell.Cell):
|
|
|
1122
1392
|
|
|
1123
1393
|
def _getProvClientUrl(self, iden):
|
|
1124
1394
|
|
|
1125
|
-
provlisn = self.
|
|
1395
|
+
provlisn = self._getProvListen()
|
|
1396
|
+
|
|
1397
|
+
provport = self.provaddr[1]
|
|
1398
|
+
provhost = self._getDnsName()
|
|
1126
1399
|
|
|
1127
1400
|
urlinfo = s_telepath.chopurl(provlisn)
|
|
1128
1401
|
|
|
@@ -1133,8 +1406,8 @@ class AhaCell(s_cell.Cell):
|
|
|
1133
1406
|
host = urlinfo.get('host')
|
|
1134
1407
|
|
|
1135
1408
|
newinfo = {
|
|
1136
|
-
'host':
|
|
1137
|
-
'port':
|
|
1409
|
+
'host': provhost,
|
|
1410
|
+
'port': provport,
|
|
1138
1411
|
'scheme': scheme,
|
|
1139
1412
|
'path': '/' + iden,
|
|
1140
1413
|
}
|
|
@@ -1171,25 +1444,25 @@ class AhaCell(s_cell.Cell):
|
|
|
1171
1444
|
userinfo = s_msgpack.un(byts)
|
|
1172
1445
|
logger.info(f'Deleted user enrollment username={userinfo.get("name")}, iden={iden.decode()}')
|
|
1173
1446
|
|
|
1447
|
+
@s_nexus.Pusher.onPushAuto('aha:clone:clear')
|
|
1448
|
+
async def clearAhaClones(self):
|
|
1449
|
+
for lkey, byts in self.slab.scanByFull(db='aha:clones'):
|
|
1450
|
+
self.slab.delete(lkey, db='aha:clones')
|
|
1451
|
+
cloninfo = s_msgpack.un(byts)
|
|
1452
|
+
logger.info(f'Deleted AHA clone enrollment username={cloninfo.get("host")}, iden={s_common.ehex(lkey)}')
|
|
1453
|
+
|
|
1174
1454
|
@s_nexus.Pusher.onPushAuto('aha:svc:prov:del')
|
|
1175
1455
|
async def delAhaSvcProv(self, iden):
|
|
1176
1456
|
self.slab.delete(iden.encode(), db='aha:provs')
|
|
1177
1457
|
|
|
1178
1458
|
async def addAhaUserEnroll(self, name, userinfo=None, again=False):
|
|
1179
1459
|
|
|
1180
|
-
provurl = self.conf.get('provision:listen')
|
|
1181
|
-
if provurl is None:
|
|
1182
|
-
mesg = 'The AHA server does not have a provision:listen URL!'
|
|
1183
|
-
raise s_exc.NeedConfValu(mesg=mesg)
|
|
1184
|
-
|
|
1185
|
-
ahanetw = self.conf.get('aha:network')
|
|
1186
|
-
if ahanetw is None:
|
|
1187
|
-
mesg = 'AHA server requires aha:network configuration.'
|
|
1188
|
-
raise s_exc.NeedConfValu(mesg=mesg)
|
|
1189
|
-
|
|
1190
1460
|
if not name:
|
|
1191
1461
|
raise s_exc.BadArg(mesg='Empty name values are not allowed for provisioning.')
|
|
1192
1462
|
|
|
1463
|
+
provurl = self._reqProvListen()
|
|
1464
|
+
ahanetw = self.conf.req('aha:network')
|
|
1465
|
+
|
|
1193
1466
|
username = f'{name}@{ahanetw}'
|
|
1194
1467
|
|
|
1195
1468
|
if len(username) > 64:
|