setup.py 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. '''
  4. The setup script for salt
  5. '''
  6. # pylint: disable=file-perms,ungrouped-imports,wrong-import-order,wrong-import-position,repr-flag-used-in-string
  7. # pylint: disable=3rd-party-local-module-not-gated,resource-leakage
  8. # pylint: disable=C0111,E1101,E1103,F0401,W0611,W0201,W0232,R0201,R0902,R0903
  9. # For Python 2.5. A no-op on 2.6 and above.
  10. from __future__ import absolute_import, print_function, with_statement
  11. import os
  12. import sys
  13. import glob
  14. import operator
  15. import platform
  16. try:
  17. from urllib2 import urlopen
  18. except ImportError:
  19. from urllib.request import urlopen # pylint: disable=no-name-in-module
  20. from datetime import datetime
  21. # pylint: disable=E0611
  22. import distutils.dist
  23. from distutils import log
  24. from distutils.cmd import Command
  25. from distutils.errors import DistutilsArgError
  26. from distutils.command.build import build
  27. from distutils.command.clean import clean
  28. from distutils.command.sdist import sdist
  29. from distutils.command.install_lib import install_lib
  30. from distutils.version import LooseVersion # pylint: disable=blacklisted-module
  31. from ctypes.util import find_library
  32. # pylint: enable=E0611
  33. try:
  34. import zmq
  35. HAS_ZMQ = True
  36. except ImportError:
  37. HAS_ZMQ = False
  38. try:
  39. DATE = datetime.utcfromtimestamp(int(os.environ['SOURCE_DATE_EPOCH']))
  40. except (KeyError, ValueError):
  41. DATE = datetime.utcnow()
  42. # Change to salt source's directory prior to running any command
  43. try:
  44. SETUP_DIRNAME = os.path.dirname(__file__)
  45. except NameError:
  46. # We're most likely being frozen and __file__ triggered this NameError
  47. # Let's work around that
  48. SETUP_DIRNAME = os.path.dirname(sys.argv[0])
  49. if SETUP_DIRNAME != '':
  50. os.chdir(SETUP_DIRNAME)
  51. SETUP_DIRNAME = os.path.abspath(SETUP_DIRNAME)
  52. BOOTSTRAP_SCRIPT_DISTRIBUTED_VERSION = os.environ.get(
  53. # The user can provide a different bootstrap-script version.
  54. # ATTENTION: A tag for that version MUST exist
  55. 'BOOTSTRAP_SCRIPT_VERSION',
  56. # If no bootstrap-script version was provided from the environment, let's
  57. # provide the one we define.
  58. 'v2014.06.21'
  59. )
  60. # Store a reference to the executing platform
  61. IS_WINDOWS_PLATFORM = sys.platform.startswith('win')
  62. if IS_WINDOWS_PLATFORM:
  63. IS_SMARTOS_PLATFORM = False
  64. else:
  65. # os.uname() not available on Windows.
  66. IS_SMARTOS_PLATFORM = os.uname()[0] == 'SunOS' and os.uname()[3].startswith('joyent_')
  67. # Store a reference whether if we're running under Python 3 and above
  68. IS_PY3 = sys.version_info > (3,)
  69. # Use setuptools only if the user opts-in by setting the USE_SETUPTOOLS env var
  70. # Or if setuptools was previously imported (which is the case when using
  71. # 'distribute')
  72. # This ensures consistent behavior but allows for advanced usage with
  73. # virtualenv, buildout, and others.
  74. WITH_SETUPTOOLS = False
  75. if 'USE_SETUPTOOLS' in os.environ or 'setuptools' in sys.modules:
  76. try:
  77. from setuptools import setup
  78. from setuptools.command.develop import develop
  79. from setuptools.command.install import install
  80. from setuptools.command.sdist import sdist
  81. from setuptools.command.egg_info import egg_info
  82. WITH_SETUPTOOLS = True
  83. except ImportError:
  84. WITH_SETUPTOOLS = False
  85. if WITH_SETUPTOOLS is False:
  86. import warnings
  87. # pylint: disable=E0611
  88. from distutils.command.install import install
  89. from distutils.core import setup
  90. # pylint: enable=E0611
  91. warnings.filterwarnings(
  92. 'ignore',
  93. 'Unknown distribution option: \'(extras_require|tests_require|install_requires|zip_safe)\'',
  94. UserWarning,
  95. 'distutils.dist'
  96. )
  97. try:
  98. # Add the esky bdist target if the module is available
  99. # may require additional modules depending on platform
  100. from esky import bdist_esky
  101. # bbfreeze chosen for its tight integration with distutils
  102. import bbfreeze
  103. HAS_ESKY = True
  104. except ImportError:
  105. HAS_ESKY = False
  106. SALT_VERSION = os.path.join(os.path.abspath(SETUP_DIRNAME), 'salt', 'version.py')
  107. SALT_VERSION_HARDCODED = os.path.join(os.path.abspath(SETUP_DIRNAME), 'salt', '_version.py')
  108. SALT_SYSPATHS_HARDCODED = os.path.join(os.path.abspath(SETUP_DIRNAME), 'salt', '_syspaths.py')
  109. SALT_REQS = os.path.join(os.path.abspath(SETUP_DIRNAME), 'requirements', 'base.txt')
  110. SALT_ZEROMQ_REQS = os.path.join(os.path.abspath(SETUP_DIRNAME), 'requirements', 'zeromq.txt')
  111. SALT_WINDOWS_REQS = os.path.join(os.path.abspath(SETUP_DIRNAME), 'pkg', 'windows', 'req.txt')
  112. SALT_LONG_DESCRIPTION_FILE = os.path.join(os.path.abspath(SETUP_DIRNAME), 'README.rst')
  113. # Salt SSH Packaging Detection
  114. PACKAGED_FOR_SALT_SSH_FILE = os.path.join(os.path.abspath(SETUP_DIRNAME), '.salt-ssh-package')
  115. PACKAGED_FOR_SALT_SSH = os.path.isfile(PACKAGED_FOR_SALT_SSH_FILE)
  116. # pylint: disable=W0122
  117. exec(compile(open(SALT_VERSION).read(), SALT_VERSION, 'exec'))
  118. # pylint: enable=W0122
  119. # ----- Helper Functions -------------------------------------------------------------------------------------------->
  120. def _parse_op(op):
  121. '''
  122. >>> _parse_op('>')
  123. 'gt'
  124. >>> _parse_op('>=')
  125. 'ge'
  126. >>> _parse_op('=>')
  127. 'ge'
  128. >>> _parse_op('=> ')
  129. 'ge'
  130. >>> _parse_op('<')
  131. 'lt'
  132. >>> _parse_op('<=')
  133. 'le'
  134. >>> _parse_op('==')
  135. 'eq'
  136. >>> _parse_op(' <= ')
  137. 'le'
  138. '''
  139. op = op.strip()
  140. if '>' in op:
  141. if '=' in op:
  142. return 'ge'
  143. else:
  144. return 'gt'
  145. elif '<' in op:
  146. if '=' in op:
  147. return 'le'
  148. else:
  149. return 'lt'
  150. elif '!' in op:
  151. return 'ne'
  152. else:
  153. return 'eq'
  154. def _parse_ver(ver):
  155. '''
  156. >>> _parse_ver("'3.4' # pyzmq 17.1.0 stopped building wheels for python3.4")
  157. '3.4'
  158. >>> _parse_ver('"3.4"')
  159. '3.4'
  160. >>> _parse_ver('"2.6.17"')
  161. '2.6.17'
  162. '''
  163. if '#' in ver:
  164. ver, _ = ver.split('#', 1)
  165. ver = ver.strip()
  166. return ver.strip('\'').strip('"')
  167. def _check_ver(pyver, op, wanted):
  168. '''
  169. >>> _check_ver('2.7.15', 'gt', '2.7')
  170. True
  171. >>> _check_ver('2.7.15', 'gt', '2.7.15')
  172. False
  173. >>> _check_ver('2.7.15', 'ge', '2.7.15')
  174. True
  175. >>> _check_ver('2.7.15', 'eq', '2.7.15')
  176. True
  177. '''
  178. pyver = distutils.version.LooseVersion(pyver)
  179. wanted = distutils.version.LooseVersion(wanted)
  180. if IS_PY3:
  181. if not isinstance(pyver, str):
  182. pyver = str(pyver)
  183. if not isinstance(wanted, str):
  184. wanted = str(wanted)
  185. return getattr(operator, '__{}__'.format(op))(pyver, wanted)
  186. def _parse_requirements_file(requirements_file):
  187. parsed_requirements = []
  188. with open(requirements_file) as rfh:
  189. for line in rfh.readlines():
  190. line = line.strip()
  191. if not line or line.startswith(('#', '-r')):
  192. continue
  193. if IS_WINDOWS_PLATFORM:
  194. if 'libcloud' in line:
  195. continue
  196. if IS_PY3 and 'futures' in line.lower():
  197. # Python 3 already has futures, installing it will only break
  198. # the current python installation whenever futures is imported
  199. continue
  200. try:
  201. pkg, pyverspec = line.rsplit(';', 1)
  202. except ValueError:
  203. pkg, pyverspec = line, ''
  204. pyverspec = pyverspec.strip()
  205. if pyverspec:
  206. _, op, ver = pyverspec.split(' ', 2)
  207. if not _check_ver(platform.python_version(), _parse_op(op), _parse_ver(ver)):
  208. continue
  209. parsed_requirements.append(pkg)
  210. return parsed_requirements
  211. # <---- Helper Functions ---------------------------------------------------------------------------------------------
  212. # ----- Custom Distutils/Setuptools Commands ------------------------------------------------------------------------>
  213. class WriteSaltVersion(Command):
  214. description = 'Write salt\'s hardcoded version file'
  215. user_options = []
  216. def initialize_options(self):
  217. '''
  218. Abstract method that is required to be overwritten
  219. '''
  220. def finalize_options(self):
  221. '''
  222. Abstract method that is required to be overwritten
  223. '''
  224. def run(self):
  225. if not os.path.exists(SALT_VERSION_HARDCODED) or self.distribution.with_salt_version:
  226. # Write the version file
  227. if getattr(self.distribution, 'salt_version_hardcoded_path', None) is None:
  228. print('This command is not meant to be called on it\'s own')
  229. exit(1)
  230. if not self.distribution.with_salt_version:
  231. salt_version = __saltstack_version__ # pylint: disable=undefined-variable
  232. else:
  233. from salt.version import SaltStackVersion
  234. salt_version = SaltStackVersion.parse(self.distribution.with_salt_version)
  235. # pylint: disable=E0602
  236. open(self.distribution.salt_version_hardcoded_path, 'w').write(
  237. INSTALL_VERSION_TEMPLATE.format(
  238. date=DATE,
  239. full_version_info=salt_version.full_info
  240. )
  241. )
  242. # pylint: enable=E0602
  243. class GenerateSaltSyspaths(Command):
  244. description = 'Generate salt\'s hardcoded syspaths file'
  245. def initialize_options(self):
  246. pass
  247. def finalize_options(self):
  248. pass
  249. def run(self):
  250. # Write the syspaths file
  251. if getattr(self.distribution, 'salt_syspaths_hardcoded_path', None) is None:
  252. print('This command is not meant to be called on it\'s own')
  253. exit(1)
  254. # Write the system paths file
  255. open(self.distribution.salt_syspaths_hardcoded_path, 'w').write(
  256. INSTALL_SYSPATHS_TEMPLATE.format(
  257. date=DATE,
  258. root_dir=self.distribution.salt_root_dir,
  259. share_dir=self.distribution.salt_share_dir,
  260. config_dir=self.distribution.salt_config_dir,
  261. cache_dir=self.distribution.salt_cache_dir,
  262. sock_dir=self.distribution.salt_sock_dir,
  263. srv_root_dir=self.distribution.salt_srv_root_dir,
  264. base_file_roots_dir=self.distribution.salt_base_file_roots_dir,
  265. base_pillar_roots_dir=self.distribution.salt_base_pillar_roots_dir,
  266. base_master_roots_dir=self.distribution.salt_base_master_roots_dir,
  267. base_thorium_roots_dir=self.distribution.salt_base_thorium_roots_dir,
  268. logs_dir=self.distribution.salt_logs_dir,
  269. pidfile_dir=self.distribution.salt_pidfile_dir,
  270. spm_parent_path=self.distribution.salt_spm_parent_dir,
  271. spm_formula_path=self.distribution.salt_spm_formula_dir,
  272. spm_pillar_path=self.distribution.salt_spm_pillar_dir,
  273. spm_reactor_path=self.distribution.salt_spm_reactor_dir,
  274. home_dir=self.distribution.salt_home_dir,
  275. )
  276. )
  277. class WriteSaltSshPackagingFile(Command):
  278. description = 'Write salt\'s ssh packaging file'
  279. user_options = []
  280. def initialize_options(self):
  281. '''
  282. Abstract method that is required to be overwritten
  283. '''
  284. def finalize_options(self):
  285. '''
  286. Abstract method that is required to be overwritten
  287. '''
  288. def run(self):
  289. if not os.path.exists(PACKAGED_FOR_SALT_SSH_FILE):
  290. # Write the salt-ssh packaging file
  291. if getattr(self.distribution, 'salt_ssh_packaging_file', None) is None:
  292. print('This command is not meant to be called on it\'s own')
  293. exit(1)
  294. # pylint: disable=E0602
  295. open(self.distribution.salt_ssh_packaging_file, 'w').write('Packaged for Salt-SSH\n')
  296. # pylint: enable=E0602
  297. if WITH_SETUPTOOLS:
  298. class Develop(develop):
  299. user_options = develop.user_options + [
  300. ('write-salt-version', None,
  301. 'Generate Salt\'s _version.py file which allows proper version '
  302. 'reporting. This defaults to False on develop/editable setups. '
  303. 'If WRITE_SALT_VERSION is found in the environment this flag is '
  304. 'switched to True.'),
  305. ('generate-salt-syspaths', None,
  306. 'Generate Salt\'s _syspaths.py file which allows tweaking some '
  307. 'common paths that salt uses. This defaults to False on '
  308. 'develop/editable setups. If GENERATE_SALT_SYSPATHS is found in '
  309. 'the environment this flag is switched to True.'),
  310. ('mimic-salt-install', None,
  311. 'Mimmic the install command when running the develop command. '
  312. 'This will generate salt\'s _version.py and _syspaths.py files. '
  313. 'Generate Salt\'s _syspaths.py file which allows tweaking some '
  314. 'This defaults to False on develop/editable setups. '
  315. 'If MIMIC_INSTALL is found in the environment this flag is '
  316. 'switched to True.')
  317. ]
  318. boolean_options = develop.boolean_options + [
  319. 'write-salt-version',
  320. 'generate-salt-syspaths',
  321. 'mimic-salt-install'
  322. ]
  323. def initialize_options(self):
  324. develop.initialize_options(self)
  325. self.write_salt_version = False
  326. self.generate_salt_syspaths = False
  327. self.mimic_salt_install = False
  328. def finalize_options(self):
  329. develop.finalize_options(self)
  330. if 'WRITE_SALT_VERSION' in os.environ:
  331. self.write_salt_version = True
  332. if 'GENERATE_SALT_SYSPATHS' in os.environ:
  333. self.generate_salt_syspaths = True
  334. if 'MIMIC_SALT_INSTALL' in os.environ:
  335. self.mimic_salt_install = True
  336. if self.mimic_salt_install:
  337. self.write_salt_version = True
  338. self.generate_salt_syspaths = True
  339. def run(self):
  340. if IS_WINDOWS_PLATFORM:
  341. # Download the required DLLs
  342. self.distribution.salt_download_windows_dlls = True
  343. self.run_command('download-windows-dlls')
  344. self.distribution.salt_download_windows_dlls = None
  345. if self.write_salt_version is True:
  346. self.distribution.running_salt_install = True
  347. self.distribution.salt_version_hardcoded_path = SALT_VERSION_HARDCODED
  348. self.run_command('write_salt_version')
  349. if self.generate_salt_syspaths:
  350. self.distribution.salt_syspaths_hardcoded_path = SALT_SYSPATHS_HARDCODED
  351. self.run_command('generate_salt_syspaths')
  352. # Resume normal execution
  353. develop.run(self)
  354. class DownloadWindowsDlls(Command):
  355. description = 'Download required DLL\'s for windows'
  356. def initialize_options(self):
  357. pass
  358. def finalize_options(self):
  359. pass
  360. def run(self):
  361. if getattr(self.distribution, 'salt_download_windows_dlls', None) is None:
  362. print('This command is not meant to be called on it\'s own')
  363. exit(1)
  364. import pip
  365. # pip has moved many things to `_internal` starting with pip 10
  366. if LooseVersion(pip.__version__) < LooseVersion('10.0'):
  367. from pip.utils.logging import indent_log # pylint: disable=no-name-in-module
  368. else:
  369. from pip._internal.utils.logging import indent_log # pylint: disable=no-name-in-module
  370. platform_bits, _ = platform.architecture()
  371. url = 'https://repo.saltstack.com/windows/dependencies/{bits}/{fname}.dll'
  372. dest = os.path.join(os.path.dirname(sys.executable), '{fname}.dll')
  373. with indent_log():
  374. for fname in ('libeay32', 'ssleay32', 'msvcr120'):
  375. # See if the library is already on the system
  376. if find_library(fname):
  377. continue
  378. furl = url.format(bits=platform_bits[:2], fname=fname)
  379. fdest = dest.format(fname=fname)
  380. if not os.path.exists(fdest):
  381. log.info('Downloading {0}.dll to {1} from {2}'.format(fname, fdest, furl))
  382. try:
  383. import requests
  384. from contextlib import closing
  385. with closing(requests.get(furl, stream=True)) as req:
  386. if req.status_code == 200:
  387. with open(fdest, 'wb') as wfh:
  388. for chunk in req.iter_content(chunk_size=4096):
  389. if chunk: # filter out keep-alive new chunks
  390. wfh.write(chunk)
  391. wfh.flush()
  392. else:
  393. log.error(
  394. 'Failed to download {0}.dll to {1} from {2}'.format(
  395. fname, fdest, furl
  396. )
  397. )
  398. except ImportError:
  399. req = urlopen(furl)
  400. if req.getcode() == 200:
  401. with open(fdest, 'wb') as wfh:
  402. if IS_PY3:
  403. while True:
  404. chunk = req.read(4096)
  405. if len(chunk) == 0:
  406. break
  407. wfh.write(chunk)
  408. wfh.flush()
  409. else:
  410. while True:
  411. for chunk in req.read(4096):
  412. if not chunk:
  413. break
  414. wfh.write(chunk)
  415. wfh.flush()
  416. else:
  417. log.error(
  418. 'Failed to download {0}.dll to {1} from {2}'.format(
  419. fname, fdest, furl
  420. )
  421. )
  422. class Sdist(sdist):
  423. def make_release_tree(self, base_dir, files):
  424. if self.distribution.ssh_packaging:
  425. self.distribution.salt_ssh_packaging_file = PACKAGED_FOR_SALT_SSH_FILE
  426. self.run_command('write_salt_ssh_packaging_file')
  427. self.filelist.files.append(os.path.basename(PACKAGED_FOR_SALT_SSH_FILE))
  428. if not IS_PY3 and not isinstance(base_dir, str):
  429. # Work around some bad code in distutils which logs unicode paths
  430. # against a str format string.
  431. base_dir = base_dir.encode('utf-8')
  432. sdist.make_release_tree(self, base_dir, files)
  433. # Let's generate salt/_version.py to include in the sdist tarball
  434. self.distribution.running_salt_sdist = True
  435. self.distribution.salt_version_hardcoded_path = os.path.join(
  436. base_dir, 'salt', '_version.py'
  437. )
  438. self.run_command('write_salt_version')
  439. def make_distribution(self):
  440. sdist.make_distribution(self)
  441. if self.distribution.ssh_packaging:
  442. os.unlink(PACKAGED_FOR_SALT_SSH_FILE)
  443. class CloudSdist(Sdist): # pylint: disable=too-many-ancestors
  444. user_options = Sdist.user_options + [
  445. ('download-bootstrap-script', None,
  446. 'Download the latest stable bootstrap-salt.sh script. This '
  447. 'can also be triggered by having `DOWNLOAD_BOOTSTRAP_SCRIPT=1` as an '
  448. 'environment variable.')
  449. ]
  450. boolean_options = Sdist.boolean_options + [
  451. 'download-bootstrap-script'
  452. ]
  453. def initialize_options(self):
  454. Sdist.initialize_options(self)
  455. self.skip_bootstrap_download = True
  456. self.download_bootstrap_script = False
  457. def finalize_options(self):
  458. Sdist.finalize_options(self)
  459. if 'SKIP_BOOTSTRAP_DOWNLOAD' in os.environ:
  460. log('Please stop using \'SKIP_BOOTSTRAP_DOWNLOAD\' and use ' # pylint: disable=not-callable
  461. '\'DOWNLOAD_BOOTSTRAP_SCRIPT\' instead')
  462. if 'DOWNLOAD_BOOTSTRAP_SCRIPT' in os.environ:
  463. download_bootstrap_script = os.environ.get(
  464. 'DOWNLOAD_BOOTSTRAP_SCRIPT', '0'
  465. )
  466. self.download_bootstrap_script = download_bootstrap_script == '1'
  467. def run(self):
  468. if self.download_bootstrap_script is True:
  469. # Let's update the bootstrap-script to the version defined to be
  470. # distributed. See BOOTSTRAP_SCRIPT_DISTRIBUTED_VERSION above.
  471. url = (
  472. 'https://github.com/saltstack/salt-bootstrap/raw/{0}'
  473. '/bootstrap-salt.sh'.format(
  474. BOOTSTRAP_SCRIPT_DISTRIBUTED_VERSION
  475. )
  476. )
  477. deploy_path = os.path.join(
  478. SETUP_DIRNAME,
  479. 'salt',
  480. 'cloud',
  481. 'deploy',
  482. 'bootstrap-salt.sh'
  483. )
  484. log.info(
  485. 'Updating bootstrap-salt.sh.'
  486. '\n\tSource: {0}'
  487. '\n\tDestination: {1}'.format(
  488. url,
  489. deploy_path
  490. )
  491. )
  492. try:
  493. import requests
  494. req = requests.get(url)
  495. if req.status_code == 200:
  496. script_contents = req.text.encode(req.encoding)
  497. else:
  498. log.error(
  499. 'Failed to update the bootstrap-salt.sh script. HTTP '
  500. 'Error code: {0}'.format(
  501. req.status_code
  502. )
  503. )
  504. except ImportError:
  505. req = urlopen(url)
  506. if req.getcode() == 200:
  507. script_contents = req.read()
  508. else:
  509. log.error(
  510. 'Failed to update the bootstrap-salt.sh script. HTTP '
  511. 'Error code: {0}'.format(
  512. req.getcode()
  513. )
  514. )
  515. try:
  516. with open(deploy_path, 'w') as fp_:
  517. fp_.write(script_contents)
  518. except (OSError, IOError) as err:
  519. log.error(
  520. 'Failed to write the updated script: {0}'.format(err)
  521. )
  522. # Let's the rest of the build command
  523. Sdist.run(self)
  524. def write_manifest(self):
  525. # We only need to ship the scripts which are supposed to be installed
  526. dist_scripts = self.distribution.scripts
  527. for script in self.filelist.files[:]:
  528. if not script.startswith('scripts/'):
  529. continue
  530. if script not in dist_scripts:
  531. self.filelist.files.remove(script)
  532. return Sdist.write_manifest(self)
  533. class TestCommand(Command):
  534. description = 'Run tests'
  535. user_options = [
  536. ('runtests-opts=', 'R', 'Command line options to pass to runtests.py')
  537. ]
  538. def initialize_options(self):
  539. self.runtests_opts = None
  540. def finalize_options(self):
  541. '''
  542. Abstract method that is required to be overwritten
  543. '''
  544. def run(self):
  545. from subprocess import Popen
  546. self.run_command('build')
  547. build_cmd = self.get_finalized_command('build_ext')
  548. runner = os.path.abspath('tests/runtests.py')
  549. test_cmd = sys.executable + ' {0}'.format(runner)
  550. if self.runtests_opts:
  551. test_cmd += ' {0}'.format(self.runtests_opts)
  552. print('running test')
  553. test_process = Popen(
  554. test_cmd, shell=True,
  555. stdout=sys.stdout, stderr=sys.stderr,
  556. cwd=build_cmd.build_lib
  557. )
  558. test_process.communicate()
  559. sys.exit(test_process.returncode)
  560. class Clean(clean):
  561. def run(self):
  562. clean.run(self)
  563. # Let's clean compiled *.py[c,o]
  564. for subdir in ('salt', 'tests', 'doc'):
  565. root = os.path.join(os.path.dirname(__file__), subdir)
  566. for dirname, _, _ in os.walk(root):
  567. for to_remove_filename in glob.glob('{0}/*.py[oc]'.format(dirname)):
  568. os.remove(to_remove_filename)
  569. INSTALL_VERSION_TEMPLATE = '''\
  570. # This file was auto-generated by salt's setup
  571. from salt.version import SaltStackVersion
  572. __saltstack_version__ = SaltStackVersion{full_version_info!r}
  573. '''
  574. INSTALL_SYSPATHS_TEMPLATE = '''\
  575. # This file was auto-generated by salt's setup on \
  576. {date:%A, %d %B %Y @ %H:%m:%S UTC}.
  577. ROOT_DIR = {root_dir!r}
  578. SHARE_DIR = {share_dir!r}
  579. CONFIG_DIR = {config_dir!r}
  580. CACHE_DIR = {cache_dir!r}
  581. SOCK_DIR = {sock_dir!r}
  582. SRV_ROOT_DIR= {srv_root_dir!r}
  583. BASE_FILE_ROOTS_DIR = {base_file_roots_dir!r}
  584. BASE_PILLAR_ROOTS_DIR = {base_pillar_roots_dir!r}
  585. BASE_MASTER_ROOTS_DIR = {base_master_roots_dir!r}
  586. BASE_THORIUM_ROOTS_DIR = {base_thorium_roots_dir!r}
  587. LOGS_DIR = {logs_dir!r}
  588. PIDFILE_DIR = {pidfile_dir!r}
  589. SPM_PARENT_PATH = {spm_parent_path!r}
  590. SPM_FORMULA_PATH = {spm_formula_path!r}
  591. SPM_PILLAR_PATH = {spm_pillar_path!r}
  592. SPM_REACTOR_PATH = {spm_reactor_path!r}
  593. HOME_DIR = {home_dir!r}
  594. '''
  595. class Build(build):
  596. def run(self):
  597. # Run build.run function
  598. build.run(self)
  599. if getattr(self.distribution, 'with_salt_version', False):
  600. # Write the hardcoded salt version module salt/_version.py
  601. self.distribution.salt_version_hardcoded_path = os.path.join(
  602. self.build_lib, 'salt', '_version.py'
  603. )
  604. self.run_command('write_salt_version')
  605. if getattr(self.distribution, 'running_salt_install', False):
  606. # If our install attribute is present and set to True, we'll go
  607. # ahead and write our install time python modules.
  608. # Write the hardcoded salt version module salt/_version.py
  609. self.run_command('write_salt_version')
  610. # Write the system paths file
  611. self.distribution.salt_syspaths_hardcoded_path = os.path.join(
  612. self.build_lib, 'salt', '_syspaths.py'
  613. )
  614. self.run_command('generate_salt_syspaths')
  615. class Install(install):
  616. def initialize_options(self):
  617. install.initialize_options(self)
  618. def finalize_options(self):
  619. install.finalize_options(self)
  620. def run(self):
  621. # Let's set the running_salt_install attribute so we can add
  622. # _version.py in the build command
  623. self.distribution.running_salt_install = True
  624. self.distribution.salt_version_hardcoded_path = os.path.join(
  625. self.build_lib, 'salt', '_version.py'
  626. )
  627. if IS_WINDOWS_PLATFORM:
  628. # Download the required DLLs
  629. self.distribution.salt_download_windows_dlls = True
  630. self.run_command('download-windows-dlls')
  631. self.distribution.salt_download_windows_dlls = None
  632. # Run install.run
  633. install.run(self)
  634. class InstallLib(install_lib):
  635. def run(self):
  636. executables = [
  637. 'salt/templates/git/ssh-id-wrapper',
  638. 'salt/templates/lxc/salt_tarball',
  639. ]
  640. install_lib.run(self)
  641. # input and outputs match 1-1
  642. inp = self.get_inputs()
  643. out = self.get_outputs()
  644. chmod = []
  645. for idx, inputfile in enumerate(inp):
  646. for executable in executables:
  647. if inputfile.endswith(executable):
  648. chmod.append(idx)
  649. for idx in chmod:
  650. filename = out[idx]
  651. os.chmod(filename, 0o755)
  652. # <---- Custom Distutils/Setuptools Commands -------------------------------------------------------------------------
  653. # ----- Custom Distribution Class ----------------------------------------------------------------------------------->
  654. # We use this to override the package name in case --ssh-packaging is passed to
  655. # setup.py or the special .salt-ssh-package is found
  656. class SaltDistribution(distutils.dist.Distribution):
  657. '''
  658. Just so it's completely clear
  659. Under windows, the following scripts should be installed:
  660. * salt-call
  661. * salt-cp
  662. * salt-minion
  663. * salt-syndic
  664. * salt-unity
  665. * spm
  666. When packaged for salt-ssh, the following scripts should be installed:
  667. * salt-call
  668. * salt-run
  669. * salt-ssh
  670. * salt-cloud
  671. Under windows, the following scripts should be omitted from the salt-ssh
  672. package:
  673. * salt-cloud
  674. * salt-run
  675. Under *nix, all scripts should be installed
  676. '''
  677. global_options = distutils.dist.Distribution.global_options + [
  678. ('ssh-packaging', None, 'Run in SSH packaging mode'),
  679. ('salt-transport=', None, 'The transport to prepare salt for. Currently, the only choice '
  680. 'is \'zeromq\'. This may be expanded in the future. Defaults to '
  681. '\'zeromq\'', 'zeromq')] + [
  682. ('with-salt-version=', None, 'Set a fixed version for Salt instead calculating it'),
  683. # Salt's Paths Configuration Settings
  684. ('salt-root-dir=', None,
  685. 'Salt\'s pre-configured root directory'),
  686. ('salt-share-dir=', None,
  687. 'Salt\'s pre-configured share directory'),
  688. ('salt-config-dir=', None,
  689. 'Salt\'s pre-configured configuration directory'),
  690. ('salt-cache-dir=', None,
  691. 'Salt\'s pre-configured cache directory'),
  692. ('salt-sock-dir=', None,
  693. 'Salt\'s pre-configured socket directory'),
  694. ('salt-srv-root-dir=', None,
  695. 'Salt\'s pre-configured service directory'),
  696. ('salt-base-file-roots-dir=', None,
  697. 'Salt\'s pre-configured file roots directory'),
  698. ('salt-base-pillar-roots-dir=', None,
  699. 'Salt\'s pre-configured pillar roots directory'),
  700. ('salt-base-master-roots-dir=', None,
  701. 'Salt\'s pre-configured master roots directory'),
  702. ('salt-logs-dir=', None,
  703. 'Salt\'s pre-configured logs directory'),
  704. ('salt-pidfile-dir=', None,
  705. 'Salt\'s pre-configured pidfiles directory'),
  706. ('salt-spm-formula-dir=', None,
  707. 'Salt\'s pre-configured SPM formulas directory'),
  708. ('salt-spm-pillar-dir=', None,
  709. 'Salt\'s pre-configured SPM pillar directory'),
  710. ('salt-spm-reactor-dir=', None,
  711. 'Salt\'s pre-configured SPM reactor directory'),
  712. ('salt-home-dir=', None,
  713. 'Salt\'s pre-configured user home directory'),
  714. ]
  715. def __init__(self, attrs=None):
  716. distutils.dist.Distribution.__init__(self, attrs)
  717. self.ssh_packaging = PACKAGED_FOR_SALT_SSH
  718. self.salt_transport = None
  719. # Salt Paths Configuration Settings
  720. self.salt_root_dir = None
  721. self.salt_share_dir = None
  722. self.salt_config_dir = None
  723. self.salt_cache_dir = None
  724. self.salt_sock_dir = None
  725. self.salt_srv_root_dir = None
  726. self.salt_base_file_roots_dir = None
  727. self.salt_base_thorium_roots_dir = None
  728. self.salt_base_pillar_roots_dir = None
  729. self.salt_base_master_roots_dir = None
  730. self.salt_logs_dir = None
  731. self.salt_pidfile_dir = None
  732. self.salt_spm_parent_dir = None
  733. self.salt_spm_formula_dir = None
  734. self.salt_spm_pillar_dir = None
  735. self.salt_spm_reactor_dir = None
  736. self.salt_home_dir = None
  737. # Salt version
  738. self.with_salt_version = None
  739. self.name = 'salt-ssh' if PACKAGED_FOR_SALT_SSH else 'salt'
  740. self.salt_version = __version__ # pylint: disable=undefined-variable
  741. self.description = 'Portable, distributed, remote execution and configuration management system'
  742. kwargs = {}
  743. if IS_PY3:
  744. kwargs['encoding'] = 'utf-8'
  745. with open(SALT_LONG_DESCRIPTION_FILE, **kwargs) as f:
  746. self.long_description = f.read()
  747. self.long_description_content_type = 'text/x-rst'
  748. self.author = 'Thomas S Hatch'
  749. self.author_email = 'thatch45@gmail.com'
  750. self.url = 'http://saltstack.org'
  751. self.cmdclass.update({'test': TestCommand,
  752. 'clean': Clean,
  753. 'build': Build,
  754. 'sdist': Sdist,
  755. 'install': Install,
  756. 'write_salt_version': WriteSaltVersion,
  757. 'generate_salt_syspaths': GenerateSaltSyspaths,
  758. 'write_salt_ssh_packaging_file': WriteSaltSshPackagingFile})
  759. if not IS_WINDOWS_PLATFORM:
  760. self.cmdclass.update({'sdist': CloudSdist,
  761. 'install_lib': InstallLib})
  762. if IS_WINDOWS_PLATFORM:
  763. self.cmdclass.update({'download-windows-dlls': DownloadWindowsDlls})
  764. if WITH_SETUPTOOLS:
  765. self.cmdclass.update({'develop': Develop})
  766. self.license = 'Apache Software License 2.0'
  767. self.packages = self.discover_packages()
  768. self.zip_safe = False
  769. if HAS_ESKY:
  770. self.setup_esky()
  771. self.update_metadata()
  772. def update_metadata(self):
  773. for attrname in dir(self):
  774. if attrname.startswith('__'):
  775. continue
  776. attrvalue = getattr(self, attrname, None)
  777. if attrvalue == 0:
  778. continue
  779. if attrname == 'salt_version':
  780. attrname = 'version'
  781. if hasattr(self.metadata, 'set_{0}'.format(attrname)):
  782. getattr(self.metadata, 'set_{0}'.format(attrname))(attrvalue)
  783. elif hasattr(self.metadata, attrname):
  784. try:
  785. setattr(self.metadata, attrname, attrvalue)
  786. except AttributeError:
  787. pass
  788. def discover_packages(self):
  789. modules = []
  790. for root, _, files in os.walk(os.path.join(SETUP_DIRNAME, 'salt')):
  791. if '__init__.py' not in files:
  792. continue
  793. modules.append(os.path.relpath(root, SETUP_DIRNAME).replace(os.sep, '.'))
  794. return modules
  795. # ----- Static Data -------------------------------------------------------------------------------------------->
  796. @property
  797. def _property_classifiers(self):
  798. return ['Programming Language :: Python',
  799. 'Programming Language :: Cython',
  800. 'Programming Language :: Python :: 2.6',
  801. 'Programming Language :: Python :: 2.7',
  802. 'Development Status :: 5 - Production/Stable',
  803. 'Environment :: Console',
  804. 'Intended Audience :: Developers',
  805. 'Intended Audience :: Information Technology',
  806. 'Intended Audience :: System Administrators',
  807. 'License :: OSI Approved :: Apache Software License',
  808. 'Operating System :: POSIX :: Linux',
  809. 'Topic :: System :: Clustering',
  810. 'Topic :: System :: Distributed Computing']
  811. @property
  812. def _property_dependency_links(self):
  813. return ['https://github.com/saltstack/salt-testing/tarball/develop#egg=SaltTesting']
  814. @property
  815. def _property_tests_require(self):
  816. return ['SaltTesting']
  817. # <---- Static Data ----------------------------------------------------------------------------------------------
  818. # ----- Dynamic Data -------------------------------------------------------------------------------------------->
  819. @property
  820. def _property_package_data(self):
  821. package_data = {'salt.templates': ['rh_ip/*.jinja',
  822. 'debian_ip/*.jinja',
  823. 'virt/*.jinja',
  824. 'git/*',
  825. 'lxc/*',
  826. ]}
  827. if not IS_WINDOWS_PLATFORM:
  828. package_data['salt.cloud'] = ['deploy/*.sh']
  829. if not self.ssh_packaging and not PACKAGED_FOR_SALT_SSH:
  830. package_data['salt.daemons.flo'] = ['*.flo']
  831. return package_data
  832. @property
  833. def _property_data_files(self):
  834. # Data files common to all scenarios
  835. data_files = [
  836. ('share/man/man1', ['doc/man/salt-call.1', 'doc/man/salt-run.1']),
  837. ('share/man/man7', ['doc/man/salt.7'])
  838. ]
  839. if self.ssh_packaging or PACKAGED_FOR_SALT_SSH:
  840. data_files[0][1].append('doc/man/salt-ssh.1')
  841. if IS_WINDOWS_PLATFORM:
  842. return data_files
  843. data_files[0][1].append('doc/man/salt-cloud.1')
  844. return data_files
  845. if IS_WINDOWS_PLATFORM:
  846. data_files[0][1].extend(['doc/man/salt-cp.1',
  847. 'doc/man/salt-key.1',
  848. 'doc/man/salt-minion.1',
  849. 'doc/man/salt-syndic.1',
  850. 'doc/man/salt-unity.1',
  851. 'doc/man/spm.1'])
  852. return data_files
  853. # *nix, so, we need all man pages
  854. data_files[0][1].extend(['doc/man/salt-api.1',
  855. 'doc/man/salt-cloud.1',
  856. 'doc/man/salt-cp.1',
  857. 'doc/man/salt-key.1',
  858. 'doc/man/salt-master.1',
  859. 'doc/man/salt-minion.1',
  860. 'doc/man/salt-proxy.1',
  861. 'doc/man/spm.1',
  862. 'doc/man/salt.1',
  863. 'doc/man/salt-ssh.1',
  864. 'doc/man/salt-syndic.1',
  865. 'doc/man/salt-unity.1'])
  866. return data_files
  867. @property
  868. def _property_install_requires(self):
  869. install_requires = _parse_requirements_file(SALT_REQS)
  870. if self.salt_transport == 'zeromq':
  871. install_requires += _parse_requirements_file(SALT_ZEROMQ_REQS)
  872. if IS_WINDOWS_PLATFORM:
  873. install_requires = _parse_requirements_file(SALT_WINDOWS_REQS)
  874. return install_requires
  875. @property
  876. def _property_scripts(self):
  877. # Scripts common to all scenarios
  878. scripts = ['scripts/salt-call', 'scripts/salt-run']
  879. if self.ssh_packaging or PACKAGED_FOR_SALT_SSH:
  880. scripts.append('scripts/salt-ssh')
  881. if IS_WINDOWS_PLATFORM:
  882. return scripts
  883. scripts.extend(['scripts/salt-cloud', 'scripts/spm'])
  884. return scripts
  885. if IS_WINDOWS_PLATFORM:
  886. scripts.extend(['scripts/salt-cp',
  887. 'scripts/salt-key',
  888. 'scripts/salt-minion',
  889. 'scripts/salt-syndic',
  890. 'scripts/salt-unity',
  891. 'scripts/spm'])
  892. return scripts
  893. # *nix, so, we need all scripts
  894. scripts.extend(['scripts/salt',
  895. 'scripts/salt-api',
  896. 'scripts/salt-cloud',
  897. 'scripts/salt-cp',
  898. 'scripts/salt-key',
  899. 'scripts/salt-master',
  900. 'scripts/salt-minion',
  901. 'scripts/salt-proxy',
  902. 'scripts/salt-ssh',
  903. 'scripts/salt-syndic',
  904. 'scripts/salt-unity',
  905. 'scripts/spm'])
  906. return scripts
  907. @property
  908. def _property_entry_points(self):
  909. # console scripts common to all scenarios
  910. scripts = ['salt-call = salt.scripts:salt_call',
  911. 'salt-run = salt.scripts:salt_run']
  912. if self.ssh_packaging or PACKAGED_FOR_SALT_SSH:
  913. scripts.append('salt-ssh = salt.scripts:salt_ssh')
  914. if IS_WINDOWS_PLATFORM:
  915. return {'console_scripts': scripts}
  916. scripts.append('salt-cloud = salt.scripts:salt_cloud')
  917. return {'console_scripts': scripts}
  918. if IS_WINDOWS_PLATFORM:
  919. scripts.extend(['salt-cp = salt.scripts:salt_cp',
  920. 'salt-key = salt.scripts:salt_key',
  921. 'salt-minion = salt.scripts:salt_minion',
  922. 'salt-syndic = salt.scripts:salt_syndic',
  923. 'salt-unity = salt.scripts:salt_unity',
  924. 'spm = salt.scripts:salt_spm'])
  925. return {'console_scripts': scripts}
  926. # *nix, so, we need all scripts
  927. scripts.extend(['salt = salt.scripts:salt_main',
  928. 'salt-api = salt.scripts:salt_api',
  929. 'salt-cloud = salt.scripts:salt_cloud',
  930. 'salt-cp = salt.scripts:salt_cp',
  931. 'salt-key = salt.scripts:salt_key',
  932. 'salt-master = salt.scripts:salt_master',
  933. 'salt-minion = salt.scripts:salt_minion',
  934. 'salt-ssh = salt.scripts:salt_ssh',
  935. 'salt-syndic = salt.scripts:salt_syndic',
  936. 'salt-unity = salt.scripts:salt_unity',
  937. 'spm = salt.scripts:salt_spm'])
  938. return {'console_scripts': scripts}
  939. # <---- Dynamic Data ---------------------------------------------------------------------------------------------
  940. # ----- Esky Setup ---------------------------------------------------------------------------------------------->
  941. def setup_esky(self):
  942. opt_dict = self.get_option_dict('bdist_esky')
  943. opt_dict['freezer_module'] = ('setup script', 'bbfreeze')
  944. opt_dict['freezer_options'] = ('setup script', {'includes': self.get_esky_freezer_includes()})
  945. @property
  946. def _property_freezer_options(self):
  947. return {'includes': self.get_esky_freezer_includes()}
  948. def get_esky_freezer_includes(self):
  949. # Sometimes the auto module traversal doesn't find everything, so we
  950. # explicitly add it. The auto dependency tracking especially does not work for
  951. # imports occurring in salt.modules, as they are loaded at salt runtime.
  952. # Specifying includes that don't exist doesn't appear to cause a freezing
  953. # error.
  954. freezer_includes = [
  955. 'zmq.core.*',
  956. 'zmq.utils.*',
  957. 'ast',
  958. 'csv',
  959. 'difflib',
  960. 'distutils',
  961. 'distutils.version',
  962. 'numbers',
  963. 'json',
  964. 'M2Crypto',
  965. 'Cookie',
  966. 'asyncore',
  967. 'fileinput',
  968. 'sqlite3',
  969. 'email',
  970. 'email.mime.*',
  971. 'requests',
  972. 'sqlite3',
  973. ]
  974. if HAS_ZMQ and hasattr(zmq, 'pyzmq_version_info'):
  975. if HAS_ZMQ and zmq.pyzmq_version_info() >= (0, 14):
  976. # We're freezing, and when freezing ZMQ needs to be installed, so this
  977. # works fine
  978. if 'zmq.core.*' in freezer_includes:
  979. # For PyZMQ >= 0.14, freezing does not need 'zmq.core.*'
  980. freezer_includes.remove('zmq.core.*')
  981. if IS_WINDOWS_PLATFORM:
  982. freezer_includes.extend([
  983. 'imp',
  984. 'win32api',
  985. 'win32file',
  986. 'win32con',
  987. 'win32com',
  988. 'win32net',
  989. 'win32netcon',
  990. 'win32gui',
  991. 'win32security',
  992. 'ntsecuritycon',
  993. 'pywintypes',
  994. 'pythoncom',
  995. '_winreg',
  996. 'wmi',
  997. 'site',
  998. 'psutil',
  999. 'pytz',
  1000. ])
  1001. elif IS_SMARTOS_PLATFORM:
  1002. # we have them as requirements in pkg/smartos/esky/requirements.txt
  1003. # all these should be safe to force include
  1004. freezer_includes.extend([
  1005. 'cherrypy',
  1006. 'python-dateutil',
  1007. 'pyghmi',
  1008. 'croniter',
  1009. 'mako',
  1010. 'gnupg',
  1011. ])
  1012. elif sys.platform.startswith('linux'):
  1013. freezer_includes.append('spwd')
  1014. try:
  1015. import yum # pylint: disable=unused-variable
  1016. freezer_includes.append('yum')
  1017. except ImportError:
  1018. pass
  1019. elif sys.platform.startswith('sunos'):
  1020. # (The sledgehammer approach)
  1021. # Just try to include everything
  1022. # (This may be a better way to generate freezer_includes generally)
  1023. try:
  1024. from bbfreeze.modulegraph.modulegraph import ModuleGraph
  1025. mgraph = ModuleGraph(sys.path[:])
  1026. for arg in glob.glob('salt/modules/*.py'):
  1027. mgraph.run_script(arg)
  1028. for mod in mgraph.flatten():
  1029. if type(mod).__name__ != 'Script' and mod.filename:
  1030. freezer_includes.append(str(os.path.basename(mod.identifier)))
  1031. except ImportError:
  1032. pass
  1033. return freezer_includes
  1034. # <---- Esky Setup -----------------------------------------------------------------------------------------------
  1035. # ----- Overridden Methods -------------------------------------------------------------------------------------->
  1036. def parse_command_line(self):
  1037. args = distutils.dist.Distribution.parse_command_line(self)
  1038. if not self.ssh_packaging and PACKAGED_FOR_SALT_SSH:
  1039. self.ssh_packaging = 1
  1040. if self.ssh_packaging:
  1041. self.metadata.name = 'salt-ssh'
  1042. self.salt_transport = 'ssh'
  1043. elif self.salt_transport is None:
  1044. self.salt_transport = 'zeromq'
  1045. if self.salt_transport not in ('zeromq', 'both', 'ssh', 'none'):
  1046. raise DistutilsArgError(
  1047. 'The value of --salt-transport needs be \'zeromq\', '
  1048. '\'both\', \'ssh\', or \'none\' not \'{0}\''.format(
  1049. self.salt_transport
  1050. )
  1051. )
  1052. # Setup our property functions after class initialization and
  1053. # after parsing the command line since most are set to None
  1054. # ATTENTION: This should be the last step before returning the args or
  1055. # some of the requirements won't be correctly set
  1056. for funcname in dir(self):
  1057. if not funcname.startswith('_property_'):
  1058. continue
  1059. property_name = funcname.split('_property_', 1)[-1]
  1060. setattr(self, property_name, getattr(self, funcname))
  1061. return args
  1062. # <---- Overridden Methods ---------------------------------------------------------------------------------------
  1063. # <---- Custom Distribution Class ------------------------------------------------------------------------------------
  1064. if __name__ == '__main__':
  1065. setup(distclass=SaltDistribution)