runtests.py 39 KB

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