runtests.py 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. '''
  4. Discover all instances of unittest.TestCase in this directory.
  5. '''
  6. # pylint: disable=file-perms
  7. # Import python libs
  8. from __future__ import absolute_import, print_function
  9. import os
  10. import sys
  11. import time
  12. import warnings
  13. import collections
  14. TESTS_DIR = os.path.dirname(os.path.normpath(os.path.abspath(__file__)))
  15. if os.name == 'nt':
  16. TESTS_DIR = TESTS_DIR.replace('\\', '\\\\')
  17. CODE_DIR = os.path.dirname(TESTS_DIR)
  18. # Let's inject CODE_DIR so salt is importable if not there already
  19. if '' in sys.path:
  20. sys.path.remove('')
  21. if TESTS_DIR in sys.path:
  22. sys.path.remove(TESTS_DIR)
  23. if CODE_DIR in sys.path and sys.path[0] != CODE_DIR:
  24. sys.path.remove(CODE_DIR)
  25. if CODE_DIR not in sys.path:
  26. sys.path.insert(0, CODE_DIR)
  27. if TESTS_DIR not in sys.path:
  28. sys.path.insert(1, TESTS_DIR)
  29. try:
  30. import tests
  31. if not tests.__file__.startswith(CODE_DIR):
  32. print('Found tests module not from salt in {}'.format(tests.__file__))
  33. sys.modules.pop('tests')
  34. module_dir = os.path.dirname(tests.__file__)
  35. if module_dir in sys.path:
  36. sys.path.remove(module_dir)
  37. del tests
  38. except ImportError:
  39. pass
  40. # Import salt libs
  41. try:
  42. from tests.support.paths import TMP, SYS_TMP_DIR, INTEGRATION_TEST_DIR
  43. from tests.support.paths import CODE_DIR as SALT_ROOT
  44. except ImportError as exc:
  45. try:
  46. import tests
  47. print('Found tests module not from salt in {}'.format(tests.__file__))
  48. except ImportError:
  49. print('Unable to import salt test module')
  50. print('PYTHONPATH:', os.environ.get('PYTHONPATH'))
  51. print('Current sys.path:')
  52. import pprint
  53. pprint.pprint(sys.path)
  54. raise exc
  55. from tests.integration import TestDaemon, TestDaemonStartFailed # pylint: disable=W0403
  56. from tests.multimaster import MultimasterTestDaemon
  57. import salt.utils.platform
  58. if not salt.utils.platform.is_windows():
  59. import resource
  60. # Import Salt Testing libs
  61. from tests.support.parser import PNUM, print_header
  62. from tests.support.parser.cover import SaltCoverageTestingParser
  63. XML_OUTPUT_DIR = os.environ.get(
  64. 'SALT_XML_TEST_REPORTS_DIR',
  65. os.path.join(TMP, 'xml-test-reports')
  66. )
  67. HTML_OUTPUT_DIR = os.environ.get(
  68. 'SALT_HTML_TEST_REPORTS_DIR',
  69. os.path.join(TMP, 'html-test-reports')
  70. )
  71. TEST_DIR = os.path.dirname(INTEGRATION_TEST_DIR)
  72. try:
  73. if SALT_ROOT:
  74. os.chdir(SALT_ROOT)
  75. except OSError as err:
  76. print('Failed to change directory to salt\'s source: {0}'.format(err))
  77. # Soft and hard limits on max open filehandles
  78. MAX_OPEN_FILES = {
  79. 'integration': {
  80. 'soft_limit': 3072,
  81. 'hard_limit': 4096,
  82. },
  83. 'unit': {
  84. 'soft_limit': 1024,
  85. 'hard_limit': 2048,
  86. },
  87. }
  88. # Combine info from command line options and test suite directories. A test
  89. # suite is a python package of test modules relative to the tests directory.
  90. TEST_SUITES_UNORDERED = {
  91. 'unit':
  92. {'display_name': 'Unit',
  93. 'path': 'unit'},
  94. 'kitchen':
  95. {'display_name': 'Kitchen',
  96. 'path': 'kitchen'},
  97. 'multimaster':
  98. {'display_name': 'Multimaster',
  99. 'path': 'multimaster'},
  100. 'module':
  101. {'display_name': 'Module',
  102. 'path': 'integration/modules'},
  103. 'state':
  104. {'display_name': 'State',
  105. 'path': 'integration/states'},
  106. 'cli':
  107. {'display_name': 'CLI',
  108. 'path': 'integration/cli'},
  109. 'client':
  110. {'display_name': 'Client',
  111. 'path': 'integration/client'},
  112. 'doc':
  113. {'display_name': 'Documentation',
  114. 'path': 'integration/doc'},
  115. 'ext_pillar':
  116. {'display_name': 'External Pillar',
  117. 'path': 'integration/pillar'},
  118. 'grains':
  119. {'display_name': 'Grains',
  120. 'path': 'integration/grains'},
  121. 'shell':
  122. {'display_name': 'Shell',
  123. 'path': 'integration/shell'},
  124. 'runners':
  125. {'display_name': 'Runners',
  126. 'path': 'integration/runners'},
  127. 'renderers':
  128. {'display_name': 'Renderers',
  129. 'path': 'integration/renderers'},
  130. 'returners':
  131. {'display_name': 'Returners',
  132. 'path': 'integration/returners'},
  133. 'ssh-int':
  134. {'display_name': 'SSH Integration',
  135. 'path': 'integration/ssh'},
  136. 'spm':
  137. {'display_name': 'SPM',
  138. 'path': 'integration/spm'},
  139. 'loader':
  140. {'display_name': 'Loader',
  141. 'path': 'integration/loader'},
  142. 'outputter':
  143. {'display_name': 'Outputter',
  144. 'path': 'integration/output'},
  145. 'fileserver':
  146. {'display_name': 'Fileserver',
  147. 'path': 'integration/fileserver'},
  148. 'wheel':
  149. {'display_name': 'Wheel',
  150. 'path': 'integration/wheel'},
  151. 'api':
  152. {'display_name': 'NetAPI',
  153. 'path': 'integration/netapi'},
  154. 'cloud_provider':
  155. {'display_name': 'Cloud Provider',
  156. 'path': 'integration/cloud/clouds'},
  157. 'minion':
  158. {'display_name': 'Minion',
  159. 'path': 'integration/minion'},
  160. 'reactor':
  161. {'display_name': 'Reactor',
  162. 'path': 'integration/reactor'},
  163. 'proxy':
  164. {'display_name': 'Proxy',
  165. 'path': 'integration/proxy'},
  166. 'external_api':
  167. {'display_name': 'ExternalAPIs',
  168. 'path': 'integration/externalapi'},
  169. 'daemons':
  170. {'display_name': 'Daemon',
  171. 'path': 'integration/daemons'},
  172. 'scheduler':
  173. {'display_name': 'Scheduler',
  174. 'path': 'integration/scheduler'},
  175. 'sdb':
  176. {'display_name': 'Sdb',
  177. 'path': 'integration/sdb'},
  178. 'logging':
  179. {'display_name': 'Logging',
  180. 'path': 'integration/logging'},
  181. }
  182. TEST_SUITES = collections.OrderedDict(sorted(TEST_SUITES_UNORDERED.items(),
  183. key=lambda x: x[0]))
  184. class SaltTestsuiteParser(SaltCoverageTestingParser):
  185. support_docker_execution = True
  186. support_destructive_tests_selection = True
  187. source_code_basedir = SALT_ROOT
  188. def _get_suites(self, include_unit=False, include_cloud_provider=False,
  189. include_proxy=False, include_kitchen=False, include_multimaster=False):
  190. '''
  191. Return a set of all test suites except unit and cloud provider tests
  192. unless requested
  193. '''
  194. suites = set(TEST_SUITES.keys())
  195. if not include_unit:
  196. suites -= set(['unit'])
  197. if not include_cloud_provider:
  198. suites -= set(['cloud_provider'])
  199. if not include_proxy:
  200. suites -= set(['proxy'])
  201. if not include_kitchen:
  202. suites -= set(['kitchen'])
  203. if not include_multimaster:
  204. suites -= set(['multimaster'])
  205. return suites
  206. def _check_enabled_suites(self, include_unit=False,
  207. include_cloud_provider=False,
  208. include_proxy=False,
  209. include_kitchen=False,
  210. include_multimaster=False):
  211. '''
  212. Query whether test suites have been enabled
  213. '''
  214. suites = self._get_suites(include_unit=include_unit,
  215. include_cloud_provider=include_cloud_provider,
  216. include_proxy=include_proxy,
  217. include_kitchen=include_kitchen,
  218. include_multimaster=include_multimaster)
  219. return any([getattr(self.options, suite) for suite in suites])
  220. def _enable_suites(self, include_unit=False, include_cloud_provider=False,
  221. include_proxy=False, include_kitchen=False, include_multimaster=False):
  222. '''
  223. Enable test suites for current test run
  224. '''
  225. suites = self._get_suites(include_unit=include_unit,
  226. include_cloud_provider=include_cloud_provider,
  227. include_proxy=include_proxy,
  228. include_kitchen=include_kitchen,
  229. include_multimaster=include_multimaster)
  230. for suite in suites:
  231. setattr(self.options, suite, True)
  232. def setup_additional_options(self):
  233. self.add_option(
  234. '--sysinfo',
  235. default=False,
  236. action='store_true',
  237. help='Print some system information.'
  238. )
  239. self.add_option(
  240. '--transport',
  241. default='zeromq',
  242. choices=('zeromq', 'tcp'),
  243. help=('Select which transport to run the integration tests with, '
  244. 'zeromq or tcp. Default: %default')
  245. )
  246. self.add_option(
  247. '--interactive',
  248. default=False,
  249. action='store_true',
  250. help='Do not run any tests. Simply start the daemons.'
  251. )
  252. self.output_options_group.add_option(
  253. '--no-colors',
  254. '--no-colours',
  255. default=False,
  256. action='store_true',
  257. help='Disable colour printing.'
  258. )
  259. self.test_selection_group.add_option(
  260. '-m',
  261. '--module',
  262. '--module-tests',
  263. dest='module',
  264. default=False,
  265. action='store_true',
  266. help='Run tests for modules'
  267. )
  268. self.test_selection_group.add_option(
  269. '-S',
  270. '--state',
  271. '--state-tests',
  272. dest='state',
  273. default=False,
  274. action='store_true',
  275. help='Run tests for states'
  276. )
  277. self.test_selection_group.add_option(
  278. '-C',
  279. '--cli',
  280. '--cli-tests',
  281. dest='cli',
  282. default=False,
  283. action='store_true',
  284. help='Run tests for cli'
  285. )
  286. self.test_selection_group.add_option(
  287. '-c',
  288. '--client',
  289. '--client-tests',
  290. dest='client',
  291. default=False,
  292. action='store_true',
  293. help='Run tests for client'
  294. )
  295. self.test_selection_group.add_option(
  296. '-d',
  297. '--doc',
  298. '--doc-tests',
  299. dest='doc',
  300. default=False,
  301. action='store_true',
  302. help='Run tests for documentation'
  303. )
  304. self.test_selection_group.add_option(
  305. '-I',
  306. '--ext-pillar',
  307. '--ext-pillar-tests',
  308. dest='ext_pillar',
  309. default=False,
  310. action='store_true',
  311. help='Run ext_pillar tests'
  312. )
  313. self.test_selection_group.add_option(
  314. '-G',
  315. '--grains',
  316. '--grains-tests',
  317. dest='grains',
  318. default=False,
  319. action='store_true',
  320. help='Run tests for grains'
  321. )
  322. self.test_selection_group.add_option(
  323. '-s',
  324. '--shell',
  325. '--shell-tests',
  326. dest='shell',
  327. default=False,
  328. action='store_true',
  329. help='Run shell tests'
  330. )
  331. self.test_selection_group.add_option(
  332. '-r',
  333. '--runners',
  334. '--runner-tests',
  335. dest='runners',
  336. default=False,
  337. action='store_true',
  338. help='Run salt/runners/*.py tests'
  339. )
  340. self.test_selection_group.add_option(
  341. '-R',
  342. '--renderers',
  343. '--renderer-tests',
  344. dest='renderers',
  345. default=False,
  346. action='store_true',
  347. help='Run salt/renderers/*.py tests'
  348. )
  349. self.test_selection_group.add_option(
  350. '--reactor',
  351. dest='reactor',
  352. default=False,
  353. action='store_true',
  354. help='Run salt/reactor/*.py tests'
  355. )
  356. self.test_selection_group.add_option(
  357. '--minion',
  358. '--minion-tests',
  359. dest='minion',
  360. default=False,
  361. action='store_true',
  362. help='Run tests for minion'
  363. )
  364. self.test_selection_group.add_option(
  365. '--returners',
  366. dest='returners',
  367. default=False,
  368. action='store_true',
  369. help='Run salt/returners/*.py tests'
  370. )
  371. self.test_selection_group.add_option(
  372. '--spm',
  373. dest='spm',
  374. default=False,
  375. action='store_true',
  376. help='Run spm integration tests'
  377. )
  378. self.test_selection_group.add_option(
  379. '-l',
  380. '--loader',
  381. '--loader-tests',
  382. dest='loader',
  383. default=False,
  384. action='store_true',
  385. help='Run loader tests'
  386. )
  387. self.test_selection_group.add_option(
  388. '-u',
  389. '--unit',
  390. '--unit-tests',
  391. dest='unit',
  392. default=False,
  393. action='store_true',
  394. help='Run unit tests'
  395. )
  396. self.test_selection_group.add_option(
  397. '-k',
  398. '--kitchen',
  399. '--kitchen-tests',
  400. dest='kitchen',
  401. default=False,
  402. action='store_true',
  403. help='Run kitchen tests'
  404. )
  405. self.test_selection_group.add_option(
  406. '--fileserver',
  407. '--fileserver-tests',
  408. dest='fileserver',
  409. default=False,
  410. action='store_true',
  411. help='Run Fileserver tests'
  412. )
  413. self.test_selection_group.add_option(
  414. '-w',
  415. '--wheel',
  416. '--wheel-tests',
  417. dest='wheel',
  418. action='store_true',
  419. default=False,
  420. help='Run wheel tests'
  421. )
  422. self.test_selection_group.add_option(
  423. '-o',
  424. '--outputter',
  425. '--outputter-tests',
  426. dest='outputter',
  427. action='store_true',
  428. default=False,
  429. help='Run outputter tests'
  430. )
  431. self.test_selection_group.add_option(
  432. '--cloud-provider',
  433. '--cloud-provider-tests',
  434. dest='cloud_provider',
  435. action='store_true',
  436. default=False,
  437. help=('Run cloud provider tests. These tests create and delete '
  438. 'instances on cloud providers. Must provide valid credentials '
  439. 'in salt/tests/integration/files/conf/cloud.*.d to run tests.')
  440. )
  441. self.test_selection_group.add_option(
  442. '--ssh',
  443. '--ssh-tests',
  444. dest='ssh',
  445. action='store_true',
  446. default=False,
  447. help='Run salt-ssh tests. These tests will spin up a temporary '
  448. 'SSH server on your machine. In certain environments, this '
  449. 'may be insecure! Default: False'
  450. )
  451. self.test_selection_group.add_option(
  452. '--ssh-int',
  453. dest='ssh-int',
  454. action='store_true',
  455. default=False,
  456. help='Run salt-ssh integration tests. Requires to be run with --ssh'
  457. 'to spin up the SSH server on your machine.'
  458. )
  459. self.test_selection_group.add_option(
  460. '-A',
  461. '--api',
  462. '--api-tests',
  463. dest='api',
  464. action='store_true',
  465. default=False,
  466. help='Run salt-api tests'
  467. )
  468. self.test_selection_group.add_option(
  469. '--sdb',
  470. '--sdb-tests',
  471. dest='sdb',
  472. action='store_true',
  473. default=False,
  474. help='Run sdb tests'
  475. )
  476. self.test_selection_group.add_option(
  477. '-P',
  478. '--proxy',
  479. '--proxy-tests',
  480. dest='proxy',
  481. action='store_true',
  482. default=False,
  483. help='Run salt-proxy tests'
  484. )
  485. self.test_selection_group.add_option(
  486. '--external',
  487. '--external-api',
  488. '--external-api-tests',
  489. dest='external_api',
  490. action='store_true',
  491. default=False,
  492. help='Run venafi runner tests'
  493. )
  494. self.test_selection_group.add_option(
  495. '--daemons',
  496. '--daemon-tests',
  497. dest='daemons',
  498. action='store_true',
  499. default=False,
  500. help='Run salt/daemons/*.py tests'
  501. )
  502. self.test_selection_group.add_option(
  503. '--scheduler',
  504. dest='scheduler',
  505. action='store_true',
  506. default=False,
  507. help='Run scheduler integration tests'
  508. )
  509. self.test_selection_group.add_option(
  510. '--logging',
  511. dest='logging',
  512. action='store_true',
  513. default=False,
  514. help='Run logging integration tests'
  515. )
  516. self.test_selection_group.add_option(
  517. '--multimaster',
  518. dest='multimaster',
  519. action='store_true',
  520. default=False,
  521. help='Start multimaster daemons and run multimaster integration tests'
  522. )
  523. def validate_options(self):
  524. if self.options.cloud_provider or self.options.external_api:
  525. # Turn on expensive tests execution
  526. os.environ['EXPENSIVE_TESTS'] = 'True'
  527. # This fails even with salt.utils.platform imported in the global
  528. # scope, unless we import it again here.
  529. import salt.utils.platform
  530. if salt.utils.platform.is_windows():
  531. import salt.utils.win_functions
  532. current_user = salt.utils.win_functions.get_current_user()
  533. if current_user == 'SYSTEM':
  534. is_admin = True
  535. else:
  536. is_admin = salt.utils.win_functions.is_admin(current_user)
  537. if self.options.coverage and any((
  538. self.options.name,
  539. not is_admin,
  540. not self.options.run_destructive)) \
  541. and self._check_enabled_suites(include_unit=True):
  542. warnings.warn("Test suite not running with elevated priviledges")
  543. else:
  544. is_admin = os.geteuid() == 0
  545. if self.options.coverage and any((
  546. self.options.name,
  547. not is_admin,
  548. not self.options.run_destructive)) \
  549. and self._check_enabled_suites(include_unit=True):
  550. self.error(
  551. 'No sense in generating the tests coverage report when '
  552. 'not running the full test suite, including the '
  553. 'destructive tests, as \'root\'. It would only produce '
  554. 'incorrect results.'
  555. )
  556. # When no tests are specifically enumerated on the command line, setup
  557. # a default run: +unit -cloud_provider
  558. if not self.options.name and not \
  559. self._check_enabled_suites(include_unit=True,
  560. include_cloud_provider=True,
  561. include_proxy=True,
  562. include_kitchen=True,
  563. include_multimaster=True):
  564. self._enable_suites(include_unit=True, include_multimaster=True)
  565. self.start_coverage(
  566. branch=True,
  567. source=[os.path.join(SALT_ROOT, 'salt')],
  568. )
  569. # Print out which version of python this test suite is running on
  570. print(' * Python Version: {0}'.format(' '.join(sys.version.split())))
  571. # Transplant configuration
  572. TestDaemon.transplant_configs(transport=self.options.transport)
  573. MultimasterTestDaemon.transplant_configs(transport=self.options.transport)
  574. def post_execution_cleanup(self):
  575. SaltCoverageTestingParser.post_execution_cleanup(self)
  576. if self.options.clean:
  577. TestDaemon.clean()
  578. def run_integration_suite(self, path='', display_name=''):
  579. '''
  580. Run an integration test suite
  581. '''
  582. full_path = os.path.join(TEST_DIR, path)
  583. return self.run_suite(
  584. full_path, display_name, suffix='test_*.py',
  585. failfast=self.options.failfast,
  586. )
  587. def start_daemons_only(self):
  588. if not salt.utils.platform.is_windows():
  589. self.set_filehandle_limits('integration')
  590. try:
  591. print_header(
  592. ' * Setting up Salt daemons for interactive use',
  593. top=False, width=getattr(self.options, 'output_columns', PNUM)
  594. )
  595. except TypeError:
  596. print_header(' * Setting up Salt daemons for interactive use', top=False)
  597. try:
  598. with TestDaemon(self):
  599. print_header(' * Salt daemons started')
  600. master_conf = TestDaemon.config('master')
  601. minion_conf = TestDaemon.config('minion')
  602. proxy_conf = TestDaemon.config('proxy')
  603. sub_minion_conf = TestDaemon.config('sub_minion')
  604. syndic_conf = TestDaemon.config('syndic')
  605. syndic_master_conf = TestDaemon.config('syndic_master')
  606. print_header(' * Syndic master configuration values (MoM)', top=False)
  607. print('interface: {0}'.format(syndic_master_conf['interface']))
  608. print('publish port: {0}'.format(syndic_master_conf['publish_port']))
  609. print('return port: {0}'.format(syndic_master_conf['ret_port']))
  610. print('\n')
  611. print_header(' * Syndic configuration values', top=True)
  612. print('interface: {0}'.format(syndic_conf['interface']))
  613. print('syndic master: {0}'.format(syndic_conf['syndic_master']))
  614. print('syndic master port: {0}'.format(syndic_conf['syndic_master_port']))
  615. print('\n')
  616. print_header(' * Master configuration values', top=True)
  617. print('interface: {0}'.format(master_conf['interface']))
  618. print('publish port: {0}'.format(master_conf['publish_port']))
  619. print('return port: {0}'.format(master_conf['ret_port']))
  620. print('\n')
  621. print_header(' * Minion configuration values', top=True)
  622. print('interface: {0}'.format(minion_conf['interface']))
  623. print('master: {0}'.format(minion_conf['master']))
  624. print('master port: {0}'.format(minion_conf['master_port']))
  625. if minion_conf['ipc_mode'] == 'tcp':
  626. print('tcp pub port: {0}'.format(minion_conf['tcp_pub_port']))
  627. print('tcp pull port: {0}'.format(minion_conf['tcp_pull_port']))
  628. print('\n')
  629. print_header(' * Sub Minion configuration values', top=True)
  630. print('interface: {0}'.format(sub_minion_conf['interface']))
  631. print('master: {0}'.format(sub_minion_conf['master']))
  632. print('master port: {0}'.format(sub_minion_conf['master_port']))
  633. if sub_minion_conf['ipc_mode'] == 'tcp':
  634. print('tcp pub port: {0}'.format(sub_minion_conf['tcp_pub_port']))
  635. print('tcp pull port: {0}'.format(sub_minion_conf['tcp_pull_port']))
  636. print('\n')
  637. print_header(' * Proxy Minion configuration values', top=True)
  638. print('interface: {0}'.format(proxy_conf['interface']))
  639. print('master: {0}'.format(proxy_conf['master']))
  640. print('master port: {0}'.format(proxy_conf['master_port']))
  641. if minion_conf['ipc_mode'] == 'tcp':
  642. print('tcp pub port: {0}'.format(proxy_conf['tcp_pub_port']))
  643. print('tcp pull port: {0}'.format(proxy_conf['tcp_pull_port']))
  644. print('\n')
  645. print_header(' Your client configuration is at {0}'.format(TestDaemon.config_location()))
  646. print('To access the minion: salt -c {0} minion test.ping'.format(TestDaemon.config_location()))
  647. while True:
  648. time.sleep(1)
  649. except TestDaemonStartFailed:
  650. self.exit(status=2)
  651. def start_multimaster_daemons_only(self):
  652. if not salt.utils.platform.is_windows():
  653. self.set_filehandle_limits('integration')
  654. try:
  655. print_header(
  656. ' * Setting up Salt daemons for interactive use',
  657. top=False, width=getattr(self.options, 'output_columns', PNUM)
  658. )
  659. except TypeError:
  660. print_header(' * Setting up Salt daemons for interactive use', top=False)
  661. try:
  662. with MultimasterTestDaemon(self):
  663. print_header(' * Salt daemons started')
  664. master_conf = MultimasterTestDaemon.config('mm_master')
  665. sub_master_conf = MultimasterTestDaemon.config('mm_sub_master')
  666. minion_conf = MultimasterTestDaemon.config('mm_minion')
  667. sub_minion_conf = MultimasterTestDaemon.config('mm_sub_minion')
  668. print_header(' * Master configuration values', top=True)
  669. print('interface: {0}'.format(master_conf['interface']))
  670. print('publish port: {0}'.format(master_conf['publish_port']))
  671. print('return port: {0}'.format(master_conf['ret_port']))
  672. print('\n')
  673. print_header(' * Second master configuration values', top=True)
  674. print('interface: {0}'.format(sub_master_conf['interface']))
  675. print('publish port: {0}'.format(sub_master_conf['publish_port']))
  676. print('return port: {0}'.format(sub_master_conf['ret_port']))
  677. print('\n')
  678. print_header(' * Minion configuration values', top=True)
  679. print('interface: {0}'.format(minion_conf['interface']))
  680. print('masters: {0}'.format(', '.join(minion_conf['master'])))
  681. if minion_conf['ipc_mode'] == 'tcp':
  682. print('tcp pub port: {0}'.format(minion_conf['tcp_pub_port']))
  683. print('tcp pull port: {0}'.format(minion_conf['tcp_pull_port']))
  684. print('\n')
  685. print_header(' * Sub Minion configuration values', top=True)
  686. print('interface: {0}'.format(sub_minion_conf['interface']))
  687. print('masters: {0}'.format(', '.join(sub_minion_conf['master'])))
  688. if sub_minion_conf['ipc_mode'] == 'tcp':
  689. print('tcp pub port: {0}'.format(sub_minion_conf['tcp_pub_port']))
  690. print('tcp pull port: {0}'.format(sub_minion_conf['tcp_pull_port']))
  691. print('\n')
  692. print_header(' Your client configurations are at {0}'.format(
  693. ', '.join(MultimasterTestDaemon.config_location())))
  694. print('To access minions from different masters use:')
  695. for location in MultimasterTestDaemon.config_location():
  696. print(' salt -c {0} minion test.ping'.format(location))
  697. while True:
  698. time.sleep(1)
  699. except TestDaemonStartFailed:
  700. self.exit(status=2)
  701. def set_filehandle_limits(self, limits='integration'):
  702. '''
  703. Set soft and hard limits on open file handles at required thresholds
  704. for integration tests or unit tests
  705. '''
  706. # Get current limits
  707. if salt.utils.platform.is_windows():
  708. import win32file
  709. prev_hard = win32file._getmaxstdio()
  710. prev_soft = 512
  711. else:
  712. prev_soft, prev_hard = resource.getrlimit(resource.RLIMIT_NOFILE)
  713. # Get required limits
  714. min_soft = MAX_OPEN_FILES[limits]['soft_limit']
  715. min_hard = MAX_OPEN_FILES[limits]['hard_limit']
  716. # Check minimum required limits
  717. set_limits = False
  718. if prev_soft < min_soft:
  719. soft = min_soft
  720. set_limits = True
  721. else:
  722. soft = prev_soft
  723. if prev_hard < min_hard:
  724. hard = min_hard
  725. set_limits = True
  726. else:
  727. hard = prev_hard
  728. # Increase limits
  729. if set_limits:
  730. print(
  731. ' * Max open files settings is too low (soft: {0}, hard: {1}) '
  732. 'for running the tests'.format(prev_soft, prev_hard)
  733. )
  734. print(
  735. ' * Trying to raise the limits to soft: '
  736. '{0}, hard: {1}'.format(soft, hard)
  737. )
  738. try:
  739. if salt.utils.platform.is_windows():
  740. hard = 2048 if hard > 2048 else hard
  741. win32file._setmaxstdio(hard)
  742. else:
  743. resource.setrlimit(resource.RLIMIT_NOFILE, (soft, hard))
  744. except Exception as err:
  745. print(
  746. 'ERROR: Failed to raise the max open files settings -> '
  747. '{0}'.format(err)
  748. )
  749. print('Please issue the following command on your console:')
  750. print(' ulimit -n {0}'.format(soft))
  751. self.exit()
  752. finally:
  753. print('~' * getattr(self.options, 'output_columns', PNUM))
  754. def run_integration_tests(self):
  755. '''
  756. Execute the integration tests suite
  757. '''
  758. named_tests = []
  759. named_unit_test = []
  760. if self.options.name:
  761. for test in self.options.name:
  762. if test.startswith(('tests.unit.', 'unit.',
  763. 'test.kitchen.', 'kitchen.',
  764. 'test.multimaster.', 'multimaster.')):
  765. named_unit_test.append(test)
  766. continue
  767. named_tests.append(test)
  768. if (self.options.unit or self.options.kitchen or self.options.multimaster or named_unit_test) \
  769. and not named_tests \
  770. and (self.options.from_filenames or
  771. not self._check_enabled_suites(include_cloud_provider=True)):
  772. # We're either not running any integration test suites, or we're
  773. # only running unit tests by passing --unit or by passing only
  774. # `unit.<whatever>` to --name. We don't need the tests daemon
  775. # running
  776. return [True]
  777. if not salt.utils.platform.is_windows():
  778. self.set_filehandle_limits('integration')
  779. try:
  780. print_header(
  781. ' * Setting up Salt daemons to execute tests',
  782. top=False, width=getattr(self.options, 'output_columns', PNUM)
  783. )
  784. except TypeError:
  785. print_header(' * Setting up Salt daemons to execute tests', top=False)
  786. status = []
  787. # Return an empty status if no tests have been enabled
  788. if not self._check_enabled_suites(include_cloud_provider=True, include_proxy=True) and not self.options.name:
  789. return status
  790. try:
  791. with TestDaemon(self):
  792. if self.options.name:
  793. for name in self.options.name:
  794. name = name.strip()
  795. if not name:
  796. continue
  797. if os.path.isfile(name):
  798. if not name.endswith('.py'):
  799. continue
  800. if name.startswith((os.path.join('tests', 'unit'),
  801. os.path.join('tests', 'multimaster'))):
  802. continue
  803. results = self.run_suite(os.path.dirname(name),
  804. name,
  805. suffix=os.path.basename(name),
  806. failfast=self.options.failfast,
  807. load_from_name=False)
  808. status.append(results)
  809. continue
  810. if name.startswith(('tests.unit.', 'unit.',
  811. 'tests.multimaster.', 'multimaster.')):
  812. continue
  813. results = self.run_suite(
  814. '', name, suffix='test_*.py', load_from_name=True,
  815. failfast=self.options.failfast,
  816. )
  817. status.append(results)
  818. return status
  819. for suite in TEST_SUITES:
  820. if suite != 'unit' and suite != 'multimaster' and getattr(self.options, suite):
  821. status.append(self.run_integration_suite(**TEST_SUITES[suite]))
  822. return status
  823. except TestDaemonStartFailed:
  824. self.exit(status=2)
  825. def run_multimaster_tests(self):
  826. '''
  827. Execute the multimaster tests suite
  828. '''
  829. named_tests = []
  830. named_unit_test = []
  831. if self.options.name:
  832. for test in self.options.name:
  833. if test.startswith(('tests.multimaster.', 'multimaster.')):
  834. named_tests.append(test)
  835. # TODO: check 'from_filenames'
  836. if not self.options.multimaster and not named_tests:
  837. # We're not running any multimaster test suites.
  838. return [True]
  839. if not salt.utils.platform.is_windows():
  840. self.set_filehandle_limits('integration')
  841. try:
  842. print_header(
  843. ' * Setting up multimaster Salt daemons to execute tests',
  844. top=False, width=getattr(self.options, 'output_columns', PNUM)
  845. )
  846. except TypeError:
  847. print_header(' * Setting up multimaster Salt daemons to execute tests', top=False)
  848. status = []
  849. try:
  850. with MultimasterTestDaemon(self):
  851. if self.options.name:
  852. for name in self.options.name:
  853. name = name.strip()
  854. if not name:
  855. continue
  856. if os.path.isfile(name):
  857. if not name.endswith('.py'):
  858. continue
  859. if not name.startswith(os.path.join('tests', 'multimaster')):
  860. continue
  861. results = self.run_suite(os.path.dirname(name),
  862. name,
  863. suffix=os.path.basename(name),
  864. load_from_name=False)
  865. status.append(results)
  866. continue
  867. if not name.startswith(('tests.multimaster.', 'multimaster.')):
  868. continue
  869. results = self.run_suite('', name, suffix='test_*.py', load_from_name=True)
  870. status.append(results)
  871. return status
  872. status.append(self.run_integration_suite(**TEST_SUITES['multimaster']))
  873. return status
  874. except TestDaemonStartFailed:
  875. self.exit(status=2)
  876. def run_unit_tests(self):
  877. '''
  878. Execute the unit tests
  879. '''
  880. named_unit_test = []
  881. if self.options.name:
  882. for test in self.options.name:
  883. if not test.startswith(('tests.unit.', 'unit.')):
  884. continue
  885. named_unit_test.append(test)
  886. if not named_unit_test \
  887. and (self.options.from_filenames or not self.options.unit):
  888. # We are not explicitly running the unit tests and none of the
  889. # names passed to --name (or derived via --from-filenames) is a
  890. # unit test.
  891. return [True]
  892. status = []
  893. if self.options.unit:
  894. # MacOS needs more open filehandles for running unit test suite
  895. self.set_filehandle_limits('unit')
  896. results = self.run_suite(
  897. os.path.join(TEST_DIR, 'unit'), 'Unit', suffix='test_*.py',
  898. failfast=self.options.failfast,
  899. )
  900. status.append(results)
  901. # We executed ALL unittests, we can skip running unittests by name
  902. # below
  903. return status
  904. for name in named_unit_test:
  905. results = self.run_suite(
  906. os.path.join(TEST_DIR, 'unit'), name, suffix='test_*.py',
  907. load_from_name=True, failfast=self.options.failfast,
  908. )
  909. status.append(results)
  910. return status
  911. def run_kitchen_tests(self):
  912. '''
  913. Execute the kitchen tests
  914. '''
  915. named_kitchen_test = []
  916. if self.options.name:
  917. for test in self.options.name:
  918. if not test.startswith(('tests.kitchen.', 'kitchen.')):
  919. continue
  920. named_kitchen_test.append(test)
  921. if not self.options.kitchen and not named_kitchen_test:
  922. # We are not explicitly running the unit tests and none of the
  923. # names passed to --name is a unit test.
  924. return [True]
  925. status = []
  926. if self.options.kitchen:
  927. results = self.run_suite(
  928. os.path.join(TEST_DIR, 'kitchen'), 'Kitchen', suffix='test_*.py'
  929. )
  930. status.append(results)
  931. # We executed ALL unittests, we can skip running unittests by name
  932. # below
  933. return status
  934. for name in named_kitchen_test:
  935. results = self.run_suite(
  936. os.path.join(TEST_DIR, 'kitchen'), name, suffix='test_*.py', load_from_name=True
  937. )
  938. status.append(results)
  939. return status
  940. def main(**kwargs):
  941. '''
  942. Parse command line options for running specific tests
  943. '''
  944. try:
  945. parser = SaltTestsuiteParser(
  946. TEST_DIR,
  947. xml_output_dir=XML_OUTPUT_DIR,
  948. tests_logfile=os.path.join(SYS_TMP_DIR, 'salt-runtests.log')
  949. )
  950. parser.parse_args()
  951. # Override parser options (helpful when importing runtests.py and
  952. # running from within a REPL). Using kwargs.items() to avoid importing
  953. # six, as this feature will rarely be used.
  954. for key, val in kwargs.items():
  955. setattr(parser.options, key, val)
  956. overall_status = []
  957. if parser.options.interactive:
  958. if parser.options.multimaster:
  959. parser.start_multimaster_daemons_only()
  960. else:
  961. parser.start_daemons_only()
  962. status = parser.run_integration_tests()
  963. overall_status.extend(status)
  964. status = parser.run_multimaster_tests()
  965. overall_status.extend(status)
  966. status = parser.run_unit_tests()
  967. overall_status.extend(status)
  968. status = parser.run_kitchen_tests()
  969. overall_status.extend(status)
  970. false_count = overall_status.count(False)
  971. if false_count > 0:
  972. parser.finalize(1)
  973. parser.finalize(0)
  974. except KeyboardInterrupt:
  975. print('\nCaught keyboard interrupt. Exiting.\n')
  976. exit(0)
  977. if __name__ == '__main__':
  978. main()