synapse 2.177.0__py311-none-any.whl → 2.179.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 +170 -31
- synapse/datamodel.py +47 -1
- synapse/exc.py +1 -0
- synapse/lib/aha.py +362 -88
- synapse/lib/ast.py +26 -22
- synapse/lib/base.py +39 -12
- synapse/lib/cell.py +315 -119
- synapse/lib/config.py +15 -11
- synapse/lib/coro.py +27 -0
- synapse/lib/drive.py +551 -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/schemas.py +39 -0
- synapse/lib/snap.py +17 -7
- synapse/lib/storm.py +3 -1
- synapse/lib/stormhttp.py +1 -0
- synapse/lib/stormlib/imap.py +6 -2
- synapse/lib/stormlib/modelext.py +29 -3
- synapse/lib/stormlib/smtp.py +12 -2
- synapse/lib/stormlib/stix.py +40 -17
- synapse/lib/stormlib/vault.py +2 -2
- synapse/lib/stormtypes.py +1 -1
- synapse/lib/types.py +9 -0
- synapse/lib/version.py +2 -2
- synapse/lookup/pe.py +303 -38
- synapse/models/dns.py +24 -1
- synapse/models/geospace.py +4 -1
- synapse/models/infotech.py +26 -1
- 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 +67 -60
- synapse/tests/test_lib_agenda.py +3 -3
- synapse/tests/test_lib_aha.py +353 -490
- synapse/tests/test_lib_base.py +20 -0
- synapse/tests/test_lib_cell.py +273 -22
- synapse/tests/test_lib_config.py +4 -3
- synapse/tests/test_lib_coro.py +12 -0
- synapse/tests/test_lib_nexus.py +8 -0
- synapse/tests/test_lib_stormhttp.py +40 -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_modelext.py +55 -3
- synapse/tests/test_lib_stormlib_smtp.py +51 -0
- synapse/tests/test_lib_stormlib_stix.py +15 -0
- synapse/tests/test_lib_stormlib_vault.py +11 -1
- synapse/tests/test_lib_stormtypes.py +5 -0
- synapse/tests/test_lib_types.py +9 -0
- synapse/tests/test_model_dns.py +8 -0
- synapse/tests/test_model_geospace.py +3 -1
- synapse/tests/test_model_infotech.py +47 -0
- synapse/tests/test_model_syn.py +11 -0
- synapse/tests/test_tools_aha.py +78 -101
- synapse/tests/test_utils_stormcov.py +1 -1
- 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 +31 -1
- {synapse-2.177.0.dist-info → synapse-2.179.0.dist-info}/METADATA +48 -48
- {synapse-2.177.0.dist-info → synapse-2.179.0.dist-info}/RECORD +73 -65
- {synapse-2.177.0.dist-info → synapse-2.179.0.dist-info}/WHEEL +1 -1
- {synapse-2.177.0.dist-info → synapse-2.179.0.dist-info}/LICENSE +0 -0
- {synapse-2.177.0.dist-info → synapse-2.179.0.dist-info}/top_level.txt +0 -0
synapse/lib/cell.py
CHANGED
|
@@ -37,6 +37,7 @@ import synapse.lib.hive as s_hive
|
|
|
37
37
|
import synapse.lib.link as s_link
|
|
38
38
|
import synapse.lib.cache as s_cache
|
|
39
39
|
import synapse.lib.const as s_const
|
|
40
|
+
import synapse.lib.drive as s_drive
|
|
40
41
|
import synapse.lib.nexus as s_nexus
|
|
41
42
|
import synapse.lib.queue as s_queue
|
|
42
43
|
import synapse.lib.scope as s_scope
|
|
@@ -910,7 +911,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
910
911
|
},
|
|
911
912
|
'nexslog:async': {
|
|
912
913
|
'default': True,
|
|
913
|
-
'description': '
|
|
914
|
+
'description': 'Deprecated. This option ignored.',
|
|
914
915
|
'type': 'boolean',
|
|
915
916
|
'hidedocs': True,
|
|
916
917
|
'hidecmdl': True,
|
|
@@ -967,7 +968,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
967
968
|
'type': 'string',
|
|
968
969
|
},
|
|
969
970
|
'aha:network': {
|
|
970
|
-
'description': 'The AHA service network.
|
|
971
|
+
'description': 'The AHA service network.',
|
|
971
972
|
'type': 'string',
|
|
972
973
|
},
|
|
973
974
|
'aha:registry': {
|
|
@@ -1111,6 +1112,9 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1111
1112
|
self._checkspace = s_coro.Event()
|
|
1112
1113
|
self._reloadfuncs = {} # name -> func
|
|
1113
1114
|
|
|
1115
|
+
self.nexslock = asyncio.Lock()
|
|
1116
|
+
self.netready = asyncio.Event()
|
|
1117
|
+
|
|
1114
1118
|
self.conf = self._initCellConf(conf)
|
|
1115
1119
|
|
|
1116
1120
|
self.minfree = self.conf.get('limit:disk:free')
|
|
@@ -1181,23 +1185,32 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1181
1185
|
self.backlastexc = None # err, errmsg, errtrace of last backup
|
|
1182
1186
|
|
|
1183
1187
|
if self.conf.get('mirror') and not self.conf.get('nexslog:en'):
|
|
1184
|
-
|
|
1185
|
-
raise s_exc.BadConfValu(mesg=mesg)
|
|
1188
|
+
self.modCellConf({'nexslog:en': True})
|
|
1186
1189
|
|
|
1187
|
-
# construct our nexsroot instance ( but do not start it )
|
|
1188
1190
|
await s_nexus.Pusher.__anit__(self, self.iden)
|
|
1189
1191
|
|
|
1190
1192
|
self._initCertDir()
|
|
1191
1193
|
|
|
1192
|
-
|
|
1194
|
+
await self.enter_context(s_telepath.loadTeleCell(self.dirn))
|
|
1193
1195
|
|
|
1194
|
-
|
|
1195
|
-
self.onfini(root.fini)
|
|
1196
|
-
root.onfini(self.fini)
|
|
1196
|
+
await self._initCellSlab(readonly=readonly)
|
|
1197
1197
|
|
|
1198
|
-
|
|
1198
|
+
# initialize network daemons (but do not listen yet)
|
|
1199
|
+
# to allow registration of callbacks and shared objects
|
|
1200
|
+
await self._initCellHttp()
|
|
1201
|
+
await self._initCellDmon()
|
|
1202
|
+
|
|
1203
|
+
await self.initServiceEarly()
|
|
1204
|
+
|
|
1205
|
+
nexsroot = await self._ctorNexsRoot()
|
|
1206
|
+
|
|
1207
|
+
self.setNexsRoot(nexsroot)
|
|
1208
|
+
|
|
1209
|
+
async def fini():
|
|
1210
|
+
await self.nexsroot.fini()
|
|
1211
|
+
|
|
1212
|
+
self.onfini(fini)
|
|
1199
1213
|
|
|
1200
|
-
await self._initCellSlab(readonly=readonly)
|
|
1201
1214
|
self.apikeydb = self.slab.initdb('user:apikeys') # apikey -> useriden
|
|
1202
1215
|
self.usermetadb = self.slab.initdb('user:meta') # useriden + <valu> -> dict valu
|
|
1203
1216
|
self.rolemetadb = self.slab.initdb('role:meta') # roleiden + <valu> -> dict valu
|
|
@@ -1272,14 +1285,10 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1272
1285
|
# initialize network backend infrastructure
|
|
1273
1286
|
await self._initAhaRegistry()
|
|
1274
1287
|
|
|
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
1288
|
# phase 2 - service storage
|
|
1289
|
+
await self.initCellStorage()
|
|
1282
1290
|
await self.initServiceStorage()
|
|
1291
|
+
|
|
1283
1292
|
# phase 3 - nexus subsystem
|
|
1284
1293
|
await self.initNexusSubsystem()
|
|
1285
1294
|
|
|
@@ -1612,10 +1621,10 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1612
1621
|
|
|
1613
1622
|
async def _runFreeSpaceLoop(self):
|
|
1614
1623
|
|
|
1615
|
-
nexsroot = self.getCellNexsRoot()
|
|
1616
|
-
|
|
1617
1624
|
while not self.isfini:
|
|
1618
1625
|
|
|
1626
|
+
nexsroot = self.getCellNexsRoot()
|
|
1627
|
+
|
|
1619
1628
|
self._checkspace.clear()
|
|
1620
1629
|
|
|
1621
1630
|
disk = shutil.disk_usage(self.dirn)
|
|
@@ -1662,37 +1671,61 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1662
1671
|
|
|
1663
1672
|
await self.waitfini(self.SYSCTL_CHECK_FREQ)
|
|
1664
1673
|
|
|
1665
|
-
def _getAhaAdmin(self):
|
|
1666
|
-
name = self.conf.get('aha:admin')
|
|
1667
|
-
if name is not None:
|
|
1668
|
-
return name
|
|
1669
|
-
|
|
1670
1674
|
async def _initAhaRegistry(self):
|
|
1671
1675
|
|
|
1672
|
-
|
|
1673
|
-
if
|
|
1676
|
+
ahaurls = self.conf.get('aha:registry')
|
|
1677
|
+
if ahaurls is not None:
|
|
1678
|
+
|
|
1679
|
+
await s_telepath.addAhaUrl(ahaurls)
|
|
1680
|
+
if self.ahaclient is not None:
|
|
1681
|
+
await self.ahaclient.fini()
|
|
1682
|
+
|
|
1683
|
+
async def onlink(proxy):
|
|
1684
|
+
ahauser = self.conf.get('aha:user', 'root')
|
|
1685
|
+
newurls = await proxy.getAhaUrls(user=ahauser)
|
|
1686
|
+
oldurls = self.conf.get('aha:registry')
|
|
1687
|
+
if isinstance(oldurls, str):
|
|
1688
|
+
oldurls = (oldurls,)
|
|
1689
|
+
elif isinstance(oldurls, list):
|
|
1690
|
+
oldurls = tuple(oldurls)
|
|
1691
|
+
if newurls and newurls != oldurls:
|
|
1692
|
+
if oldurls[0].startswith('tcp://'):
|
|
1693
|
+
s_common.deprecated('aha:registry: tcp:// client values.')
|
|
1694
|
+
logger.warning('tcp:// based aha:registry options are deprecated and will be removed in Synapse v3.0.0')
|
|
1695
|
+
return
|
|
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.modCellConf({'aha:registry': newurls})
|
|
1698
|
+
self.ahaclient.setBootUrls(newurls)
|
|
1680
1699
|
|
|
1681
|
-
|
|
1682
|
-
|
|
1700
|
+
self.ahaclient = await s_telepath.Client.anit(ahaurls, onlink=onlink)
|
|
1701
|
+
self.onfini(self.ahaclient)
|
|
1683
1702
|
|
|
1684
|
-
|
|
1703
|
+
async def fini():
|
|
1704
|
+
await s_telepath.delAhaUrl(ahaurls)
|
|
1705
|
+
|
|
1706
|
+
self.ahaclient.onfini(fini)
|
|
1685
1707
|
|
|
1708
|
+
ahaadmin = self.conf.get('aha:admin')
|
|
1686
1709
|
ahauser = self.conf.get('aha:user')
|
|
1687
|
-
ahanetw = self.conf.get('aha:network')
|
|
1688
1710
|
|
|
1689
|
-
ahaadmin = self._getAhaAdmin()
|
|
1690
1711
|
if ahaadmin is not None:
|
|
1691
1712
|
await self._addAdminUser(ahaadmin)
|
|
1692
1713
|
|
|
1693
1714
|
if ahauser is not None:
|
|
1694
1715
|
await self._addAdminUser(ahauser)
|
|
1695
1716
|
|
|
1717
|
+
def _getDmonListen(self):
|
|
1718
|
+
|
|
1719
|
+
lisn = self.conf.get('dmon:listen', s_common.novalu)
|
|
1720
|
+
if lisn is not s_common.novalu:
|
|
1721
|
+
return lisn
|
|
1722
|
+
|
|
1723
|
+
ahaname = self.conf.get('aha:name')
|
|
1724
|
+
ahanetw = self.conf.get('aha:network')
|
|
1725
|
+
if ahaname is not None and ahanetw is not None:
|
|
1726
|
+
hostname = f'{ahaname}.{ahanetw}'
|
|
1727
|
+
return f'ssl://0.0.0.0:0?hostname={hostname}&ca={ahanetw}'
|
|
1728
|
+
|
|
1696
1729
|
async def _addAdminUser(self, username):
|
|
1697
1730
|
# add the user in a pre-nexus compatible way
|
|
1698
1731
|
user = await self.auth.getUserByName(username)
|
|
@@ -1708,6 +1741,132 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1708
1741
|
if user.isLocked():
|
|
1709
1742
|
await user.setLocked(False, logged=False)
|
|
1710
1743
|
|
|
1744
|
+
async def initServiceEarly(self):
|
|
1745
|
+
pass
|
|
1746
|
+
|
|
1747
|
+
async def initCellStorage(self):
|
|
1748
|
+
self.drive = await s_drive.Drive.anit(self.slab, 'celldrive')
|
|
1749
|
+
self.onfini(self.drive.fini)
|
|
1750
|
+
|
|
1751
|
+
async def addDriveItem(self, info, path=None, reldir=s_drive.rootdir):
|
|
1752
|
+
|
|
1753
|
+
iden = info.get('iden')
|
|
1754
|
+
if iden is None:
|
|
1755
|
+
info['iden'] = s_common.guid()
|
|
1756
|
+
|
|
1757
|
+
info.setdefault('created', s_common.now())
|
|
1758
|
+
info.setdefault('creator', self.auth.rootuser.iden)
|
|
1759
|
+
|
|
1760
|
+
return await self._push('drive:add', info, path=path, reldir=reldir)
|
|
1761
|
+
|
|
1762
|
+
@s_nexus.Pusher.onPush('drive:add')
|
|
1763
|
+
async def _addDriveItem(self, info, path=None, reldir=s_drive.rootdir):
|
|
1764
|
+
|
|
1765
|
+
# replay safety...
|
|
1766
|
+
iden = info.get('iden')
|
|
1767
|
+
if self.drive.hasItemInfo(iden): # pragma: no cover
|
|
1768
|
+
return await self.drive.getItemPath(iden)
|
|
1769
|
+
|
|
1770
|
+
return await self.drive.addItemInfo(info, path=path, reldir=reldir)
|
|
1771
|
+
|
|
1772
|
+
async def getDriveInfo(self, iden):
|
|
1773
|
+
return self.drive.getItemInfo(iden)
|
|
1774
|
+
|
|
1775
|
+
async def getDrivePath(self, path, reldir=s_drive.rootdir):
|
|
1776
|
+
'''
|
|
1777
|
+
Return a list of drive info elements for each step in path.
|
|
1778
|
+
|
|
1779
|
+
This may be used as a sort of "open" which returns all the
|
|
1780
|
+
path info entries. You may then operate directly on drive iden
|
|
1781
|
+
entries and/or check easyperm entries on them before you do...
|
|
1782
|
+
'''
|
|
1783
|
+
return await self.drive.getPathInfo(path, reldir=reldir)
|
|
1784
|
+
|
|
1785
|
+
async def addDrivePath(self, path, perm=None, reldir=s_drive.rootdir):
|
|
1786
|
+
'''
|
|
1787
|
+
Create the given path using the specified permissions.
|
|
1788
|
+
|
|
1789
|
+
The specified permissions are only used when creating new directories.
|
|
1790
|
+
|
|
1791
|
+
NOTE: We must do this outside the Drive class to allow us to generate
|
|
1792
|
+
iden and tick but remain nexus compatible.
|
|
1793
|
+
'''
|
|
1794
|
+
tick = s_common.now()
|
|
1795
|
+
user = self.auth.rootuser.iden
|
|
1796
|
+
path = self.drive.getPathNorm(path)
|
|
1797
|
+
|
|
1798
|
+
if perm is None:
|
|
1799
|
+
perm = {'users': {}, 'roles': {}}
|
|
1800
|
+
|
|
1801
|
+
for name in path:
|
|
1802
|
+
|
|
1803
|
+
info = self.drive.getStepInfo(reldir, name)
|
|
1804
|
+
await asyncio.sleep(0)
|
|
1805
|
+
|
|
1806
|
+
if info is not None:
|
|
1807
|
+
reldir = info.get('iden')
|
|
1808
|
+
continue
|
|
1809
|
+
|
|
1810
|
+
info = {
|
|
1811
|
+
'name': name,
|
|
1812
|
+
'perm': perm,
|
|
1813
|
+
'iden': s_common.guid(),
|
|
1814
|
+
'created': tick,
|
|
1815
|
+
'creator': user,
|
|
1816
|
+
}
|
|
1817
|
+
pathinfo = await self.addDriveItem(info, reldir=reldir)
|
|
1818
|
+
reldir = pathinfo[-1].get('iden')
|
|
1819
|
+
|
|
1820
|
+
return await self.drive.getItemPath(reldir)
|
|
1821
|
+
|
|
1822
|
+
async def getDriveData(self, iden, vers=None):
|
|
1823
|
+
'''
|
|
1824
|
+
Return the data associated with the drive item by iden.
|
|
1825
|
+
If vers is specified, return that specific version.
|
|
1826
|
+
'''
|
|
1827
|
+
return self.drive.getItemData(iden, vers=vers)
|
|
1828
|
+
|
|
1829
|
+
async def getDriveDataVersions(self, iden):
|
|
1830
|
+
async for item in self.drive.getItemDataVersions(iden):
|
|
1831
|
+
yield item
|
|
1832
|
+
|
|
1833
|
+
@s_nexus.Pusher.onPushAuto('drive:del')
|
|
1834
|
+
async def delDriveInfo(self, iden):
|
|
1835
|
+
if self.drive.getItemInfo(iden) is not None:
|
|
1836
|
+
await self.drive.delItemInfo(iden)
|
|
1837
|
+
|
|
1838
|
+
@s_nexus.Pusher.onPushAuto('drive:set:perm')
|
|
1839
|
+
async def setDriveInfoPerm(self, iden, perm):
|
|
1840
|
+
return self.drive.setItemPerm(iden, perm)
|
|
1841
|
+
|
|
1842
|
+
@s_nexus.Pusher.onPushAuto('drive:set:path')
|
|
1843
|
+
async def setDriveInfoPath(self, iden, path):
|
|
1844
|
+
|
|
1845
|
+
path = self.drive.getPathNorm(path)
|
|
1846
|
+
pathinfo = await self.drive.getItemPath(iden)
|
|
1847
|
+
if path == [p.get('name') for p in pathinfo]:
|
|
1848
|
+
return pathinfo
|
|
1849
|
+
|
|
1850
|
+
return await self.drive.setItemPath(iden, path)
|
|
1851
|
+
|
|
1852
|
+
@s_nexus.Pusher.onPushAuto('drive:data:set')
|
|
1853
|
+
async def setDriveData(self, iden, versinfo, data):
|
|
1854
|
+
return self.drive.setItemData(iden, versinfo, data)
|
|
1855
|
+
|
|
1856
|
+
async def delDriveData(self, iden, vers=None):
|
|
1857
|
+
if vers is None:
|
|
1858
|
+
info = self.drive.reqItemInfo(iden)
|
|
1859
|
+
vers = info.get('version')
|
|
1860
|
+
return await self._push('drive:data:del', iden, vers)
|
|
1861
|
+
|
|
1862
|
+
@s_nexus.Pusher.onPush('drive:data:del')
|
|
1863
|
+
async def _delDriveData(self, iden, vers):
|
|
1864
|
+
return self.drive.delItemData(iden, vers)
|
|
1865
|
+
|
|
1866
|
+
async def getDriveKids(self, iden):
|
|
1867
|
+
async for info in self.drive.getItemKids(iden):
|
|
1868
|
+
yield info
|
|
1869
|
+
|
|
1711
1870
|
async def initServiceStorage(self):
|
|
1712
1871
|
pass
|
|
1713
1872
|
|
|
@@ -1720,7 +1879,11 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1720
1879
|
if self.minfree is not None:
|
|
1721
1880
|
self.schedCoro(self._runFreeSpaceLoop())
|
|
1722
1881
|
|
|
1723
|
-
async def
|
|
1882
|
+
async def _bindDmonListen(self):
|
|
1883
|
+
|
|
1884
|
+
# functionalized so downstream code can bind early.
|
|
1885
|
+
if self.sockaddr is not None:
|
|
1886
|
+
return
|
|
1724
1887
|
|
|
1725
1888
|
# start a unix local socket daemon listener
|
|
1726
1889
|
sockpath = os.path.join(self.dirn, 'sock')
|
|
@@ -1728,24 +1891,25 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1728
1891
|
|
|
1729
1892
|
try:
|
|
1730
1893
|
await self.dmon.listen(sockurl)
|
|
1731
|
-
except asyncio.CancelledError: # pragma: no cover TODO: remove once >= py 3.8 only
|
|
1732
|
-
raise
|
|
1733
1894
|
except OSError as e:
|
|
1734
1895
|
logger.error(f'Failed to listen on unix socket at: [{sockpath}][{e}]')
|
|
1735
1896
|
logger.error('LOCAL UNIX SOCKET WILL BE UNAVAILABLE')
|
|
1736
1897
|
except Exception: # pragma: no cover
|
|
1737
1898
|
logging.exception('Unknown dmon listen error.')
|
|
1738
|
-
raise
|
|
1739
1899
|
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
turl = self.conf.get('dmon:listen')
|
|
1900
|
+
turl = self._getDmonListen()
|
|
1743
1901
|
if turl is not None:
|
|
1744
|
-
self.sockaddr = await self.dmon.listen(turl)
|
|
1745
1902
|
logger.info(f'dmon listening: {turl}')
|
|
1903
|
+
self.sockaddr = await self.dmon.listen(turl)
|
|
1904
|
+
|
|
1905
|
+
async def initServiceNetwork(self):
|
|
1906
|
+
|
|
1907
|
+
await self._bindDmonListen()
|
|
1746
1908
|
|
|
1747
1909
|
await self._initAhaService()
|
|
1748
1910
|
|
|
1911
|
+
self.netready.set()
|
|
1912
|
+
|
|
1749
1913
|
port = self.conf.get('https:port')
|
|
1750
1914
|
if port is not None:
|
|
1751
1915
|
await self.addHttpsPort(port)
|
|
@@ -1804,27 +1968,34 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1804
1968
|
if ahaname is None:
|
|
1805
1969
|
return
|
|
1806
1970
|
|
|
1807
|
-
ahalead = self.conf.get('aha:leader')
|
|
1808
1971
|
ahanetw = self.conf.get('aha:network')
|
|
1972
|
+
if ahanetw is None:
|
|
1973
|
+
return
|
|
1809
1974
|
|
|
1810
1975
|
ahainfo = await self.getAhaInfo()
|
|
1811
1976
|
if ahainfo is None:
|
|
1812
1977
|
return
|
|
1813
1978
|
|
|
1979
|
+
ahalead = self.conf.get('aha:leader')
|
|
1980
|
+
|
|
1814
1981
|
self.ahasvcname = f'{ahaname}.{ahanetw}'
|
|
1815
1982
|
|
|
1816
1983
|
async def _runAhaRegLoop():
|
|
1817
1984
|
|
|
1818
1985
|
while not self.isfini:
|
|
1986
|
+
|
|
1819
1987
|
try:
|
|
1820
1988
|
proxy = await self.ahaclient.proxy()
|
|
1989
|
+
|
|
1821
1990
|
info = await self.getAhaInfo()
|
|
1822
1991
|
await proxy.addAhaSvc(ahaname, info, network=ahanetw)
|
|
1823
1992
|
if self.isactive and ahalead is not None:
|
|
1824
1993
|
await proxy.addAhaSvc(ahalead, info, network=ahanetw)
|
|
1994
|
+
|
|
1825
1995
|
except Exception as e:
|
|
1826
1996
|
logger.exception(f'Error registering service {self.ahasvcname} with AHA: {e}')
|
|
1827
1997
|
await self.waitfini(1)
|
|
1998
|
+
|
|
1828
1999
|
else:
|
|
1829
2000
|
await proxy.waitfini()
|
|
1830
2001
|
|
|
@@ -1905,6 +2076,11 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1905
2076
|
async def setNexsIndx(self, indx):
|
|
1906
2077
|
return await self.nexsroot.setindex(indx)
|
|
1907
2078
|
|
|
2079
|
+
def getMyUrl(self, user='root'):
|
|
2080
|
+
host = self.conf.req('aha:name')
|
|
2081
|
+
network = self.conf.req('aha:network')
|
|
2082
|
+
return f'aha://{host}.{network}'
|
|
2083
|
+
|
|
1908
2084
|
async def promote(self, graceful=False):
|
|
1909
2085
|
'''
|
|
1910
2086
|
Transform this cell from a passive follower to
|
|
@@ -1915,48 +2091,39 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1915
2091
|
mesg = 'promote() called on non-mirror'
|
|
1916
2092
|
raise s_exc.BadConfValu(mesg=mesg)
|
|
1917
2093
|
|
|
1918
|
-
|
|
1919
|
-
logger.warning(f'PROMOTION: Performing leadership promotion graceful={graceful}
|
|
2094
|
+
_dispname = f' ahaname={self.conf.get("aha:name")}' if self.conf.get('aha:name') else ''
|
|
2095
|
+
logger.warning(f'PROMOTION: Performing leadership promotion graceful={graceful}{_dispname}.')
|
|
1920
2096
|
|
|
1921
2097
|
if graceful:
|
|
1922
2098
|
|
|
1923
|
-
|
|
1924
|
-
mesg = 'Cannot gracefully promote without aha:name configured.'
|
|
1925
|
-
raise s_exc.BadArg(mesg=mesg)
|
|
2099
|
+
myurl = self.getMyUrl()
|
|
1926
2100
|
|
|
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}')
|
|
2101
|
+
logger.debug(f'PROMOTION: Connecting to {mirurl} to request leadership handoff{_dispname}.')
|
|
1934
2102
|
async with await s_telepath.openurl(mirurl) as lead:
|
|
1935
|
-
logger.debug(f'PROMOTION: Requesting leadership handoff to ahaname={ahaname}')
|
|
1936
2103
|
await lead.handoff(myurl)
|
|
1937
|
-
logger.warning(f'PROMOTION: Completed leadership handoff to
|
|
2104
|
+
logger.warning(f'PROMOTION: Completed leadership handoff to {myurl}{_dispname}')
|
|
1938
2105
|
return
|
|
1939
2106
|
|
|
1940
|
-
logger.debug(f'PROMOTION: Clearing mirror configuration
|
|
2107
|
+
logger.debug(f'PROMOTION: Clearing mirror configuration{_dispname}.')
|
|
1941
2108
|
self.modCellConf({'mirror': None})
|
|
1942
2109
|
|
|
1943
|
-
logger.debug(f'PROMOTION: Promoting the nexus root
|
|
2110
|
+
logger.debug(f'PROMOTION: Promoting the nexus root{_dispname}.')
|
|
1944
2111
|
await self.nexsroot.promote()
|
|
1945
2112
|
|
|
1946
|
-
logger.debug(f'PROMOTION: Setting the cell as active
|
|
2113
|
+
logger.debug(f'PROMOTION: Setting the cell as active{_dispname}.')
|
|
1947
2114
|
await self.setCellActive(True)
|
|
1948
2115
|
|
|
1949
|
-
logger.warning(f'PROMOTION: Finished leadership promotion
|
|
2116
|
+
logger.warning(f'PROMOTION: Finished leadership promotion{_dispname}.')
|
|
1950
2117
|
|
|
1951
2118
|
async def handoff(self, turl, timeout=30):
|
|
1952
2119
|
'''
|
|
1953
2120
|
Hand off leadership to a mirror in a transactional fashion.
|
|
1954
2121
|
'''
|
|
1955
|
-
|
|
1956
|
-
logger.warning(f'HANDOFF: Performing leadership handoff to {s_urlhelp.sanitizeUrl(turl)}
|
|
2122
|
+
_dispname = f' ahaname={self.conf.get("aha:name")}' if self.conf.get('aha:name') else ''
|
|
2123
|
+
logger.warning(f'HANDOFF: Performing leadership handoff to {s_urlhelp.sanitizeUrl(turl)}{_dispname}.')
|
|
1957
2124
|
async with await s_telepath.openurl(turl) as cell:
|
|
1958
2125
|
|
|
1959
|
-
logger.debug(f'HANDOFF: Connected to {s_urlhelp.sanitizeUrl(turl)}
|
|
2126
|
+
logger.debug(f'HANDOFF: Connected to {s_urlhelp.sanitizeUrl(turl)}{_dispname}.')
|
|
1960
2127
|
|
|
1961
2128
|
if self.iden != await cell.getCellIden(): # pragma: no cover
|
|
1962
2129
|
mesg = 'Mirror handoff remote cell iden does not match!'
|
|
@@ -1966,33 +2133,34 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
1966
2133
|
mesg = 'Cannot handoff mirror leadership to myself!'
|
|
1967
2134
|
raise s_exc.BadArg(mesg=mesg)
|
|
1968
2135
|
|
|
1969
|
-
logger.debug(f'HANDOFF: Obtaining nexus
|
|
2136
|
+
logger.debug(f'HANDOFF: Obtaining nexus lock{_dispname}.')
|
|
1970
2137
|
|
|
1971
|
-
async with self.
|
|
2138
|
+
async with self.nexslock:
|
|
1972
2139
|
|
|
1973
|
-
logger.debug(f'HANDOFF: Obtained nexus
|
|
2140
|
+
logger.debug(f'HANDOFF: Obtained nexus lock{_dispname}.')
|
|
1974
2141
|
indx = await self.getNexsIndx()
|
|
1975
2142
|
|
|
1976
|
-
logger.debug(f'HANDOFF: Waiting {timeout} seconds for mirror to reach {indx=}
|
|
2143
|
+
logger.debug(f'HANDOFF: Waiting {timeout} seconds for mirror to reach {indx=}{_dispname}.')
|
|
1977
2144
|
if not await cell.waitNexsOffs(indx - 1, timeout=timeout): # pragma: no cover
|
|
1978
2145
|
mndx = await cell.getNexsIndx()
|
|
1979
2146
|
mesg = f'Remote mirror did not catch up in time: {mndx}/{indx}.'
|
|
1980
2147
|
raise s_exc.NotReady(mesg=mesg)
|
|
1981
2148
|
|
|
1982
|
-
logger.debug(f'HANDOFF: Mirror has caught up to the current leader, performing promotion
|
|
2149
|
+
logger.debug(f'HANDOFF: Mirror has caught up to the current leader, performing promotion{_dispname}.')
|
|
1983
2150
|
await cell.promote()
|
|
1984
2151
|
|
|
1985
|
-
logger.debug(f'HANDOFF: Setting the service as inactive
|
|
2152
|
+
logger.debug(f'HANDOFF: Setting the service as inactive{_dispname}.')
|
|
1986
2153
|
await self.setCellActive(False)
|
|
1987
2154
|
|
|
1988
|
-
logger.debug(f'HANDOFF: Configuring service to
|
|
2155
|
+
logger.debug(f'HANDOFF: Configuring service to sync from new leader{_dispname}.')
|
|
1989
2156
|
self.modCellConf({'mirror': turl})
|
|
1990
2157
|
|
|
1991
|
-
logger.debug(f'HANDOFF: Restarting the nexus
|
|
2158
|
+
logger.debug(f'HANDOFF: Restarting the nexus{_dispname}.')
|
|
1992
2159
|
await self.nexsroot.startup()
|
|
1993
2160
|
|
|
1994
|
-
logger.debug(f'HANDOFF: Released nexus
|
|
1995
|
-
|
|
2161
|
+
logger.debug(f'HANDOFF: Released nexus lock{_dispname}.')
|
|
2162
|
+
|
|
2163
|
+
logger.warning(f'HANDOFF: Done performing the leadership handoff with {s_urlhelp.sanitizeUrl(turl)}{_dispname}.')
|
|
1996
2164
|
|
|
1997
2165
|
async def reqAhaProxy(self, timeout=None):
|
|
1998
2166
|
if self.ahaclient is None:
|
|
@@ -2025,7 +2193,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
2025
2193
|
await proxy.fini()
|
|
2026
2194
|
return
|
|
2027
2195
|
|
|
2028
|
-
ahanetw = self.conf.
|
|
2196
|
+
ahanetw = self.conf.req('aha:network')
|
|
2029
2197
|
try:
|
|
2030
2198
|
await proxy.addAhaSvc(ahalead, ahainfo, network=ahanetw)
|
|
2031
2199
|
except asyncio.CancelledError: # pragma: no cover
|
|
@@ -2292,7 +2460,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
2292
2460
|
|
|
2293
2461
|
try:
|
|
2294
2462
|
|
|
2295
|
-
async with self.
|
|
2463
|
+
async with self.nexslock:
|
|
2296
2464
|
|
|
2297
2465
|
logger.debug('Syncing LMDB Slabs')
|
|
2298
2466
|
|
|
@@ -3007,7 +3175,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3007
3175
|
sslctx = self.initSslCtx(certpath, pkeypath)
|
|
3008
3176
|
|
|
3009
3177
|
kwargs = {
|
|
3010
|
-
'xheaders': self.conf.
|
|
3178
|
+
'xheaders': self.conf.req('https:parse:proxy:remoteip')
|
|
3011
3179
|
}
|
|
3012
3180
|
serv = self.wapp.listen(port, address=addr, ssl_options=sslctx, **kwargs)
|
|
3013
3181
|
self.httpds.append(serv)
|
|
@@ -3158,9 +3326,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3158
3326
|
|
|
3159
3327
|
async def _initCellDmon(self):
|
|
3160
3328
|
|
|
3161
|
-
ahainfo = {
|
|
3162
|
-
'name': self.ahasvcname
|
|
3163
|
-
}
|
|
3329
|
+
ahainfo = {'name': self.ahasvcname}
|
|
3164
3330
|
|
|
3165
3331
|
self.dmon = await s_daemon.Daemon.anit(ahainfo=ahainfo)
|
|
3166
3332
|
self.dmon.share('*', self)
|
|
@@ -3174,6 +3340,15 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3174
3340
|
|
|
3175
3341
|
return hive
|
|
3176
3342
|
|
|
3343
|
+
async def _initSlabFile(self, path, readonly=False, ephemeral=False):
|
|
3344
|
+
slab = await s_lmdbslab.Slab.anit(path, map_size=SLAB_MAP_SIZE, readonly=readonly)
|
|
3345
|
+
slab.addResizeCallback(self.checkFreeSpace)
|
|
3346
|
+
fini = slab.fini
|
|
3347
|
+
if ephemeral:
|
|
3348
|
+
fini = slab
|
|
3349
|
+
self.onfini(fini)
|
|
3350
|
+
return slab
|
|
3351
|
+
|
|
3177
3352
|
async def _initCellSlab(self, readonly=False):
|
|
3178
3353
|
|
|
3179
3354
|
s_common.gendir(self.dirn, 'slabs')
|
|
@@ -3185,10 +3360,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3185
3360
|
_slab.initdb('hive')
|
|
3186
3361
|
await _slab.fini()
|
|
3187
3362
|
|
|
3188
|
-
self.slab = await
|
|
3189
|
-
self.slab.addResizeCallback(self.checkFreeSpace)
|
|
3190
|
-
|
|
3191
|
-
self.onfini(self.slab.fini)
|
|
3363
|
+
self.slab = await self._initSlabFile(path)
|
|
3192
3364
|
|
|
3193
3365
|
async def _initCellAuth(self):
|
|
3194
3366
|
|
|
@@ -3598,13 +3770,24 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3598
3770
|
return pars
|
|
3599
3771
|
|
|
3600
3772
|
async def _initCellBoot(self):
|
|
3773
|
+
# NOTE: best hook point for custom provisioning
|
|
3601
3774
|
|
|
3602
|
-
pnfo = await self._bootCellProv()
|
|
3775
|
+
isok, pnfo = await self._bootCellProv()
|
|
3603
3776
|
|
|
3604
3777
|
# check this before we setup loadTeleCell()
|
|
3605
3778
|
if not self._mustBootMirror():
|
|
3606
3779
|
return
|
|
3607
3780
|
|
|
3781
|
+
if not isok:
|
|
3782
|
+
# The way that we get to this requires the following states to be true:
|
|
3783
|
+
# 1. self.dirn/cell.guid file is NOT present in the service directory.
|
|
3784
|
+
# 2. mirror config is present.
|
|
3785
|
+
# 3. aha:provision config is not set OR the aha:provision guid matches the self.dirn/prov.done file.
|
|
3786
|
+
mesg = 'Service has been configured to boot from an upstream mirror, but has entered into an invalid ' \
|
|
3787
|
+
'state. This may have been caused by manipulation of the service storage or an error during a ' \
|
|
3788
|
+
f'backup / restore operation. {pnfo.get("mesg")}'
|
|
3789
|
+
raise s_exc.FatalErr(mesg=mesg)
|
|
3790
|
+
|
|
3608
3791
|
async with s_telepath.loadTeleCell(self.dirn):
|
|
3609
3792
|
await self._bootCellMirror(pnfo)
|
|
3610
3793
|
|
|
@@ -3736,7 +3919,8 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3736
3919
|
|
|
3737
3920
|
provurl = self.conf.get('aha:provision')
|
|
3738
3921
|
if provurl is None:
|
|
3739
|
-
return
|
|
3922
|
+
return False, {'mesg': 'No aha:provision configuration has been provided to allow the service to '
|
|
3923
|
+
'bootstrap via AHA.'}
|
|
3740
3924
|
|
|
3741
3925
|
doneiden = None
|
|
3742
3926
|
|
|
@@ -3749,7 +3933,8 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3749
3933
|
providen = urlinfo.get('path').strip('/')
|
|
3750
3934
|
|
|
3751
3935
|
if doneiden == providen:
|
|
3752
|
-
return
|
|
3936
|
+
return False, {'mesg': f'The aha:provision URL guid matches the service prov.done guid, '
|
|
3937
|
+
f'aha:provision={provurl}'}
|
|
3753
3938
|
|
|
3754
3939
|
logger.info(f'Provisioning {self.getCellType()} from AHA service.')
|
|
3755
3940
|
|
|
@@ -3809,7 +3994,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3809
3994
|
|
|
3810
3995
|
logger.info(f'Done provisioning {self.getCellType()} AHA service.')
|
|
3811
3996
|
|
|
3812
|
-
return provconf, providen
|
|
3997
|
+
return True, {'conf': provconf, 'iden': providen}
|
|
3813
3998
|
|
|
3814
3999
|
async def _bootProvConf(self, provconf):
|
|
3815
4000
|
'''
|
|
@@ -3891,23 +4076,14 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3891
4076
|
await self.nexsroot.enNexsLog()
|
|
3892
4077
|
await self.sync()
|
|
3893
4078
|
|
|
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!)')
|
|
4079
|
+
async def _initCloneCell(self, proxy):
|
|
3902
4080
|
|
|
3903
4081
|
tarpath = s_common.genpath(self.dirn, 'tmp', 'bootstrap.tgz')
|
|
3904
|
-
|
|
3905
4082
|
try:
|
|
3906
4083
|
|
|
3907
|
-
|
|
3908
|
-
await cell.readyToMirror()
|
|
4084
|
+
await proxy.readyToMirror()
|
|
3909
4085
|
with s_common.genfile(tarpath) as fd:
|
|
3910
|
-
async for byts in
|
|
4086
|
+
async for byts in proxy.iterNewBackupArchive(remove=True):
|
|
3911
4087
|
fd.write(byts)
|
|
3912
4088
|
|
|
3913
4089
|
with tarfile.open(tarpath) as tgz:
|
|
@@ -3922,6 +4098,18 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
3922
4098
|
if os.path.isfile(tarpath):
|
|
3923
4099
|
os.unlink(tarpath)
|
|
3924
4100
|
|
|
4101
|
+
async def _bootCellMirror(self, pnfo):
|
|
4102
|
+
# this function must assume almost nothing is initialized
|
|
4103
|
+
# but that's ok since it will only run rarely.
|
|
4104
|
+
# It assumes it has a tuple of (provisioning configuration, provisioning iden) available
|
|
4105
|
+
murl = self.conf.req('mirror')
|
|
4106
|
+
provconf, providen = pnfo.get('conf'), pnfo.get('iden')
|
|
4107
|
+
|
|
4108
|
+
logger.warning(f'Bootstrap mirror from: {murl} (this could take a while!)')
|
|
4109
|
+
|
|
4110
|
+
async with await s_telepath.openurl(murl) as proxy:
|
|
4111
|
+
await self._initCloneCell(proxy)
|
|
4112
|
+
|
|
3925
4113
|
# Remove aha:provision from cell.yaml if it exists and the iden differs.
|
|
3926
4114
|
mnfo = s_common.yamlload(self.dirn, 'cell.yaml')
|
|
3927
4115
|
if mnfo:
|
|
@@ -4017,15 +4205,12 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
4017
4205
|
|
|
4018
4206
|
try:
|
|
4019
4207
|
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
lisn = cell.conf.get('dmon:listen')
|
|
4025
|
-
if lisn is None:
|
|
4026
|
-
lisn = cell.getLocalUrl()
|
|
4208
|
+
turl = cell._getDmonListen()
|
|
4209
|
+
if turl is None:
|
|
4210
|
+
turl = opts.telepath
|
|
4211
|
+
await cell.dmon.listen(turl)
|
|
4027
4212
|
|
|
4028
|
-
|
|
4213
|
+
logger.info(f'...{cell.getCellType()} API (telepath): {turl}')
|
|
4029
4214
|
|
|
4030
4215
|
if 'https:port' not in cell.conf:
|
|
4031
4216
|
await cell.addHttpsPort(opts.https)
|
|
@@ -4267,6 +4452,7 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
4267
4452
|
'type': self.getCellType(),
|
|
4268
4453
|
'iden': self.getCellIden(),
|
|
4269
4454
|
'active': self.isactive,
|
|
4455
|
+
'started': self.startms,
|
|
4270
4456
|
'ready': self.nexsroot.ready.is_set(),
|
|
4271
4457
|
'commit': self.COMMIT,
|
|
4272
4458
|
'version': self.VERSION,
|
|
@@ -4684,18 +4870,28 @@ class Cell(s_nexus.Pusher, s_telepath.Aware):
|
|
|
4684
4870
|
sslctx.check_hostname = False
|
|
4685
4871
|
sslctx.verify_mode = ssl.CERT_NONE
|
|
4686
4872
|
|
|
4687
|
-
|
|
4688
|
-
|
|
4873
|
+
# crypto functions require reading certs/keys from disk so make a temp dir
|
|
4874
|
+
# to save any certs/keys to disk so they can be read.
|
|
4875
|
+
with self.getTempDir() as tmpdir:
|
|
4876
|
+
if opts.get('ca_cert'):
|
|
4877
|
+
ca_cert = opts.get('ca_cert').encode()
|
|
4878
|
+
with tempfile.NamedTemporaryFile(dir=tmpdir, mode='wb', delete=False) as fh:
|
|
4879
|
+
fh.write(ca_cert)
|
|
4880
|
+
try:
|
|
4881
|
+
sslctx.load_verify_locations(cafile=fh.name)
|
|
4882
|
+
except Exception as e: # pragma: no cover
|
|
4883
|
+
raise s_exc.BadArg(mesg=f'Error loading CA cert: {str(e)}') from None
|
|
4689
4884
|
|
|
4690
|
-
|
|
4885
|
+
if not opts['client_cert']:
|
|
4886
|
+
return sslctx
|
|
4691
4887
|
|
|
4692
|
-
|
|
4693
|
-
client_key = opts['client_key'].encode()
|
|
4694
|
-
else:
|
|
4695
|
-
client_key = None
|
|
4696
|
-
client_key_path = None
|
|
4888
|
+
client_cert = opts['client_cert'].encode()
|
|
4697
4889
|
|
|
4698
|
-
|
|
4890
|
+
if opts['client_key']:
|
|
4891
|
+
client_key = opts['client_key'].encode()
|
|
4892
|
+
else:
|
|
4893
|
+
client_key = None
|
|
4894
|
+
client_key_path = None
|
|
4699
4895
|
|
|
4700
4896
|
with tempfile.NamedTemporaryFile(dir=tmpdir, mode='wb', delete=False) as fh:
|
|
4701
4897
|
fh.write(client_cert)
|