runtests.py 39 KB

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