synapse 2.177.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/cortex.py +8 -4
- synapse/lib/aha.py +361 -88
- synapse/lib/base.py +27 -9
- synapse/lib/cell.py +167 -110
- synapse/lib/config.py +15 -11
- synapse/lib/coro.py +13 -0
- synapse/lib/layer.py +0 -5
- synapse/lib/link.py +1 -1
- synapse/lib/lmdbslab.py +3 -3
- synapse/lib/nexus.py +24 -12
- synapse/lib/stormlib/imap.py +6 -2
- synapse/lib/stormlib/smtp.py +12 -2
- synapse/lib/version.py +2 -2
- 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/rstorm/testsvc.py +1 -1
- synapse/tests/test_axon.py +1 -1
- synapse/tests/test_cortex.py +22 -59
- synapse/tests/test_lib_agenda.py +3 -3
- synapse/tests/test_lib_aha.py +336 -490
- synapse/tests/test_lib_base.py +20 -0
- synapse/tests/test_lib_cell.py +49 -22
- synapse/tests/test_lib_config.py +4 -3
- synapse/tests/test_lib_nexus.py +8 -0
- synapse/tests/test_lib_stormlib_aha.py +35 -35
- synapse/tests/test_lib_stormlib_cell.py +4 -15
- synapse/tests/test_lib_stormlib_imap.py +14 -3
- synapse/tests/test_lib_stormlib_smtp.py +51 -0
- synapse/tests/test_tools_aha.py +78 -101
- synapse/tests/utils.py +86 -120
- synapse/tools/aha/clone.py +50 -0
- synapse/tools/aha/enroll.py +2 -1
- synapse/tools/backup.py +2 -2
- synapse/tools/changelog.py +3 -1
- {synapse-2.177.0.dist-info → synapse-2.178.0.dist-info}/METADATA +48 -48
- {synapse-2.177.0.dist-info → synapse-2.178.0.dist-info}/RECORD +44 -37
- {synapse-2.177.0.dist-info → synapse-2.178.0.dist-info}/LICENSE +0 -0
- {synapse-2.177.0.dist-info → synapse-2.178.0.dist-info}/WHEEL +0 -0
- {synapse-2.177.0.dist-info → synapse-2.178.0.dist-info}/top_level.txt +0 -0
synapse/lib/base.py
CHANGED
|
@@ -179,6 +179,13 @@ class Base:
|
|
|
179
179
|
'''
|
|
180
180
|
Add a function/coroutine/Base to be called on fini().
|
|
181
181
|
'''
|
|
182
|
+
if self.isfini:
|
|
183
|
+
if isinstance(func, Base):
|
|
184
|
+
s_coro.create_task(func.fini())
|
|
185
|
+
else:
|
|
186
|
+
s_coro.create_task(s_coro.ornot(func))
|
|
187
|
+
return
|
|
188
|
+
|
|
182
189
|
if isinstance(func, Base):
|
|
183
190
|
self.tofini.add(func)
|
|
184
191
|
return
|
|
@@ -408,8 +415,6 @@ class Base:
|
|
|
408
415
|
for fini in self._fini_funcs:
|
|
409
416
|
try:
|
|
410
417
|
await s_coro.ornot(fini)
|
|
411
|
-
except asyncio.CancelledError: # pragma: no cover TODO: remove once >= py 3.8 only
|
|
412
|
-
raise
|
|
413
418
|
except Exception:
|
|
414
419
|
logger.exception(f'{self} - fini function failed: {fini}')
|
|
415
420
|
|
|
@@ -579,7 +584,7 @@ class Base:
|
|
|
579
584
|
loop.add_signal_handler(signal.SIGINT, sigint)
|
|
580
585
|
loop.add_signal_handler(signal.SIGTERM, sigterm)
|
|
581
586
|
|
|
582
|
-
async def main(self):
|
|
587
|
+
async def main(self): # pragma: no cover
|
|
583
588
|
'''
|
|
584
589
|
Helper function to setup signal handlers for this base as the main object.
|
|
585
590
|
( use base.waitfini() to block )
|
|
@@ -590,7 +595,7 @@ class Base:
|
|
|
590
595
|
await self.addSignalHandlers()
|
|
591
596
|
return await self.waitfini()
|
|
592
597
|
|
|
593
|
-
def waiter(self, count, *names):
|
|
598
|
+
def waiter(self, count, *names, timeout=None):
|
|
594
599
|
'''
|
|
595
600
|
Construct and return a new Waiter for events on this base.
|
|
596
601
|
|
|
@@ -615,16 +620,17 @@ class Base:
|
|
|
615
620
|
race conditions with this mechanism ;)
|
|
616
621
|
|
|
617
622
|
'''
|
|
618
|
-
return Waiter(self, count, *names)
|
|
623
|
+
return Waiter(self, count, *names, timeout=timeout)
|
|
619
624
|
|
|
620
625
|
class Waiter:
|
|
621
626
|
'''
|
|
622
627
|
A helper to wait for a given number of events on a Base.
|
|
623
628
|
'''
|
|
624
|
-
def __init__(self, base, count, *names):
|
|
629
|
+
def __init__(self, base, count, *names, timeout=None):
|
|
625
630
|
self.base = base
|
|
626
631
|
self.names = names
|
|
627
632
|
self.count = count
|
|
633
|
+
self.timeout = timeout
|
|
628
634
|
self.event = asyncio.Event()
|
|
629
635
|
|
|
630
636
|
self.events = []
|
|
@@ -656,6 +662,9 @@ class Waiter:
|
|
|
656
662
|
doStuff(evnt)
|
|
657
663
|
|
|
658
664
|
'''
|
|
665
|
+
if timeout is None:
|
|
666
|
+
timeout = self.timeout
|
|
667
|
+
|
|
659
668
|
try:
|
|
660
669
|
|
|
661
670
|
retn = await s_coro.event_wait(self.event, timeout)
|
|
@@ -676,6 +685,18 @@ class Waiter:
|
|
|
676
685
|
self.base.unlink(self._onWaitEvent)
|
|
677
686
|
del self.event
|
|
678
687
|
|
|
688
|
+
async def __aenter__(self):
|
|
689
|
+
return self
|
|
690
|
+
|
|
691
|
+
async def __aexit__(self, exc, cls, tb):
|
|
692
|
+
if exc is None:
|
|
693
|
+
if await self.wait() is None: # pragma: no cover
|
|
694
|
+
# these lines are 100% covered by the tests but
|
|
695
|
+
# the coverage plugin cannot seem to see them...
|
|
696
|
+
events = ','.join(self.names)
|
|
697
|
+
mesg = f'timeout waiting for {self.count} event(s): {events}'
|
|
698
|
+
raise s_exc.TimeOut(mesg=mesg)
|
|
699
|
+
|
|
679
700
|
class BaseRef(Base):
|
|
680
701
|
'''
|
|
681
702
|
An object for managing multiple Base instances by name.
|
|
@@ -777,9 +798,6 @@ async def schedGenr(genr, maxsize=100):
|
|
|
777
798
|
|
|
778
799
|
await q.put((False, None))
|
|
779
800
|
|
|
780
|
-
except asyncio.CancelledError: # pragma: no cover TODO: remove once >= py 3.8 only
|
|
781
|
-
raise
|
|
782
|
-
|
|
783
801
|
except Exception:
|
|
784
802
|
if not base.isfini:
|
|
785
803
|
await q.put((False, None))
|
synapse/lib/cell.py
CHANGED
|
@@ -910,7 +910,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
910
910
|
},
|
|
911
911
|
'nexslog:async': {
|
|
912
912
|
'default': True,
|
|
913
|
-
'description': '
|
|
913
|
+
'description': 'Deprecated. This option ignored.',
|
|
914
914
|
'type': 'boolean',
|
|
915
915
|
'hidedocs': True,
|
|
916
916
|
'hidecmdl': True,
|
|
@@ -967,7 +967,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
967
967
|
'type': 'string',
|
|
968
968
|
},
|
|
969
969
|
'aha:network': {
|
|
970
|
-
'description': 'The AHA service network.
|
|
970
|
+
'description': 'The AHA service network.',
|
|
971
971
|
'type': 'string',
|
|
972
972
|
},
|
|
973
973
|
'aha:registry': {
|
|
@@ -1111,6 +1111,9 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1111
1111
|
self._checkspace = s_coro.Event()
|
|
1112
1112
|
self._reloadfuncs = {} # name -> func
|
|
1113
1113
|
|
|
1114
|
+
self.nexslock = asyncio.Lock()
|
|
1115
|
+
self.netready = asyncio.Event()
|
|
1116
|
+
|
|
1114
1117
|
self.conf = self._initCellConf(conf)
|
|
1115
1118
|
|
|
1116
1119
|
self.minfree = self.conf.get('limit:disk:free')
|
|
@@ -1181,23 +1184,32 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1181
1184
|
self.backlastexc = None # err, errmsg, errtrace of last backup
|
|
1182
1185
|
|
|
1183
1186
|
if self.conf.get('mirror') and not self.conf.get('nexslog:en'):
|
|
1184
|
-
|
|
1185
|
-
raise s_exc.BadConfValu(mesg=mesg)
|
|
1187
|
+
self.modCellConf({'nexslog:en': True})
|
|
1186
1188
|
|
|
1187
|
-
# construct our nexsroot instance ( but do not start it )
|
|
1188
1189
|
await s_nexus.Pusher.__anit__(self, self.iden)
|
|
1189
1190
|
|
|
1190
1191
|
self._initCertDir()
|
|
1191
1192
|
|
|
1192
|
-
|
|
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()
|
|
1193
1201
|
|
|
1194
|
-
|
|
1195
|
-
self.onfini(root.fini)
|
|
1196
|
-
root.onfini(self.fini)
|
|
1202
|
+
await self.initServiceEarly()
|
|
1197
1203
|
|
|
1198
|
-
self.
|
|
1204
|
+
nexsroot = await self._ctorNexsRoot()
|
|
1205
|
+
|
|
1206
|
+
self.setNexsRoot(nexsroot)
|
|
1207
|
+
|
|
1208
|
+
async def fini():
|
|
1209
|
+
await self.nexsroot.fini()
|
|
1210
|
+
|
|
1211
|
+
self.onfini(fini)
|
|
1199
1212
|
|
|
1200
|
-
await self._initCellSlab(readonly=readonly)
|
|
1201
1213
|
self.apikeydb = self.slab.initdb('user:apikeys') # apikey -> useriden
|
|
1202
1214
|
self.usermetadb = self.slab.initdb('user:meta') # useriden + <valu> -> dict valu
|
|
1203
1215
|
self.rolemetadb = self.slab.initdb('role:meta') # roleiden + <valu> -> dict valu
|
|
@@ -1272,12 +1284,6 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1272
1284
|
# initialize network backend infrastructure
|
|
1273
1285
|
await self._initAhaRegistry()
|
|
1274
1286
|
|
|
1275
|
-
# initialize network daemons (but do not listen yet)
|
|
1276
|
-
# to allow registration of callbacks and shared objects
|
|
1277
|
-
# within phase 2/4.
|
|
1278
|
-
await self._initCellHttp()
|
|
1279
|
-
await self._initCellDmon()
|
|
1280
|
-
|
|
1281
1287
|
# phase 2 - service storage
|
|
1282
1288
|
await self.initServiceStorage()
|
|
1283
1289
|
# phase 3 - nexus subsystem
|
|
@@ -1612,10 +1618,10 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1612
1618
|
|
|
1613
1619
|
async def _runFreeSpaceLoop(self):
|
|
1614
1620
|
|
|
1615
|
-
nexsroot = self.getCellNexsRoot()
|
|
1616
|
-
|
|
1617
1621
|
while not self.isfini:
|
|
1618
1622
|
|
|
1623
|
+
nexsroot = self.getCellNexsRoot()
|
|
1624
|
+
|
|
1619
1625
|
self._checkspace.clear()
|
|
1620
1626
|
|
|
1621
1627
|
disk = shutil.disk_usage(self.dirn)
|
|
@@ -1662,37 +1668,61 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1662
1668
|
|
|
1663
1669
|
await self.waitfini(self.SYSCTL_CHECK_FREQ)
|
|
1664
1670
|
|
|
1665
|
-
def _getAhaAdmin(self):
|
|
1666
|
-
name = self.conf.get('aha:admin')
|
|
1667
|
-
if name is not None:
|
|
1668
|
-
return name
|
|
1669
|
-
|
|
1670
1671
|
async def _initAhaRegistry(self):
|
|
1671
1672
|
|
|
1672
|
-
|
|
1673
|
-
if
|
|
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
|
|
1693
|
+
|
|
1694
|
+
self.modCellConf({'aha:registry': newurls})
|
|
1695
|
+
self.ahaclient.setBootUrls(newurls)
|
|
1674
1696
|
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
self.ahaclient = await s_telepath.Client.anit(info.get('url'))
|
|
1678
|
-
self.ahaclient._fini_atexit = True
|
|
1679
|
-
self.onfini(self.ahaclient)
|
|
1697
|
+
self.ahaclient = await s_telepath.Client.anit(ahaurls, onlink=onlink)
|
|
1698
|
+
self.onfini(self.ahaclient)
|
|
1680
1699
|
|
|
1681
|
-
async def
|
|
1682
|
-
await s_telepath.delAhaUrl(
|
|
1700
|
+
async def fini():
|
|
1701
|
+
await s_telepath.delAhaUrl(ahaurls)
|
|
1683
1702
|
|
|
1684
|
-
self.onfini(
|
|
1703
|
+
self.ahaclient.onfini(fini)
|
|
1685
1704
|
|
|
1705
|
+
ahaadmin = self.conf.get('aha:admin')
|
|
1686
1706
|
ahauser = self.conf.get('aha:user')
|
|
1687
|
-
ahanetw = self.conf.get('aha:network')
|
|
1688
1707
|
|
|
1689
|
-
ahaadmin = self._getAhaAdmin()
|
|
1690
1708
|
if ahaadmin is not None:
|
|
1691
1709
|
await self._addAdminUser(ahaadmin)
|
|
1692
1710
|
|
|
1693
1711
|
if ahauser is not None:
|
|
1694
1712
|
await self._addAdminUser(ahauser)
|
|
1695
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
|
+
|
|
1696
1726
|
async def _addAdminUser(self, username):
|
|
1697
1727
|
# add the user in a pre-nexus compatible way
|
|
1698
1728
|
user = await self.auth.getUserByName(username)
|
|
@@ -1708,6 +1738,9 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1708
1738
|
if user.isLocked():
|
|
1709
1739
|
await user.setLocked(False, logged=False)
|
|
1710
1740
|
|
|
1741
|
+
async def initServiceEarly(self):
|
|
1742
|
+
pass
|
|
1743
|
+
|
|
1711
1744
|
async def initServiceStorage(self):
|
|
1712
1745
|
pass
|
|
1713
1746
|
|
|
@@ -1720,7 +1753,11 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1720
1753
|
if self.minfree is not None:
|
|
1721
1754
|
self.schedCoro(self._runFreeSpaceLoop())
|
|
1722
1755
|
|
|
1723
|
-
async def
|
|
1756
|
+
async def _bindDmonListen(self):
|
|
1757
|
+
|
|
1758
|
+
# functionalized so downstream code can bind early.
|
|
1759
|
+
if self.sockaddr is not None:
|
|
1760
|
+
return
|
|
1724
1761
|
|
|
1725
1762
|
# start a unix local socket daemon listener
|
|
1726
1763
|
sockpath = os.path.join(self.dirn, 'sock')
|
|
@@ -1728,24 +1765,25 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1728
1765
|
|
|
1729
1766
|
try:
|
|
1730
1767
|
await self.dmon.listen(sockurl)
|
|
1731
|
-
except asyncio.CancelledError: # pragma: no cover TODO: remove once >= py 3.8 only
|
|
1732
|
-
raise
|
|
1733
1768
|
except OSError as e:
|
|
1734
1769
|
logger.error(f'Failed to listen on unix socket at: [{sockpath}][{e}]')
|
|
1735
1770
|
logger.error('LOCAL UNIX SOCKET WILL BE UNAVAILABLE')
|
|
1736
1771
|
except Exception: # pragma: no cover
|
|
1737
1772
|
logging.exception('Unknown dmon listen error.')
|
|
1738
|
-
raise
|
|
1739
1773
|
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
turl = self.conf.get('dmon:listen')
|
|
1774
|
+
turl = self._getDmonListen()
|
|
1743
1775
|
if turl is not None:
|
|
1744
|
-
self.sockaddr = await self.dmon.listen(turl)
|
|
1745
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()
|
|
1746
1782
|
|
|
1747
1783
|
await self._initAhaService()
|
|
1748
1784
|
|
|
1785
|
+
self.netready.set()
|
|
1786
|
+
|
|
1749
1787
|
port = self.conf.get('https:port')
|
|
1750
1788
|
if port is not None:
|
|
1751
1789
|
await self.addHttpsPort(port)
|
|
@@ -1804,27 +1842,34 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1804
1842
|
if ahaname is None:
|
|
1805
1843
|
return
|
|
1806
1844
|
|
|
1807
|
-
ahalead = self.conf.get('aha:leader')
|
|
1808
1845
|
ahanetw = self.conf.get('aha:network')
|
|
1846
|
+
if ahanetw is None:
|
|
1847
|
+
return
|
|
1809
1848
|
|
|
1810
1849
|
ahainfo = await self.getAhaInfo()
|
|
1811
1850
|
if ahainfo is None:
|
|
1812
1851
|
return
|
|
1813
1852
|
|
|
1853
|
+
ahalead = self.conf.get('aha:leader')
|
|
1854
|
+
|
|
1814
1855
|
self.ahasvcname = f'{ahaname}.{ahanetw}'
|
|
1815
1856
|
|
|
1816
1857
|
async def _runAhaRegLoop():
|
|
1817
1858
|
|
|
1818
1859
|
while not self.isfini:
|
|
1860
|
+
|
|
1819
1861
|
try:
|
|
1820
1862
|
proxy = await self.ahaclient.proxy()
|
|
1863
|
+
|
|
1821
1864
|
info = await self.getAhaInfo()
|
|
1822
1865
|
await proxy.addAhaSvc(ahaname, info, network=ahanetw)
|
|
1823
1866
|
if self.isactive and ahalead is not None:
|
|
1824
1867
|
await proxy.addAhaSvc(ahalead, info, network=ahanetw)
|
|
1868
|
+
|
|
1825
1869
|
except Exception as e:
|
|
1826
1870
|
logger.exception(f'Error registering service {self.ahasvcname} with AHA: {e}')
|
|
1827
1871
|
await self.waitfini(1)
|
|
1872
|
+
|
|
1828
1873
|
else:
|
|
1829
1874
|
await proxy.waitfini()
|
|
1830
1875
|
|
|
@@ -1905,6 +1950,11 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1905
1950
|
async def setNexsIndx(self, indx):
|
|
1906
1951
|
return await self.nexsroot.setindex(indx)
|
|
1907
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
|
+
|
|
1908
1958
|
async def promote(self, graceful=False):
|
|
1909
1959
|
'''
|
|
1910
1960
|
Transform this cell from a passive follower to
|
|
@@ -1915,48 +1965,39 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1915
1965
|
mesg = 'promote() called on non-mirror'
|
|
1916
1966
|
raise s_exc.BadConfValu(mesg=mesg)
|
|
1917
1967
|
|
|
1918
|
-
|
|
1919
|
-
logger.warning(f'PROMOTION: Performing leadership promotion graceful={graceful}
|
|
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}.')
|
|
1920
1970
|
|
|
1921
1971
|
if graceful:
|
|
1922
1972
|
|
|
1923
|
-
|
|
1924
|
-
mesg = 'Cannot gracefully promote without aha:name configured.'
|
|
1925
|
-
raise s_exc.BadArg(mesg=mesg)
|
|
1973
|
+
myurl = self.getMyUrl()
|
|
1926
1974
|
|
|
1927
|
-
|
|
1928
|
-
if ahanetw is None: # pragma: no cover
|
|
1929
|
-
mesg = 'Cannot gracefully promote without aha:network configured.'
|
|
1930
|
-
raise s_exc.BadArg(mesg=mesg)
|
|
1931
|
-
|
|
1932
|
-
myurl = f'aha://{ahaname}.{ahanetw}'
|
|
1933
|
-
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}.')
|
|
1934
1976
|
async with await s_telepath.openurl(mirurl) as lead:
|
|
1935
|
-
logger.debug(f'PROMOTION: Requesting leadership handoff to ahaname={ahaname}')
|
|
1936
1977
|
await lead.handoff(myurl)
|
|
1937
|
-
logger.warning(f'PROMOTION: Completed leadership handoff to
|
|
1978
|
+
logger.warning(f'PROMOTION: Completed leadership handoff to {myurl}{_dispname}')
|
|
1938
1979
|
return
|
|
1939
1980
|
|
|
1940
|
-
logger.debug(f'PROMOTION: Clearing mirror configuration
|
|
1981
|
+
logger.debug(f'PROMOTION: Clearing mirror configuration{_dispname}.')
|
|
1941
1982
|
self.modCellConf({'mirror': None})
|
|
1942
1983
|
|
|
1943
|
-
logger.debug(f'PROMOTION: Promoting the nexus root
|
|
1984
|
+
logger.debug(f'PROMOTION: Promoting the nexus root{_dispname}.')
|
|
1944
1985
|
await self.nexsroot.promote()
|
|
1945
1986
|
|
|
1946
|
-
logger.debug(f'PROMOTION: Setting the cell as active
|
|
1987
|
+
logger.debug(f'PROMOTION: Setting the cell as active{_dispname}.')
|
|
1947
1988
|
await self.setCellActive(True)
|
|
1948
1989
|
|
|
1949
|
-
logger.warning(f'PROMOTION: Finished leadership promotion
|
|
1990
|
+
logger.warning(f'PROMOTION: Finished leadership promotion{_dispname}.')
|
|
1950
1991
|
|
|
1951
1992
|
async def handoff(self, turl, timeout=30):
|
|
1952
1993
|
'''
|
|
1953
1994
|
Hand off leadership to a mirror in a transactional fashion.
|
|
1954
1995
|
'''
|
|
1955
|
-
|
|
1956
|
-
logger.warning(f'HANDOFF: Performing leadership handoff to {s_urlhelp.sanitizeUrl(turl)}
|
|
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}.')
|
|
1957
1998
|
async with await s_telepath.openurl(turl) as cell:
|
|
1958
1999
|
|
|
1959
|
-
logger.debug(f'HANDOFF: Connected to {s_urlhelp.sanitizeUrl(turl)}
|
|
2000
|
+
logger.debug(f'HANDOFF: Connected to {s_urlhelp.sanitizeUrl(turl)}{_dispname}.')
|
|
1960
2001
|
|
|
1961
2002
|
if self.iden != await cell.getCellIden(): # pragma: no cover
|
|
1962
2003
|
mesg = 'Mirror handoff remote cell iden does not match!'
|
|
@@ -1966,33 +2007,34 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1966
2007
|
mesg = 'Cannot handoff mirror leadership to myself!'
|
|
1967
2008
|
raise s_exc.BadArg(mesg=mesg)
|
|
1968
2009
|
|
|
1969
|
-
logger.debug(f'HANDOFF: Obtaining nexus
|
|
2010
|
+
logger.debug(f'HANDOFF: Obtaining nexus lock{_dispname}.')
|
|
1970
2011
|
|
|
1971
|
-
async with self.
|
|
2012
|
+
async with self.nexslock:
|
|
1972
2013
|
|
|
1973
|
-
logger.debug(f'HANDOFF: Obtained nexus
|
|
2014
|
+
logger.debug(f'HANDOFF: Obtained nexus lock{_dispname}.')
|
|
1974
2015
|
indx = await self.getNexsIndx()
|
|
1975
2016
|
|
|
1976
|
-
logger.debug(f'HANDOFF: Waiting {timeout} seconds for mirror to reach {indx=}
|
|
2017
|
+
logger.debug(f'HANDOFF: Waiting {timeout} seconds for mirror to reach {indx=}{_dispname}.')
|
|
1977
2018
|
if not await cell.waitNexsOffs(indx - 1, timeout=timeout): # pragma: no cover
|
|
1978
2019
|
mndx = await cell.getNexsIndx()
|
|
1979
2020
|
mesg = f'Remote mirror did not catch up in time: {mndx}/{indx}.'
|
|
1980
2021
|
raise s_exc.NotReady(mesg=mesg)
|
|
1981
2022
|
|
|
1982
|
-
logger.debug(f'HANDOFF: Mirror has caught up to the current leader, performing promotion
|
|
2023
|
+
logger.debug(f'HANDOFF: Mirror has caught up to the current leader, performing promotion{_dispname}.')
|
|
1983
2024
|
await cell.promote()
|
|
1984
2025
|
|
|
1985
|
-
logger.debug(f'HANDOFF: Setting the service as inactive
|
|
2026
|
+
logger.debug(f'HANDOFF: Setting the service as inactive{_dispname}.')
|
|
1986
2027
|
await self.setCellActive(False)
|
|
1987
2028
|
|
|
1988
|
-
logger.debug(f'HANDOFF: Configuring service to
|
|
2029
|
+
logger.debug(f'HANDOFF: Configuring service to sync from new leader{_dispname}.')
|
|
1989
2030
|
self.modCellConf({'mirror': turl})
|
|
1990
2031
|
|
|
1991
|
-
logger.debug(f'HANDOFF: Restarting the nexus
|
|
2032
|
+
logger.debug(f'HANDOFF: Restarting the nexus{_dispname}.')
|
|
1992
2033
|
await self.nexsroot.startup()
|
|
1993
2034
|
|
|
1994
|
-
logger.debug(f'HANDOFF: Released nexus
|
|
1995
|
-
|
|
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}.')
|
|
1996
2038
|
|
|
1997
2039
|
async def reqAhaProxy(self, timeout=None):
|
|
1998
2040
|
if self.ahaclient is None:
|
|
@@ -2025,7 +2067,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
2025
2067
|
await proxy.fini()
|
|
2026
2068
|
return
|
|
2027
2069
|
|
|
2028
|
-
ahanetw = self.conf.
|
|
2070
|
+
ahanetw = self.conf.req('aha:network')
|
|
2029
2071
|
try:
|
|
2030
2072
|
await proxy.addAhaSvc(ahalead, ahainfo, network=ahanetw)
|
|
2031
2073
|
except asyncio.CancelledError: # pragma: no cover
|
|
@@ -2292,7 +2334,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
2292
2334
|
|
|
2293
2335
|
try:
|
|
2294
2336
|
|
|
2295
|
-
async with self.
|
|
2337
|
+
async with self.nexslock:
|
|
2296
2338
|
|
|
2297
2339
|
logger.debug('Syncing LMDB Slabs')
|
|
2298
2340
|
|
|
@@ -3007,7 +3049,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3007
3049
|
sslctx = self.initSslCtx(certpath, pkeypath)
|
|
3008
3050
|
|
|
3009
3051
|
kwargs = {
|
|
3010
|
-
'xheaders': self.conf.
|
|
3052
|
+
'xheaders': self.conf.req('https:parse:proxy:remoteip')
|
|
3011
3053
|
}
|
|
3012
3054
|
serv = self.wapp.listen(port, address=addr, ssl_options=sslctx, **kwargs)
|
|
3013
3055
|
self.httpds.append(serv)
|
|
@@ -3158,9 +3200,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3158
3200
|
|
|
3159
3201
|
async def _initCellDmon(self):
|
|
3160
3202
|
|
|
3161
|
-
ahainfo = {
|
|
3162
|
-
'name': self.ahasvcname
|
|
3163
|
-
}
|
|
3203
|
+
ahainfo = {'name': self.ahasvcname}
|
|
3164
3204
|
|
|
3165
3205
|
self.dmon = await s_daemon.Daemon.anit(ahainfo=ahainfo)
|
|
3166
3206
|
self.dmon.share('*', self)
|
|
@@ -3174,6 +3214,12 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3174
3214
|
|
|
3175
3215
|
return hive
|
|
3176
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
|
+
|
|
3177
3223
|
async def _initCellSlab(self, readonly=False):
|
|
3178
3224
|
|
|
3179
3225
|
s_common.gendir(self.dirn, 'slabs')
|
|
@@ -3185,10 +3231,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3185
3231
|
_slab.initdb('hive')
|
|
3186
3232
|
await _slab.fini()
|
|
3187
3233
|
|
|
3188
|
-
self.slab = await
|
|
3189
|
-
self.slab.addResizeCallback(self.checkFreeSpace)
|
|
3190
|
-
|
|
3191
|
-
self.onfini(self.slab.fini)
|
|
3234
|
+
self.slab = await self._initSlabFile(path)
|
|
3192
3235
|
|
|
3193
3236
|
async def _initCellAuth(self):
|
|
3194
3237
|
|
|
@@ -3598,13 +3641,24 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3598
3641
|
return pars
|
|
3599
3642
|
|
|
3600
3643
|
async def _initCellBoot(self):
|
|
3644
|
+
# NOTE: best hook point for custom provisioning
|
|
3601
3645
|
|
|
3602
|
-
pnfo = await self._bootCellProv()
|
|
3646
|
+
isok, pnfo = await self._bootCellProv()
|
|
3603
3647
|
|
|
3604
3648
|
# check this before we setup loadTeleCell()
|
|
3605
3649
|
if not self._mustBootMirror():
|
|
3606
3650
|
return
|
|
3607
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
|
+
|
|
3608
3662
|
async with s_telepath.loadTeleCell(self.dirn):
|
|
3609
3663
|
await self._bootCellMirror(pnfo)
|
|
3610
3664
|
|
|
@@ -3736,7 +3790,8 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3736
3790
|
|
|
3737
3791
|
provurl = self.conf.get('aha:provision')
|
|
3738
3792
|
if provurl is None:
|
|
3739
|
-
return
|
|
3793
|
+
return False, {'mesg': 'No aha:provision configuration has been provided to allow the service to '
|
|
3794
|
+
'bootstrap via AHA.'}
|
|
3740
3795
|
|
|
3741
3796
|
doneiden = None
|
|
3742
3797
|
|
|
@@ -3749,7 +3804,8 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3749
3804
|
providen = urlinfo.get('path').strip('/')
|
|
3750
3805
|
|
|
3751
3806
|
if doneiden == providen:
|
|
3752
|
-
return
|
|
3807
|
+
return False, {'mesg': f'The aha:provision URL guid matches the service prov.done guid, '
|
|
3808
|
+
f'aha:provision={provurl}'}
|
|
3753
3809
|
|
|
3754
3810
|
logger.info(f'Provisioning {self.getCellType()} from AHA service.')
|
|
3755
3811
|
|
|
@@ -3809,7 +3865,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3809
3865
|
|
|
3810
3866
|
logger.info(f'Done provisioning {self.getCellType()} AHA service.')
|
|
3811
3867
|
|
|
3812
|
-
return provconf, providen
|
|
3868
|
+
return True, {'conf': provconf, 'iden': providen}
|
|
3813
3869
|
|
|
3814
3870
|
async def _bootProvConf(self, provconf):
|
|
3815
3871
|
'''
|
|
@@ -3891,23 +3947,14 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3891
3947
|
await self.nexsroot.enNexsLog()
|
|
3892
3948
|
await self.sync()
|
|
3893
3949
|
|
|
3894
|
-
async def
|
|
3895
|
-
# this function must assume almost nothing is initialized
|
|
3896
|
-
# but that's ok since it will only run rarely.
|
|
3897
|
-
# It assumes it has a tuple of (provisioning configuration, provisioning iden) available
|
|
3898
|
-
murl = self.conf.reqConfValu('mirror')
|
|
3899
|
-
provconf, providen = pnfo
|
|
3900
|
-
|
|
3901
|
-
logger.warning(f'Bootstrap mirror from: {murl} (this could take a while!)')
|
|
3950
|
+
async def _initCloneCell(self, proxy):
|
|
3902
3951
|
|
|
3903
3952
|
tarpath = s_common.genpath(self.dirn, 'tmp', 'bootstrap.tgz')
|
|
3904
|
-
|
|
3905
3953
|
try:
|
|
3906
3954
|
|
|
3907
|
-
|
|
3908
|
-
await cell.readyToMirror()
|
|
3955
|
+
await proxy.readyToMirror()
|
|
3909
3956
|
with s_common.genfile(tarpath) as fd:
|
|
3910
|
-
async for byts in
|
|
3957
|
+
async for byts in proxy.iterNewBackupArchive(remove=True):
|
|
3911
3958
|
fd.write(byts)
|
|
3912
3959
|
|
|
3913
3960
|
with tarfile.open(tarpath) as tgz:
|
|
@@ -3922,6 +3969,18 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3922
3969
|
if os.path.isfile(tarpath):
|
|
3923
3970
|
os.unlink(tarpath)
|
|
3924
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
|
+
|
|
3925
3984
|
# Remove aha:provision from cell.yaml if it exists and the iden differs.
|
|
3926
3985
|
mnfo = s_common.yamlload(self.dirn, 'cell.yaml')
|
|
3927
3986
|
if mnfo:
|
|
@@ -4017,15 +4076,12 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
4017
4076
|
|
|
4018
4077
|
try:
|
|
4019
4078
|
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
lisn = cell.conf.get('dmon:listen')
|
|
4025
|
-
if lisn is None:
|
|
4026
|
-
lisn = cell.getLocalUrl()
|
|
4079
|
+
turl = cell._getDmonListen()
|
|
4080
|
+
if turl is None:
|
|
4081
|
+
turl = opts.telepath
|
|
4082
|
+
await cell.dmon.listen(turl)
|
|
4027
4083
|
|
|
4028
|
-
|
|
4084
|
+
logger.info(f'...{cell.getCellType()} API (telepath): {turl}')
|
|
4029
4085
|
|
|
4030
4086
|
if 'https:port' not in cell.conf:
|
|
4031
4087
|
await cell.addHttpsPort(opts.https)
|
|
@@ -4267,6 +4323,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
4267
4323
|
'type': self.getCellType(),
|
|
4268
4324
|
'iden': self.getCellIden(),
|
|
4269
4325
|
'active': self.isactive,
|
|
4326
|
+
'started': self.startms,
|
|
4270
4327
|
'ready': self.nexsroot.ready.is_set(),
|
|
4271
4328
|
'commit': self.COMMIT,
|
|
4272
4329
|
'version': self.VERSION,
|