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.

Files changed (44) hide show
  1. synapse/cortex.py +8 -4
  2. synapse/lib/aha.py +361 -88
  3. synapse/lib/base.py +27 -9
  4. synapse/lib/cell.py +167 -110
  5. synapse/lib/config.py +15 -11
  6. synapse/lib/coro.py +13 -0
  7. synapse/lib/layer.py +0 -5
  8. synapse/lib/link.py +1 -1
  9. synapse/lib/lmdbslab.py +3 -3
  10. synapse/lib/nexus.py +24 -12
  11. synapse/lib/stormlib/imap.py +6 -2
  12. synapse/lib/stormlib/smtp.py +12 -2
  13. synapse/lib/version.py +2 -2
  14. synapse/telepath.py +32 -17
  15. synapse/tests/files/aha/certs/cas/synapse.crt +28 -0
  16. synapse/tests/files/aha/certs/cas/synapse.key +51 -0
  17. synapse/tests/files/aha/certs/hosts/00.aha.loop.vertex.link.crt +30 -0
  18. synapse/tests/files/aha/certs/hosts/00.aha.loop.vertex.link.key +51 -0
  19. synapse/tests/files/aha/certs/users/root@synapse.crt +29 -0
  20. synapse/tests/files/aha/certs/users/root@synapse.key +51 -0
  21. synapse/tests/files/rstorm/testsvc.py +1 -1
  22. synapse/tests/test_axon.py +1 -1
  23. synapse/tests/test_cortex.py +22 -59
  24. synapse/tests/test_lib_agenda.py +3 -3
  25. synapse/tests/test_lib_aha.py +336 -490
  26. synapse/tests/test_lib_base.py +20 -0
  27. synapse/tests/test_lib_cell.py +49 -22
  28. synapse/tests/test_lib_config.py +4 -3
  29. synapse/tests/test_lib_nexus.py +8 -0
  30. synapse/tests/test_lib_stormlib_aha.py +35 -35
  31. synapse/tests/test_lib_stormlib_cell.py +4 -15
  32. synapse/tests/test_lib_stormlib_imap.py +14 -3
  33. synapse/tests/test_lib_stormlib_smtp.py +51 -0
  34. synapse/tests/test_tools_aha.py +78 -101
  35. synapse/tests/utils.py +86 -120
  36. synapse/tools/aha/clone.py +50 -0
  37. synapse/tools/aha/enroll.py +2 -1
  38. synapse/tools/backup.py +2 -2
  39. synapse/tools/changelog.py +3 -1
  40. {synapse-2.177.0.dist-info → synapse-2.178.0.dist-info}/METADATA +48 -48
  41. {synapse-2.177.0.dist-info → synapse-2.178.0.dist-info}/RECORD +44 -37
  42. {synapse-2.177.0.dist-info → synapse-2.178.0.dist-info}/LICENSE +0 -0
  43. {synapse-2.177.0.dist-info → synapse-2.178.0.dist-info}/WHEEL +0 -0
  44. {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': 'Set to false to disable async memory mapping of the nexus change log.',
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. This makes aha:name/aha:leader relative names.',
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
- mesg = 'Mirror mode requires nexslog:en=True'
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
- 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()
1193
1201
 
1194
- # mutually assured destruction with our nexs root
1195
- self.onfini(root.fini)
1196
- root.onfini(self.fini)
1202
+ await self.initServiceEarly()
1197
1203
 
1198
- self.setNexsRoot(root)
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
- ahaurl = self.conf.get('aha:registry')
1673
- 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
1693
+
1694
+ self.modCellConf({'aha:registry': newurls})
1695
+ self.ahaclient.setBootUrls(newurls)
1674
1696
 
1675
- info = await s_telepath.addAhaUrl(ahaurl)
1676
- if self.ahaclient is None:
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 finiaha():
1682
- await s_telepath.delAhaUrl(ahaurl)
1700
+ async def fini():
1701
+ await s_telepath.delAhaUrl(ahaurls)
1683
1702
 
1684
- self.onfini(finiaha)
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 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
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
- self.sockaddr = None
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
- ahaname = self.conf.get('aha:name')
1919
- 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}.')
1920
1970
 
1921
1971
  if graceful:
1922
1972
 
1923
- if ahaname is None: # pragma: no cover
1924
- mesg = 'Cannot gracefully promote without aha:name configured.'
1925
- raise s_exc.BadArg(mesg=mesg)
1973
+ myurl = self.getMyUrl()
1926
1974
 
1927
- ahanetw = self.conf.get('aha:network')
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 ahaname={ahaname}')
1978
+ logger.warning(f'PROMOTION: Completed leadership handoff to {myurl}{_dispname}')
1938
1979
  return
1939
1980
 
1940
- logger.debug(f'PROMOTION: Clearing mirror configuration for ahaname={ahaname}')
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 for ahaname={ahaname}')
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 ahaname={ahaname}')
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 ahaname={ahaname}')
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
- ahaname = self.conf.get("aha:name")
1956
- 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}.')
1957
1998
  async with await s_telepath.openurl(turl) as cell:
1958
1999
 
1959
- 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}.')
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 applylock ahaname={ahaname}')
2010
+ logger.debug(f'HANDOFF: Obtaining nexus lock{_dispname}.')
1970
2011
 
1971
- async with self.nexsroot.applylock:
2012
+ async with self.nexslock:
1972
2013
 
1973
- logger.debug(f'HANDOFF: Obtained nexus applylock ahaname={ahaname}')
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=}, ahaname={ahaname}')
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 ahaname={ahaname}')
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 ahaname={ahaname}')
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 use the new leader as its mirror ahaname={ahaname}')
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 ahaname={ahaname}')
2032
+ logger.debug(f'HANDOFF: Restarting the nexus{_dispname}.')
1992
2033
  await self.nexsroot.startup()
1993
2034
 
1994
- logger.debug(f'HANDOFF: Released nexus applylock ahaname={ahaname}')
1995
- 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}.')
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.get('aha:network')
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.nexsroot.applylock:
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.reqConfValu('https:parse:proxy:remoteip')
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 s_lmdbslab.Slab.anit(path, map_size=SLAB_MAP_SIZE, readonly=readonly)
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 _bootCellMirror(self, pnfo):
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
- async with await s_telepath.openurl(murl) as cell:
3908
- await cell.readyToMirror()
3955
+ await proxy.readyToMirror()
3909
3956
  with s_common.genfile(tarpath) as fd:
3910
- async for byts in cell.iterNewBackupArchive(remove=True):
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
- if 'dmon:listen' not in cell.conf:
4021
- await cell.dmon.listen(opts.telepath)
4022
- logger.info(f'...{cell.getCellType()} API (telepath): {opts.telepath}')
4023
- else:
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
- logger.info(f'...{cell.getCellType()} API (telepath): {lisn}')
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,