1
0

__init__.py 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332
  1. # -*- coding: utf-8 -*-
  2. '''
  3. Set up the Salt integration test suite
  4. '''
  5. # Import Python libs
  6. from __future__ import absolute_import, print_function
  7. import os
  8. import re
  9. import sys
  10. import copy
  11. import time
  12. import stat
  13. import errno
  14. import signal
  15. import shutil
  16. import pprint
  17. import atexit
  18. import socket
  19. import logging
  20. import tempfile
  21. import threading
  22. import subprocess
  23. import multiprocessing
  24. from datetime import datetime, timedelta
  25. try:
  26. import pwd
  27. except ImportError:
  28. pass
  29. # Import salt tests support dirs
  30. from tests.support.paths import * # pylint: disable=wildcard-import
  31. from tests.support.processes import * # pylint: disable=wildcard-import
  32. from tests.support.unit import TestCase
  33. from tests.support.case import ShellTestCase
  34. from tests.support.parser import PNUM, print_header, SaltTestcaseParser
  35. from tests.support.helpers import requires_sshd_server, RedirectStdStreams
  36. from tests.support.paths import ScriptPathMixin
  37. from tests.support.mixins import CheckShellBinaryNameAndVersionMixin, ShellCaseCommonTestsMixin
  38. from tests.support.mixins import AdaptedConfigurationTestCaseMixin, SaltClientTestCaseMixin
  39. from tests.support.mixins import SaltMinionEventAssertsMixin, SaltReturnAssertsMixin
  40. from tests.support.runtests import RUNTIME_VARS
  41. # Import Salt libs
  42. import salt
  43. import salt.config
  44. import salt.minion
  45. import salt.runner
  46. import salt.output
  47. import salt.version
  48. import salt.utils.color
  49. import salt.utils.files
  50. import salt.utils.path
  51. import salt.utils.platform
  52. import salt.utils.process
  53. import salt.utils.stringutils
  54. import salt.utils.yaml
  55. import salt.log.setup as salt_log_setup
  56. from salt.utils.verify import verify_env
  57. from salt.utils.immutabletypes import freeze
  58. from salt.utils.nb_popen import NonBlockingPopen
  59. from salt.exceptions import SaltClientError
  60. try:
  61. import salt.master
  62. except ImportError:
  63. # Not required for raet tests
  64. pass
  65. # Import 3rd-party libs
  66. import msgpack
  67. from salt.ext import six
  68. from salt.ext.six.moves import cStringIO
  69. try:
  70. import salt.ext.six.moves.socketserver as socketserver
  71. except ImportError:
  72. import socketserver
  73. from tornado import gen
  74. from tornado import ioloop
  75. # Import salt tests support libs
  76. from tests.support.processes import SaltMaster, SaltMinion, SaltSyndic
  77. log = logging.getLogger(__name__)
  78. _RUNTESTS_PORTS = {}
  79. def get_unused_localhost_port():
  80. '''
  81. Return a random unused port on localhost
  82. '''
  83. usock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
  84. usock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  85. usock.bind(('127.0.0.1', 0))
  86. port = usock.getsockname()[1]
  87. if port in (54505, 54506, 64505, 64506, 64510, 64511, 64520, 64521):
  88. # These ports are hardcoded in the test configuration
  89. port = get_unused_localhost_port()
  90. usock.close()
  91. return port
  92. DARWIN = True if sys.platform.startswith('darwin') else False
  93. BSD = True if 'bsd' in sys.platform else False
  94. AIX = True if sys.platform.startswith('aix') else False
  95. if (AIX or DARWIN) and port in _RUNTESTS_PORTS:
  96. port = get_unused_localhost_port()
  97. usock.close()
  98. return port
  99. _RUNTESTS_PORTS[port] = usock
  100. if DARWIN or BSD or AIX:
  101. usock.close()
  102. return port
  103. def close_open_sockets(sockets_dict):
  104. for port in list(sockets_dict):
  105. sock = sockets_dict.pop(port)
  106. sock.close()
  107. atexit.register(close_open_sockets, _RUNTESTS_PORTS)
  108. SALT_LOG_PORT = get_unused_localhost_port()
  109. class ThreadingMixIn(socketserver.ThreadingMixIn):
  110. daemon_threads = True
  111. class ThreadedSocketServer(ThreadingMixIn, socketserver.TCPServer):
  112. allow_reuse_address = True
  113. def server_activate(self):
  114. self.shutting_down = threading.Event()
  115. socketserver.TCPServer.server_activate(self)
  116. #super(ThreadedSocketServer, self).server_activate()
  117. def server_close(self):
  118. if hasattr(self, 'shutting_down'):
  119. self.shutting_down.set()
  120. socketserver.TCPServer.server_close(self)
  121. #super(ThreadedSocketServer, self).server_close()
  122. class SocketServerRequestHandler(socketserver.StreamRequestHandler):
  123. def handle(self):
  124. unpacker = msgpack.Unpacker(encoding='utf-8')
  125. while not self.server.shutting_down.is_set():
  126. try:
  127. wire_bytes = self.request.recv(1024)
  128. if not wire_bytes:
  129. break
  130. unpacker.feed(wire_bytes)
  131. for record_dict in unpacker:
  132. record = logging.makeLogRecord(record_dict)
  133. logger = logging.getLogger(record.name)
  134. logger.handle(record)
  135. del record_dict
  136. except (EOFError, KeyboardInterrupt, SystemExit):
  137. break
  138. except socket.error as exc:
  139. try:
  140. if exc.errno == errno.WSAECONNRESET:
  141. # Connection reset on windows
  142. break
  143. except AttributeError:
  144. # We're not on windows
  145. pass
  146. log.exception(exc)
  147. except Exception as exc:
  148. log.exception(exc)
  149. class TestDaemon(object):
  150. '''
  151. Set up the master and minion daemons, and run related cases
  152. '''
  153. MINIONS_CONNECT_TIMEOUT = MINIONS_SYNC_TIMEOUT = 500
  154. def __init__(self, parser):
  155. self.parser = parser
  156. self.colors = salt.utils.color.get_colors(self.parser.options.no_colors is False)
  157. if salt.utils.platform.is_windows():
  158. # There's no shell color support on windows...
  159. for key in self.colors:
  160. self.colors[key] = ''
  161. def __enter__(self):
  162. '''
  163. Start a master and minion
  164. '''
  165. # Setup the multiprocessing logging queue listener
  166. salt_log_setup.setup_multiprocessing_logging_listener(
  167. self.master_opts
  168. )
  169. # Set up PATH to mockbin
  170. self._enter_mockbin()
  171. self.minion_targets = set(['minion', 'sub_minion'])
  172. if self.parser.options.transport == 'zeromq':
  173. self.start_zeromq_daemons()
  174. elif self.parser.options.transport == 'raet':
  175. self.start_raet_daemons()
  176. elif self.parser.options.transport == 'tcp':
  177. self.start_tcp_daemons()
  178. self.pre_setup_minions()
  179. self.setup_minions()
  180. if getattr(self.parser.options, 'ssh', False):
  181. self.prep_ssh()
  182. self.wait_for_minions(time.time(), self.MINIONS_CONNECT_TIMEOUT)
  183. if self.parser.options.sysinfo:
  184. try:
  185. print_header(
  186. '~~~~~~~ Versions Report ', inline=True,
  187. width=getattr(self.parser.options, 'output_columns', PNUM)
  188. )
  189. except TypeError:
  190. print_header('~~~~~~~ Versions Report ', inline=True)
  191. print('\n'.join(salt.version.versions_report()))
  192. try:
  193. print_header(
  194. '~~~~~~~ Minion Grains Information ', inline=True,
  195. width=getattr(self.parser.options, 'output_columns', PNUM)
  196. )
  197. except TypeError:
  198. print_header('~~~~~~~ Minion Grains Information ', inline=True)
  199. grains = self.client.cmd('minion', 'grains.items')
  200. minion_opts = self.minion_opts.copy()
  201. minion_opts['color'] = self.parser.options.no_colors is False
  202. salt.output.display_output(grains, 'grains', minion_opts)
  203. try:
  204. print_header(
  205. '=', sep='=', inline=True,
  206. width=getattr(self.parser.options, 'output_columns', PNUM)
  207. )
  208. except TypeError:
  209. print_header('', sep='=', inline=True)
  210. try:
  211. return self
  212. finally:
  213. self.post_setup_minions()
  214. def start_daemon(self, cls, opts, start_fun):
  215. def start(cls, opts, start_fun):
  216. salt.utils.process.appendproctitle('{0}-{1}'.format(self.__class__.__name__, cls.__name__))
  217. daemon = cls(opts)
  218. getattr(daemon, start_fun)()
  219. process = multiprocessing.Process(target=start,
  220. args=(cls, opts, start_fun))
  221. process.start()
  222. return process
  223. def start_zeromq_daemons(self):
  224. '''
  225. Fire up the daemons used for zeromq tests
  226. '''
  227. self.log_server = ThreadedSocketServer(('localhost', SALT_LOG_PORT), SocketServerRequestHandler)
  228. self.log_server_process = threading.Thread(target=self.log_server.serve_forever)
  229. self.log_server_process.daemon = True
  230. self.log_server_process.start()
  231. try:
  232. sys.stdout.write(
  233. ' * {LIGHT_YELLOW}Starting salt-master ... {ENDC}'.format(**self.colors)
  234. )
  235. sys.stdout.flush()
  236. self.master_process = start_daemon(
  237. daemon_name='salt-master',
  238. daemon_id=self.master_opts['id'],
  239. daemon_log_prefix='salt-master/{}'.format(self.master_opts['id']),
  240. daemon_cli_script_name='master',
  241. daemon_config=self.master_opts,
  242. daemon_config_dir=RUNTIME_VARS.TMP_CONF_DIR,
  243. daemon_class=SaltMaster,
  244. bin_dir_path=SCRIPT_DIR,
  245. fail_hard=True,
  246. start_timeout=60)
  247. sys.stdout.write(
  248. '\r{0}\r'.format(
  249. ' ' * getattr(self.parser.options, 'output_columns', PNUM)
  250. )
  251. )
  252. sys.stdout.write(
  253. ' * {LIGHT_GREEN}Starting salt-master ... STARTED!\n{ENDC}'.format(**self.colors)
  254. )
  255. sys.stdout.flush()
  256. except (RuntimeWarning, RuntimeError):
  257. sys.stdout.write(
  258. '\r{0}\r'.format(
  259. ' ' * getattr(self.parser.options, 'output_columns', PNUM)
  260. )
  261. )
  262. sys.stdout.write(
  263. ' * {LIGHT_RED}Starting salt-master ... FAILED!\n{ENDC}'.format(**self.colors)
  264. )
  265. sys.stdout.flush()
  266. try:
  267. sys.stdout.write(
  268. ' * {LIGHT_YELLOW}Starting salt-minion ... {ENDC}'.format(**self.colors)
  269. )
  270. sys.stdout.flush()
  271. self.minion_process = start_daemon(
  272. daemon_name='salt-minion',
  273. daemon_id=self.master_opts['id'],
  274. daemon_log_prefix='salt-minion/{}'.format(self.minion_opts['id']),
  275. daemon_cli_script_name='minion',
  276. daemon_config=self.minion_opts,
  277. daemon_config_dir=RUNTIME_VARS.TMP_CONF_DIR,
  278. daemon_class=SaltMinion,
  279. bin_dir_path=SCRIPT_DIR,
  280. fail_hard=True,
  281. start_timeout=60)
  282. sys.stdout.write(
  283. '\r{0}\r'.format(
  284. ' ' * getattr(self.parser.options, 'output_columns', PNUM)
  285. )
  286. )
  287. sys.stdout.write(
  288. ' * {LIGHT_GREEN}Starting salt-minion ... STARTED!\n{ENDC}'.format(**self.colors)
  289. )
  290. sys.stdout.flush()
  291. except (RuntimeWarning, RuntimeError):
  292. sys.stdout.write(
  293. '\r{0}\r'.format(
  294. ' ' * getattr(self.parser.options, 'output_columns', PNUM)
  295. )
  296. )
  297. sys.stdout.write(
  298. ' * {LIGHT_RED}Starting salt-minion ... FAILED!\n{ENDC}'.format(**self.colors)
  299. )
  300. sys.stdout.flush()
  301. try:
  302. sys.stdout.write(
  303. ' * {LIGHT_YELLOW}Starting sub salt-minion ... {ENDC}'.format(**self.colors)
  304. )
  305. sys.stdout.flush()
  306. self.sub_minion_process = start_daemon(
  307. daemon_name='sub salt-minion',
  308. daemon_id=self.master_opts['id'],
  309. daemon_log_prefix='sub-salt-minion/{}'.format(self.sub_minion_opts['id']),
  310. daemon_cli_script_name='minion',
  311. daemon_config=self.sub_minion_opts,
  312. daemon_config_dir=RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR,
  313. daemon_class=SaltMinion,
  314. bin_dir_path=SCRIPT_DIR,
  315. fail_hard=True,
  316. start_timeout=60)
  317. sys.stdout.write(
  318. '\r{0}\r'.format(
  319. ' ' * getattr(self.parser.options, 'output_columns', PNUM)
  320. )
  321. )
  322. sys.stdout.write(
  323. ' * {LIGHT_GREEN}Starting sub salt-minion ... STARTED!\n{ENDC}'.format(**self.colors)
  324. )
  325. sys.stdout.flush()
  326. except (RuntimeWarning, RuntimeError):
  327. sys.stdout.write(
  328. '\r{0}\r'.format(
  329. ' ' * getattr(self.parser.options, 'output_columns', PNUM)
  330. )
  331. )
  332. sys.stdout.write(
  333. ' * {LIGHT_RED}Starting sub salt-minion ... FAILED!\n{ENDC}'.format(**self.colors)
  334. )
  335. sys.stdout.flush()
  336. try:
  337. sys.stdout.write(
  338. ' * {LIGHT_YELLOW}Starting syndic salt-master ... {ENDC}'.format(**self.colors)
  339. )
  340. sys.stdout.flush()
  341. self.prep_syndic()
  342. self.smaster_process = start_daemon(
  343. daemon_name='salt-smaster',
  344. daemon_id=self.syndic_master_opts['id'],
  345. daemon_log_prefix='salt-smaster/{}'.format(self.syndic_master_opts['id']),
  346. daemon_cli_script_name='master',
  347. daemon_config=self.syndic_master_opts,
  348. daemon_config_dir=RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR,
  349. daemon_class=SaltMaster,
  350. bin_dir_path=SCRIPT_DIR,
  351. fail_hard=True,
  352. start_timeout=60)
  353. sys.stdout.write(
  354. '\r{0}\r'.format(
  355. ' ' * getattr(self.parser.options, 'output_columns', PNUM)
  356. )
  357. )
  358. sys.stdout.write(
  359. ' * {LIGHT_GREEN}Starting syndic salt-master ... STARTED!\n{ENDC}'.format(**self.colors)
  360. )
  361. sys.stdout.flush()
  362. except (RuntimeWarning, RuntimeError):
  363. sys.stdout.write(
  364. '\r{0}\r'.format(
  365. ' ' * getattr(self.parser.options, 'output_columns', PNUM)
  366. )
  367. )
  368. sys.stdout.write(
  369. ' * {LIGHT_RED}Starting syndic salt-master ... FAILED!\n{ENDC}'.format(**self.colors)
  370. )
  371. sys.stdout.flush()
  372. try:
  373. sys.stdout.write(
  374. ' * {LIGHT_YELLOW}Starting salt-syndic ... {ENDC}'.format(**self.colors)
  375. )
  376. sys.stdout.flush()
  377. self.syndic_process = start_daemon(
  378. daemon_name='salt-syndic',
  379. daemon_id=self.syndic_opts['id'],
  380. daemon_log_prefix='salt-syndic/{}'.format(self.syndic_opts['id']),
  381. daemon_cli_script_name='syndic',
  382. daemon_config=self.syndic_opts,
  383. daemon_config_dir=RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR,
  384. daemon_class=SaltSyndic,
  385. bin_dir_path=SCRIPT_DIR,
  386. fail_hard=True,
  387. start_timeout=60)
  388. sys.stdout.write(
  389. '\r{0}\r'.format(
  390. ' ' * getattr(self.parser.options, 'output_columns', PNUM)
  391. )
  392. )
  393. sys.stdout.write(
  394. ' * {LIGHT_GREEN}Starting salt-syndic ... STARTED!\n{ENDC}'.format(**self.colors)
  395. )
  396. sys.stdout.flush()
  397. except (RuntimeWarning, RuntimeError):
  398. sys.stdout.write(
  399. '\r{0}\r'.format(
  400. ' ' * getattr(self.parser.options, 'output_columns', PNUM)
  401. )
  402. )
  403. sys.stdout.write(
  404. ' * {LIGHT_RED}Starting salt-syndic ... FAILED!\n{ENDC}'.format(**self.colors)
  405. )
  406. sys.stdout.flush()
  407. if self.parser.options.proxy:
  408. self.minion_targets.add(self.proxy_opts['id'])
  409. try:
  410. sys.stdout.write(
  411. ' * {LIGHT_YELLOW}Starting salt-proxy ... {ENDC}'.format(**self.colors)
  412. )
  413. sys.stdout.flush()
  414. self.proxy_process = start_daemon(
  415. daemon_name='salt-proxy',
  416. daemon_id=self.proxy_opts['id'],
  417. daemon_log_prefix='salt-proxy/{}'.format(self.proxy_opts['id']),
  418. daemon_cli_script_name='proxy',
  419. daemon_config=self.proxy_opts,
  420. daemon_config_dir=RUNTIME_VARS.TMP_CONF_DIR,
  421. daemon_class=SaltProxy,
  422. bin_dir_path=SCRIPT_DIR,
  423. fail_hard=True,
  424. start_timeout=60)
  425. sys.stdout.write(
  426. '\r{0}\r'.format(
  427. ' ' * getattr(self.parser.options, 'output_columns', PNUM)
  428. )
  429. )
  430. sys.stdout.write(
  431. ' * {LIGHT_GREEN}Starting salt-proxy ... STARTED!\n{ENDC}'.format(**self.colors)
  432. )
  433. sys.stdout.flush()
  434. except (RuntimeWarning, RuntimeError):
  435. sys.stdout.write(
  436. '\r{0}\r'.format(
  437. ' ' * getattr(self.parser.options, 'output_columns', PNUM)
  438. )
  439. )
  440. sys.stdout.write(
  441. ' * {LIGHT_RED}Starting salt-proxy ... FAILED!\n{ENDC}'.format(**self.colors)
  442. )
  443. sys.stdout.flush()
  444. def start_raet_daemons(self):
  445. '''
  446. Fire up the raet daemons!
  447. '''
  448. import salt.daemons.flo
  449. self.master_process = self.start_daemon(salt.daemons.flo.IofloMaster,
  450. self.master_opts,
  451. 'start')
  452. self.minion_process = self.start_daemon(salt.daemons.flo.IofloMinion,
  453. self.minion_opts,
  454. 'tune_in')
  455. self.sub_minion_process = self.start_daemon(salt.daemons.flo.IofloMinion,
  456. self.sub_minion_opts,
  457. 'tune_in')
  458. # Wait for the daemons to all spin up
  459. time.sleep(5)
  460. # self.smaster_process = self.start_daemon(salt.daemons.flo.IofloMaster,
  461. # self.syndic_master_opts,
  462. # 'start')
  463. # no raet syndic daemon yet
  464. start_tcp_daemons = start_zeromq_daemons
  465. def prep_syndic(self):
  466. '''
  467. Create a roster file for salt's syndic
  468. '''
  469. roster_path = os.path.join(FILES, 'conf/_ssh/roster')
  470. shutil.copy(roster_path, RUNTIME_VARS.TMP_CONF_DIR)
  471. shutil.copy(roster_path, RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR)
  472. def prep_ssh(self):
  473. '''
  474. Generate keys and start an ssh daemon on an alternate port
  475. '''
  476. sys.stdout.write(
  477. ' * {LIGHT_GREEN}Starting {0} ... {ENDC}'.format(
  478. 'SSH server',
  479. **self.colors
  480. )
  481. )
  482. keygen = salt.utils.path.which('ssh-keygen')
  483. sshd = salt.utils.path.which('sshd')
  484. if not (keygen and sshd):
  485. print('WARNING: Could not initialize SSH subsystem. Tests for salt-ssh may break!')
  486. return
  487. if not os.path.exists(RUNTIME_VARS.TMP_CONF_DIR):
  488. os.makedirs(RUNTIME_VARS.TMP_CONF_DIR)
  489. # Generate client key
  490. pub_key_test_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'key_test.pub')
  491. priv_key_test_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'key_test')
  492. if os.path.exists(pub_key_test_file):
  493. os.remove(pub_key_test_file)
  494. if os.path.exists(priv_key_test_file):
  495. os.remove(priv_key_test_file)
  496. keygen_process = subprocess.Popen(
  497. [keygen, '-t',
  498. 'ecdsa',
  499. '-b',
  500. '521',
  501. '-C',
  502. '"$(whoami)@$(hostname)-$(date -I)"',
  503. '-f',
  504. 'key_test',
  505. '-P',
  506. ''],
  507. stdout=subprocess.PIPE,
  508. stderr=subprocess.PIPE,
  509. close_fds=True,
  510. cwd=RUNTIME_VARS.TMP_CONF_DIR
  511. )
  512. _, keygen_err = keygen_process.communicate()
  513. if keygen_err:
  514. print('ssh-keygen had errors: {0}'.format(salt.utils.stringutils.to_str(keygen_err)))
  515. sshd_config_path = os.path.join(FILES, 'conf/_ssh/sshd_config')
  516. shutil.copy(sshd_config_path, RUNTIME_VARS.TMP_CONF_DIR)
  517. auth_key_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'key_test.pub')
  518. # Generate server key
  519. server_key_dir = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'server')
  520. if not os.path.exists(server_key_dir):
  521. os.makedirs(server_key_dir)
  522. server_dsa_priv_key_file = os.path.join(server_key_dir, 'ssh_host_dsa_key')
  523. server_dsa_pub_key_file = os.path.join(server_key_dir, 'ssh_host_dsa_key.pub')
  524. server_ecdsa_priv_key_file = os.path.join(server_key_dir, 'ssh_host_ecdsa_key')
  525. server_ecdsa_pub_key_file = os.path.join(server_key_dir, 'ssh_host_ecdsa_key.pub')
  526. server_ed25519_priv_key_file = os.path.join(server_key_dir, 'ssh_host_ed25519_key')
  527. server_ed25519_pub_key_file = os.path.join(server_key_dir, 'ssh_host.ed25519_key.pub')
  528. for server_key_file in (server_dsa_priv_key_file,
  529. server_dsa_pub_key_file,
  530. server_ecdsa_priv_key_file,
  531. server_ecdsa_pub_key_file,
  532. server_ed25519_priv_key_file,
  533. server_ed25519_pub_key_file):
  534. if os.path.exists(server_key_file):
  535. os.remove(server_key_file)
  536. keygen_process_dsa = subprocess.Popen(
  537. [keygen, '-t',
  538. 'dsa',
  539. '-b',
  540. '1024',
  541. '-C',
  542. '"$(whoami)@$(hostname)-$(date -I)"',
  543. '-f',
  544. 'ssh_host_dsa_key',
  545. '-P',
  546. ''],
  547. stdout=subprocess.PIPE,
  548. stderr=subprocess.PIPE,
  549. close_fds=True,
  550. cwd=server_key_dir
  551. )
  552. _, keygen_dsa_err = keygen_process_dsa.communicate()
  553. if keygen_dsa_err:
  554. print('ssh-keygen had errors: {0}'.format(salt.utils.stringutils.to_str(keygen_dsa_err)))
  555. keygen_process_ecdsa = subprocess.Popen(
  556. [keygen, '-t',
  557. 'ecdsa',
  558. '-b',
  559. '521',
  560. '-C',
  561. '"$(whoami)@$(hostname)-$(date -I)"',
  562. '-f',
  563. 'ssh_host_ecdsa_key',
  564. '-P',
  565. ''],
  566. stdout=subprocess.PIPE,
  567. stderr=subprocess.PIPE,
  568. close_fds=True,
  569. cwd=server_key_dir
  570. )
  571. _, keygen_escda_err = keygen_process_ecdsa.communicate()
  572. if keygen_escda_err:
  573. print('ssh-keygen had errors: {0}'.format(salt.utils.stringutils.to_str(keygen_escda_err)))
  574. keygen_process_ed25519 = subprocess.Popen(
  575. [keygen, '-t',
  576. 'ed25519',
  577. '-b',
  578. '521',
  579. '-C',
  580. '"$(whoami)@$(hostname)-$(date -I)"',
  581. '-f',
  582. 'ssh_host_ed25519_key',
  583. '-P',
  584. ''],
  585. stdout=subprocess.PIPE,
  586. stderr=subprocess.PIPE,
  587. close_fds=True,
  588. cwd=server_key_dir
  589. )
  590. _, keygen_ed25519_err = keygen_process_ed25519.communicate()
  591. if keygen_ed25519_err:
  592. print('ssh-keygen had errors: {0}'.format(salt.utils.stringutils.to_str(keygen_ed25519_err)))
  593. with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'sshd_config'), 'a') as ssh_config:
  594. ssh_config.write('AuthorizedKeysFile {0}\n'.format(auth_key_file))
  595. if not keygen_dsa_err:
  596. ssh_config.write('HostKey {0}\n'.format(server_dsa_priv_key_file))
  597. if not keygen_escda_err:
  598. ssh_config.write('HostKey {0}\n'.format(server_ecdsa_priv_key_file))
  599. if not keygen_ed25519_err:
  600. ssh_config.write('HostKey {0}\n'.format(server_ed25519_priv_key_file))
  601. self.sshd_pidfile = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'sshd.pid')
  602. self.sshd_process = subprocess.Popen(
  603. [sshd, '-f', 'sshd_config', '-oPidFile={0}'.format(self.sshd_pidfile)],
  604. stdout=subprocess.PIPE,
  605. stderr=subprocess.PIPE,
  606. close_fds=True,
  607. cwd=RUNTIME_VARS.TMP_CONF_DIR
  608. )
  609. _, sshd_err = self.sshd_process.communicate()
  610. if sshd_err:
  611. print('sshd had errors on startup: {0}'.format(salt.utils.stringutils.to_str(sshd_err)))
  612. else:
  613. os.environ['SSH_DAEMON_RUNNING'] = 'True'
  614. roster_path = os.path.join(FILES, 'conf/_ssh/roster')
  615. syndic_roster_path = os.path.join(FILES, 'conf/_ssh/syndic_roster')
  616. shutil.copy(roster_path, RUNTIME_VARS.TMP_CONF_DIR)
  617. shutil.copy(syndic_roster_path, os.path.join(RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR, 'roster'))
  618. with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'roster'), 'a') as roster:
  619. roster.write(' user: {0}\n'.format(RUNTIME_VARS.RUNNING_TESTS_USER))
  620. roster.write(' priv: {0}/{1}'.format(RUNTIME_VARS.TMP_CONF_DIR, 'key_test'))
  621. self.prep_syndic()
  622. with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR, 'roster'), 'a') as roster:
  623. roster.write(' user: {0}\n'.format(RUNTIME_VARS.RUNNING_TESTS_USER))
  624. roster.write(' priv: {0}/{1}'.format(RUNTIME_VARS.TMP_CONF_DIR, 'key_test'))
  625. sys.stdout.write(
  626. ' {LIGHT_GREEN}STARTED!\n{ENDC}'.format(
  627. **self.colors
  628. )
  629. )
  630. @classmethod
  631. def config(cls, role):
  632. '''
  633. Return a configuration for a master/minion/syndic.
  634. Currently these roles are:
  635. * master
  636. * minion
  637. * syndic
  638. * syndic_master
  639. * sub_minion
  640. * proxy
  641. '''
  642. return RUNTIME_VARS.RUNTIME_CONFIGS[role]
  643. @classmethod
  644. def config_location(cls):
  645. return RUNTIME_VARS.TMP_CONF_DIR
  646. @property
  647. def client(self):
  648. '''
  649. Return a local client which will be used for example to ping and sync
  650. the test minions.
  651. This client is defined as a class attribute because its creation needs
  652. to be deferred to a latter stage. If created it on `__enter__` like it
  653. previously was, it would not receive the master events.
  654. '''
  655. if 'runtime_client' not in RUNTIME_VARS.RUNTIME_CONFIGS:
  656. RUNTIME_VARS.RUNTIME_CONFIGS['runtime_client'] = salt.client.get_local_client(
  657. mopts=self.master_opts
  658. )
  659. return RUNTIME_VARS.RUNTIME_CONFIGS['runtime_client']
  660. @classmethod
  661. def transplant_configs(cls, transport='zeromq'):
  662. if os.path.isdir(RUNTIME_VARS.TMP_CONF_DIR):
  663. shutil.rmtree(RUNTIME_VARS.TMP_CONF_DIR)
  664. os.makedirs(RUNTIME_VARS.TMP_CONF_DIR)
  665. os.makedirs(RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR)
  666. os.makedirs(RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR)
  667. os.makedirs(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR)
  668. print(' * Transplanting configuration files to \'{0}\''.format(RUNTIME_VARS.TMP_CONF_DIR))
  669. tests_known_hosts_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'salt_ssh_known_hosts')
  670. with salt.utils.files.fopen(tests_known_hosts_file, 'w') as known_hosts:
  671. known_hosts.write('')
  672. # This master connects to syndic_master via a syndic
  673. master_opts = salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'master'))
  674. master_opts['known_hosts_file'] = tests_known_hosts_file
  675. master_opts['cachedir'] = os.path.join(TMP, 'rootdir', 'cache')
  676. master_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER
  677. master_opts['config_dir'] = RUNTIME_VARS.TMP_CONF_DIR
  678. master_opts['root_dir'] = os.path.join(TMP, 'rootdir')
  679. master_opts['pki_dir'] = os.path.join(TMP, 'rootdir', 'pki', 'master')
  680. master_opts['syndic_master'] = 'localhost'
  681. file_tree = {
  682. 'root_dir': os.path.join(FILES, 'pillar', 'base', 'file_tree'),
  683. 'follow_dir_links': False,
  684. 'keep_newline': True,
  685. }
  686. master_opts['ext_pillar'].append({'file_tree': file_tree})
  687. # This is the syndic for master
  688. # Let's start with a copy of the syndic master configuration
  689. syndic_opts = copy.deepcopy(master_opts)
  690. # Let's update with the syndic configuration
  691. syndic_opts.update(salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'syndic')))
  692. syndic_opts['cachedir'] = os.path.join(TMP, 'rootdir', 'cache')
  693. syndic_opts['config_dir'] = RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR
  694. # Under windows we can't seem to properly create a virtualenv off of another
  695. # virtualenv, we can on linux but we will still point to the virtualenv binary
  696. # outside the virtualenv running the test suite, if that's the case.
  697. try:
  698. real_prefix = sys.real_prefix
  699. # The above attribute exists, this is a virtualenv
  700. if salt.utils.is_windows():
  701. virtualenv_binary = os.path.join(real_prefix, 'Scripts', 'virtualenv.exe')
  702. else:
  703. # We need to remove the virtualenv from PATH or we'll get the virtualenv binary
  704. # from within the virtualenv, we don't want that
  705. path = os.environ.get('PATH')
  706. if path is not None:
  707. path_items = path.split(os.pathsep)
  708. for item in path_items[:]:
  709. if item.startswith(sys.base_prefix):
  710. path_items.remove(item)
  711. os.environ['PATH'] = os.pathsep.join(path_items)
  712. virtualenv_binary = salt.utils.which('virtualenv')
  713. if path is not None:
  714. # Restore previous environ PATH
  715. os.environ['PATH'] = path
  716. if not virtualenv_binary.startswith(real_prefix):
  717. virtualenv_binary = None
  718. if virtualenv_binary and not os.path.exists(virtualenv_binary):
  719. # It doesn't exist?!
  720. virtualenv_binary = None
  721. except AttributeError:
  722. # We're not running inside a virtualenv
  723. virtualenv_binary = None
  724. # This minion connects to master
  725. minion_opts = salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'minion'))
  726. minion_opts['cachedir'] = os.path.join(TMP, 'rootdir', 'cache')
  727. minion_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER
  728. minion_opts['config_dir'] = RUNTIME_VARS.TMP_CONF_DIR
  729. minion_opts['root_dir'] = os.path.join(TMP, 'rootdir')
  730. minion_opts['pki_dir'] = os.path.join(TMP, 'rootdir', 'pki')
  731. minion_opts['hosts.file'] = os.path.join(TMP, 'rootdir', 'hosts')
  732. minion_opts['aliases.file'] = os.path.join(TMP, 'rootdir', 'aliases')
  733. if virtualenv_binary:
  734. minion_opts['venv_bin'] = virtualenv_binary
  735. # This sub_minion also connects to master
  736. sub_minion_opts = salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'sub_minion'))
  737. sub_minion_opts['cachedir'] = os.path.join(TMP, 'rootdir-sub-minion', 'cache')
  738. sub_minion_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER
  739. sub_minion_opts['config_dir'] = RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR
  740. sub_minion_opts['root_dir'] = os.path.join(TMP, 'rootdir-sub-minion')
  741. sub_minion_opts['pki_dir'] = os.path.join(TMP, 'rootdir-sub-minion', 'pki', 'minion')
  742. sub_minion_opts['hosts.file'] = os.path.join(TMP, 'rootdir', 'hosts')
  743. sub_minion_opts['aliases.file'] = os.path.join(TMP, 'rootdir', 'aliases')
  744. if virtualenv_binary:
  745. sub_minion_opts['venv_bin'] = virtualenv_binary
  746. # This is the master of masters
  747. syndic_master_opts = salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'syndic_master'))
  748. syndic_master_opts['cachedir'] = os.path.join(TMP, 'rootdir-syndic-master', 'cache')
  749. syndic_master_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER
  750. syndic_master_opts['config_dir'] = RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR
  751. syndic_master_opts['root_dir'] = os.path.join(TMP, 'rootdir-syndic-master')
  752. syndic_master_opts['pki_dir'] = os.path.join(TMP, 'rootdir-syndic-master', 'pki', 'master')
  753. # This proxy connects to master
  754. proxy_opts = salt.config._read_conf_file(os.path.join(CONF_DIR, 'proxy'))
  755. proxy_opts['cachedir'] = os.path.join(TMP, 'rootdir-proxy', 'cache')
  756. # proxy_opts['user'] = running_tests_user
  757. proxy_opts['config_dir'] = RUNTIME_VARS.TMP_CONF_DIR
  758. proxy_opts['root_dir'] = os.path.join(TMP, 'rootdir-proxy')
  759. proxy_opts['pki_dir'] = os.path.join(TMP, 'rootdir-proxy', 'pki')
  760. proxy_opts['hosts.file'] = os.path.join(TMP, 'rootdir-proxy', 'hosts')
  761. proxy_opts['aliases.file'] = os.path.join(TMP, 'rootdir-proxy', 'aliases')
  762. if transport == 'raet':
  763. master_opts['transport'] = 'raet'
  764. master_opts['raet_port'] = 64506
  765. minion_opts['transport'] = 'raet'
  766. minion_opts['raet_port'] = 64510
  767. sub_minion_opts['transport'] = 'raet'
  768. sub_minion_opts['raet_port'] = 64520
  769. # syndic_master_opts['transport'] = 'raet'
  770. if transport == 'tcp':
  771. master_opts['transport'] = 'tcp'
  772. minion_opts['transport'] = 'tcp'
  773. sub_minion_opts['transport'] = 'tcp'
  774. syndic_master_opts['transport'] = 'tcp'
  775. proxy_opts['transport'] = 'tcp'
  776. # Set up config options that require internal data
  777. master_opts['pillar_roots'] = syndic_master_opts['pillar_roots'] = {
  778. 'base': [
  779. RUNTIME_VARS.TMP_PILLAR_TREE,
  780. os.path.join(FILES, 'pillar', 'base'),
  781. ]
  782. }
  783. minion_opts['pillar_roots'] = {
  784. 'base': [
  785. RUNTIME_VARS.TMP_PILLAR_TREE,
  786. os.path.join(FILES, 'pillar', 'base'),
  787. ]
  788. }
  789. master_opts['file_roots'] = syndic_master_opts['file_roots'] = {
  790. 'base': [
  791. os.path.join(FILES, 'file', 'base'),
  792. # Let's support runtime created files that can be used like:
  793. # salt://my-temp-file.txt
  794. RUNTIME_VARS.TMP_STATE_TREE
  795. ],
  796. # Alternate root to test __env__ choices
  797. 'prod': [
  798. os.path.join(FILES, 'file', 'prod'),
  799. RUNTIME_VARS.TMP_PRODENV_STATE_TREE
  800. ]
  801. }
  802. minion_opts['file_roots'] = {
  803. 'base': [
  804. os.path.join(FILES, 'file', 'base'),
  805. # Let's support runtime created files that can be used like:
  806. # salt://my-temp-file.txt
  807. RUNTIME_VARS.TMP_STATE_TREE
  808. ],
  809. # Alternate root to test __env__ choices
  810. 'prod': [
  811. os.path.join(FILES, 'file', 'prod'),
  812. RUNTIME_VARS.TMP_PRODENV_STATE_TREE
  813. ]
  814. }
  815. master_opts.setdefault('reactor', []).append(
  816. {
  817. 'salt/minion/*/start': [
  818. os.path.join(FILES, 'reactor-sync-minion.sls')
  819. ],
  820. }
  821. )
  822. for opts_dict in (master_opts, syndic_master_opts):
  823. if 'ext_pillar' not in opts_dict:
  824. opts_dict['ext_pillar'] = []
  825. if salt.utils.platform.is_windows():
  826. opts_dict['ext_pillar'].append(
  827. {'cmd_yaml': 'type {0}'.format(os.path.join(FILES, 'ext.yaml'))})
  828. else:
  829. opts_dict['ext_pillar'].append(
  830. {'cmd_yaml': 'cat {0}'.format(os.path.join(FILES, 'ext.yaml'))})
  831. # all read, only owner write
  832. autosign_file_permissions = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR
  833. for opts_dict in (master_opts, syndic_master_opts):
  834. # We need to copy the extension modules into the new master root_dir or
  835. # it will be prefixed by it
  836. new_extension_modules_path = os.path.join(opts_dict['root_dir'], 'extension_modules')
  837. if not os.path.exists(new_extension_modules_path):
  838. shutil.copytree(
  839. os.path.join(
  840. INTEGRATION_TEST_DIR, 'files', 'extension_modules'
  841. ),
  842. new_extension_modules_path
  843. )
  844. opts_dict['extension_modules'] = os.path.join(opts_dict['root_dir'], 'extension_modules')
  845. # Copy the autosign_file to the new master root_dir
  846. new_autosign_file_path = os.path.join(opts_dict['root_dir'], 'autosign_file')
  847. shutil.copyfile(
  848. os.path.join(INTEGRATION_TEST_DIR, 'files', 'autosign_file'),
  849. new_autosign_file_path
  850. )
  851. os.chmod(new_autosign_file_path, autosign_file_permissions)
  852. # Point the config values to the correct temporary paths
  853. for name in ('hosts', 'aliases'):
  854. optname = '{0}.file'.format(name)
  855. optname_path = os.path.join(TMP, name)
  856. master_opts[optname] = optname_path
  857. minion_opts[optname] = optname_path
  858. sub_minion_opts[optname] = optname_path
  859. syndic_opts[optname] = optname_path
  860. syndic_master_opts[optname] = optname_path
  861. proxy_opts[optname] = optname_path
  862. master_opts['runtests_conn_check_port'] = get_unused_localhost_port()
  863. minion_opts['runtests_conn_check_port'] = get_unused_localhost_port()
  864. sub_minion_opts['runtests_conn_check_port'] = get_unused_localhost_port()
  865. syndic_opts['runtests_conn_check_port'] = get_unused_localhost_port()
  866. syndic_master_opts['runtests_conn_check_port'] = get_unused_localhost_port()
  867. proxy_opts['runtests_conn_check_port'] = get_unused_localhost_port()
  868. for conf in (master_opts, minion_opts, sub_minion_opts, syndic_opts, syndic_master_opts, proxy_opts):
  869. if 'engines' not in conf:
  870. conf['engines'] = []
  871. conf['engines'].append({'salt_runtests': {}})
  872. if 'engines_dirs' not in conf:
  873. conf['engines_dirs'] = []
  874. conf['engines_dirs'].insert(0, ENGINES_DIR)
  875. if 'log_handlers_dirs' not in conf:
  876. conf['log_handlers_dirs'] = []
  877. conf['log_handlers_dirs'].insert(0, LOG_HANDLERS_DIR)
  878. conf['runtests_log_port'] = SALT_LOG_PORT
  879. conf['runtests_log_level'] = os.environ.get('TESTS_MIN_LOG_LEVEL_NAME') or 'debug'
  880. # ----- Transcribe Configuration ---------------------------------------------------------------------------->
  881. for entry in os.listdir(RUNTIME_VARS.CONF_DIR):
  882. if entry in ('master', 'minion', 'sub_minion', 'syndic', 'syndic_master', 'proxy'):
  883. # These have runtime computed values and will be handled
  884. # differently
  885. continue
  886. entry_path = os.path.join(RUNTIME_VARS.CONF_DIR, entry)
  887. if os.path.isfile(entry_path):
  888. shutil.copy(
  889. entry_path,
  890. os.path.join(RUNTIME_VARS.TMP_CONF_DIR, entry)
  891. )
  892. elif os.path.isdir(entry_path):
  893. shutil.copytree(
  894. entry_path,
  895. os.path.join(RUNTIME_VARS.TMP_CONF_DIR, entry)
  896. )
  897. for entry in ('master', 'minion', 'sub_minion', 'syndic', 'syndic_master', 'proxy'):
  898. computed_config = copy.deepcopy(locals()['{0}_opts'.format(entry)])
  899. with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, entry), 'w') as fp_:
  900. salt.utils.yaml.safe_dump(computed_config, fp_, default_flow_style=False)
  901. sub_minion_computed_config = copy.deepcopy(sub_minion_opts)
  902. with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR, 'minion'), 'w') as wfh:
  903. salt.utils.yaml.safe_dump(sub_minion_computed_config, wfh, default_flow_style=False)
  904. shutil.copyfile(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'master'), os.path.join(RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR, 'master'))
  905. syndic_master_computed_config = copy.deepcopy(syndic_master_opts)
  906. with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR, 'master'), 'w') as wfh:
  907. salt.utils.yaml.safe_dump(syndic_master_computed_config, wfh, default_flow_style=False)
  908. syndic_computed_config = copy.deepcopy(syndic_opts)
  909. with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR, 'minion'), 'w') as wfh:
  910. salt.utils.yaml.safe_dump(syndic_computed_config, wfh, default_flow_style=False)
  911. shutil.copyfile(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'master'), os.path.join(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR, 'master'))
  912. # <---- Transcribe Configuration -----------------------------------------------------------------------------
  913. # ----- Verify Environment ---------------------------------------------------------------------------------->
  914. master_opts = salt.config.master_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'master'))
  915. minion_opts = salt.config.minion_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'minion'))
  916. syndic_opts = salt.config.syndic_config(
  917. os.path.join(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR, 'master'),
  918. os.path.join(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR, 'minion'),
  919. )
  920. sub_minion_opts = salt.config.minion_config(os.path.join(RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR, 'minion'))
  921. syndic_master_opts = salt.config.master_config(os.path.join(RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR, 'master'))
  922. proxy_opts = salt.config.proxy_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'proxy'))
  923. RUNTIME_VARS.RUNTIME_CONFIGS['master'] = freeze(master_opts)
  924. RUNTIME_VARS.RUNTIME_CONFIGS['minion'] = freeze(minion_opts)
  925. RUNTIME_VARS.RUNTIME_CONFIGS['syndic'] = freeze(syndic_opts)
  926. RUNTIME_VARS.RUNTIME_CONFIGS['sub_minion'] = freeze(sub_minion_opts)
  927. RUNTIME_VARS.RUNTIME_CONFIGS['syndic_master'] = freeze(syndic_master_opts)
  928. RUNTIME_VARS.RUNTIME_CONFIGS['proxy'] = freeze(proxy_opts)
  929. verify_env([os.path.join(master_opts['pki_dir'], 'minions'),
  930. os.path.join(master_opts['pki_dir'], 'minions_pre'),
  931. os.path.join(master_opts['pki_dir'], 'minions_rejected'),
  932. os.path.join(master_opts['pki_dir'], 'minions_denied'),
  933. os.path.join(master_opts['cachedir'], 'jobs'),
  934. os.path.join(master_opts['cachedir'], 'raet'),
  935. os.path.join(master_opts['root_dir'], 'cache', 'tokens'),
  936. os.path.join(syndic_master_opts['pki_dir'], 'minions'),
  937. os.path.join(syndic_master_opts['pki_dir'], 'minions_pre'),
  938. os.path.join(syndic_master_opts['pki_dir'], 'minions_rejected'),
  939. os.path.join(syndic_master_opts['cachedir'], 'jobs'),
  940. os.path.join(syndic_master_opts['cachedir'], 'raet'),
  941. os.path.join(syndic_master_opts['root_dir'], 'cache', 'tokens'),
  942. os.path.join(master_opts['pki_dir'], 'accepted'),
  943. os.path.join(master_opts['pki_dir'], 'rejected'),
  944. os.path.join(master_opts['pki_dir'], 'pending'),
  945. os.path.join(syndic_master_opts['pki_dir'], 'accepted'),
  946. os.path.join(syndic_master_opts['pki_dir'], 'rejected'),
  947. os.path.join(syndic_master_opts['pki_dir'], 'pending'),
  948. os.path.join(syndic_master_opts['cachedir'], 'raet'),
  949. os.path.join(minion_opts['pki_dir'], 'accepted'),
  950. os.path.join(minion_opts['pki_dir'], 'rejected'),
  951. os.path.join(minion_opts['pki_dir'], 'pending'),
  952. os.path.join(minion_opts['cachedir'], 'raet'),
  953. os.path.join(sub_minion_opts['pki_dir'], 'accepted'),
  954. os.path.join(sub_minion_opts['pki_dir'], 'rejected'),
  955. os.path.join(sub_minion_opts['pki_dir'], 'pending'),
  956. os.path.join(sub_minion_opts['cachedir'], 'raet'),
  957. os.path.dirname(master_opts['log_file']),
  958. minion_opts['extension_modules'],
  959. sub_minion_opts['extension_modules'],
  960. sub_minion_opts['pki_dir'],
  961. master_opts['sock_dir'],
  962. syndic_master_opts['sock_dir'],
  963. sub_minion_opts['sock_dir'],
  964. minion_opts['sock_dir'],
  965. RUNTIME_VARS.TMP_STATE_TREE,
  966. RUNTIME_VARS.TMP_PILLAR_TREE,
  967. RUNTIME_VARS.TMP_PRODENV_STATE_TREE,
  968. TMP,
  969. ],
  970. RUNTIME_VARS.RUNNING_TESTS_USER,
  971. root_dir=master_opts['root_dir'],
  972. )
  973. cls.master_opts = master_opts
  974. cls.minion_opts = minion_opts
  975. # cls.proxy_opts = proxy_opts
  976. cls.sub_minion_opts = sub_minion_opts
  977. cls.syndic_opts = syndic_opts
  978. cls.syndic_master_opts = syndic_master_opts
  979. cls.proxy_opts = proxy_opts
  980. # <---- Verify Environment -----------------------------------------------------------------------------------
  981. def __exit__(self, type, value, traceback):
  982. '''
  983. Kill the minion and master processes
  984. '''
  985. self.sub_minion_process.terminate()
  986. self.minion_process.terminate()
  987. if hasattr(self, 'proxy_process'):
  988. self.proxy_process.terminate()
  989. self.master_process.terminate()
  990. try:
  991. self.syndic_process.terminate()
  992. except AttributeError:
  993. pass
  994. try:
  995. self.smaster_process.terminate()
  996. except AttributeError:
  997. pass
  998. #salt.utils.process.clean_proc(self.sub_minion_process, wait_for_kill=50)
  999. #self.sub_minion_process.join()
  1000. #salt.utils.process.clean_proc(self.minion_process, wait_for_kill=50)
  1001. #self.minion_process.join()
  1002. #salt.utils.process.clean_proc(self.master_process, wait_for_kill=50)
  1003. #self.master_process.join()
  1004. #try:
  1005. # salt.utils.process.clean_proc(self.syndic_process, wait_for_kill=50)
  1006. # self.syndic_process.join()
  1007. #except AttributeError:
  1008. # pass
  1009. #try:
  1010. # salt.utils.process.clean_proc(self.smaster_process, wait_for_kill=50)
  1011. # self.smaster_process.join()
  1012. #except AttributeError:
  1013. # pass
  1014. self.log_server.server_close()
  1015. self.log_server.shutdown()
  1016. self._exit_mockbin()
  1017. self._exit_ssh()
  1018. self.log_server_process.join()
  1019. # Shutdown the multiprocessing logging queue listener
  1020. salt_log_setup.shutdown_multiprocessing_logging()
  1021. salt_log_setup.shutdown_multiprocessing_logging_listener(daemonizing=True)
  1022. def pre_setup_minions(self):
  1023. '''
  1024. Subclass this method for additional minion setups.
  1025. '''
  1026. def setup_minions(self):
  1027. '''
  1028. Minions setup routines
  1029. '''
  1030. def post_setup_minions(self):
  1031. '''
  1032. Subclass this method to execute code after the minions have been setup
  1033. '''
  1034. def _enter_mockbin(self):
  1035. path = os.environ.get('PATH', '')
  1036. path_items = path.split(os.pathsep)
  1037. if MOCKBIN not in path_items:
  1038. path_items.insert(0, MOCKBIN)
  1039. os.environ['PATH'] = os.pathsep.join(path_items)
  1040. def _exit_ssh(self):
  1041. if hasattr(self, 'sshd_process'):
  1042. try:
  1043. self.sshd_process.kill()
  1044. except OSError as exc:
  1045. if exc.errno != 3:
  1046. raise
  1047. with salt.utils.files.fopen(self.sshd_pidfile) as fhr:
  1048. try:
  1049. os.kill(int(fhr.read()), signal.SIGKILL)
  1050. except OSError as exc:
  1051. if exc.errno != 3:
  1052. raise
  1053. def _exit_mockbin(self):
  1054. path = os.environ.get('PATH', '')
  1055. path_items = path.split(os.pathsep)
  1056. try:
  1057. path_items.remove(MOCKBIN)
  1058. except ValueError:
  1059. pass
  1060. os.environ['PATH'] = os.pathsep.join(path_items)
  1061. @classmethod
  1062. def clean(cls):
  1063. '''
  1064. Clean out the tmp files
  1065. '''
  1066. def remove_readonly(func, path, excinfo):
  1067. if os.path.exists(path):
  1068. # Give full permissions to owner
  1069. os.chmod(path, stat.S_IRWXU)
  1070. func(path)
  1071. for dirname in (TMP, RUNTIME_VARS.TMP_STATE_TREE,
  1072. RUNTIME_VARS.TMP_PILLAR_TREE, RUNTIME_VARS.TMP_PRODENV_STATE_TREE):
  1073. if os.path.isdir(dirname):
  1074. try:
  1075. shutil.rmtree(six.text_type(dirname), onerror=remove_readonly)
  1076. except Exception:
  1077. log.exception('Failed to remove directory: %s', dirname)
  1078. def wait_for_jid(self, targets, jid, timeout=120):
  1079. time.sleep(1) # Allow some time for minions to accept jobs
  1080. now = datetime.now()
  1081. expire = now + timedelta(seconds=timeout)
  1082. job_finished = False
  1083. while now <= expire:
  1084. running = self.__client_job_running(targets, jid)
  1085. sys.stdout.write(
  1086. '\r{0}\r'.format(
  1087. ' ' * getattr(self.parser.options, 'output_columns', PNUM)
  1088. )
  1089. )
  1090. if not running and job_finished is False:
  1091. # Let's not have false positives and wait one more seconds
  1092. job_finished = True
  1093. elif not running and job_finished is True:
  1094. return True
  1095. elif running and job_finished is True:
  1096. job_finished = False
  1097. if job_finished is False:
  1098. sys.stdout.write(
  1099. ' * {LIGHT_YELLOW}[Quit in {0}]{ENDC} Waiting for {1}'.format(
  1100. '{0}'.format(expire - now).rsplit('.', 1)[0],
  1101. ', '.join(running),
  1102. **self.colors
  1103. )
  1104. )
  1105. sys.stdout.flush()
  1106. time.sleep(1)
  1107. now = datetime.now()
  1108. else: # pylint: disable=W0120
  1109. sys.stdout.write(
  1110. '\n {LIGHT_RED}*{ENDC} ERROR: Failed to get information '
  1111. 'back\n'.format(**self.colors)
  1112. )
  1113. sys.stdout.flush()
  1114. return False
  1115. def __client_job_running(self, targets, jid):
  1116. running = self.client.cmd(
  1117. list(targets), 'saltutil.running', tgt_type='list'
  1118. )
  1119. return [
  1120. k for (k, v) in six.iteritems(running) if v and v[0]['jid'] == jid
  1121. ]
  1122. def sync_minion_modules_(self, modules_kind, targets, timeout=None):
  1123. if not timeout:
  1124. timeout = 120
  1125. # Let's sync all connected minions
  1126. print(
  1127. ' {LIGHT_BLUE}*{ENDC} Syncing minion\'s {1} '
  1128. '(saltutil.sync_{1})'.format(
  1129. ', '.join(targets),
  1130. modules_kind,
  1131. **self.colors
  1132. )
  1133. )
  1134. syncing = set(targets)
  1135. jid_info = self.client.run_job(
  1136. list(targets), 'saltutil.sync_{0}'.format(modules_kind),
  1137. tgt_type='list',
  1138. timeout=999999999999999,
  1139. )
  1140. if self.wait_for_jid(targets, jid_info['jid'], timeout) is False:
  1141. print(
  1142. ' {LIGHT_RED}*{ENDC} WARNING: Minions failed to sync {0}. '
  1143. 'Tests requiring these {0} WILL fail'.format(
  1144. modules_kind, **self.colors)
  1145. )
  1146. raise SystemExit()
  1147. while syncing:
  1148. rdata = self.client.get_full_returns(jid_info['jid'], syncing, 1)
  1149. if rdata:
  1150. for name, output in six.iteritems(rdata):
  1151. if not output['ret']:
  1152. # Already synced!?
  1153. syncing.remove(name)
  1154. continue
  1155. if isinstance(output['ret'], six.string_types):
  1156. # An errors has occurred
  1157. print(
  1158. ' {LIGHT_RED}*{ENDC} {0} Failed to sync {2}: '
  1159. '{1}'.format(
  1160. name, output['ret'],
  1161. modules_kind,
  1162. **self.colors)
  1163. )
  1164. return False
  1165. print(
  1166. ' {LIGHT_GREEN}*{ENDC} Synced {0} {2}: '
  1167. '{1}'.format(
  1168. name,
  1169. ', '.join(output['ret']),
  1170. modules_kind, **self.colors
  1171. )
  1172. )
  1173. # Synced!
  1174. try:
  1175. syncing.remove(name)
  1176. except KeyError:
  1177. print(
  1178. ' {LIGHT_RED}*{ENDC} {0} already synced??? '
  1179. '{1}'.format(name, output, **self.colors)
  1180. )
  1181. return True
  1182. def sync_minion_states(self, targets, timeout=None):
  1183. salt.utils.process.appendproctitle('SyncMinionStates')
  1184. self.sync_minion_modules_('states', targets, timeout=timeout)
  1185. def sync_minion_modules(self, targets, timeout=None):
  1186. salt.utils.process.appendproctitle('SyncMinionModules')
  1187. self.sync_minion_modules_('modules', targets, timeout=timeout)
  1188. def sync_minion_grains(self, targets, timeout=None):
  1189. salt.utils.process.appendproctitle('SyncMinionGrains')
  1190. self.sync_minion_modules_('grains', targets, timeout=timeout)
  1191. def wait_for_minions(self, start, timeout, sleep=5):
  1192. '''
  1193. Ensure all minions and masters (including sub-masters) are connected.
  1194. '''
  1195. while True:
  1196. try:
  1197. ret = self.client.run_job('*', 'test.ping')
  1198. except salt.exceptions.SaltClientError:
  1199. ret = None
  1200. if ret and 'minions' not in ret:
  1201. continue
  1202. if ret and sorted(ret['minions']) == sorted(self.minion_targets):
  1203. break
  1204. if time.time() - start >= timeout:
  1205. raise RuntimeError("Ping Minions Failed")
  1206. time.sleep(sleep)