test_parsers.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080
  1. # -*- coding: utf-8 -*-
  2. '''
  3. :codeauthor: Denys Havrysh <denys.gavrysh@gmail.com>
  4. '''
  5. # Import python libs
  6. from __future__ import absolute_import, print_function, unicode_literals
  7. import os
  8. # Import Salt Testing Libs
  9. from tests.support.unit import skipIf, TestCase
  10. from tests.support.helpers import destructiveTest, skip_if_not_root
  11. from tests.support.mock import (
  12. MagicMock,
  13. patch,
  14. NO_MOCK,
  15. NO_MOCK_REASON
  16. )
  17. # Import Salt Libs
  18. import salt.log.setup as log
  19. import salt.config
  20. import salt.syspaths
  21. import salt.utils.parsers
  22. import salt.utils.platform
  23. try:
  24. import pytest
  25. except ImportError:
  26. pytest = None
  27. class ErrorMock(object): # pylint: disable=too-few-public-methods
  28. '''
  29. Error handling
  30. '''
  31. def __init__(self):
  32. '''
  33. init
  34. '''
  35. self.msg = None
  36. def error(self, msg):
  37. '''
  38. Capture error message
  39. '''
  40. self.msg = msg
  41. class LogSetupMock(object):
  42. '''
  43. Logger setup
  44. '''
  45. def __init__(self):
  46. '''
  47. init
  48. '''
  49. self.log_level = None
  50. self.log_file = None
  51. self.log_level_logfile = None
  52. self.config = {}
  53. self.temp_log_level = None
  54. def setup_console_logger(self, log_level='error', **kwargs): # pylint: disable=unused-argument
  55. '''
  56. Set console loglevel
  57. '''
  58. self.log_level = log_level
  59. def setup_extended_logging(self, opts):
  60. '''
  61. Set opts
  62. '''
  63. self.config = opts
  64. def setup_logfile_logger(self, logfile, loglevel, **kwargs): # pylint: disable=unused-argument
  65. '''
  66. Set logfile and loglevel
  67. '''
  68. self.log_file = logfile
  69. self.log_level_logfile = loglevel
  70. @staticmethod
  71. def get_multiprocessing_logging_queue(): # pylint: disable=invalid-name
  72. '''
  73. Mock
  74. '''
  75. import multiprocessing
  76. return multiprocessing.Queue()
  77. def setup_multiprocessing_logging_listener(self, opts, *args): # pylint: disable=invalid-name,unused-argument
  78. '''
  79. Set opts
  80. '''
  81. self.config = opts
  82. def setup_temp_logger(self, log_level='error'):
  83. '''
  84. Set temp loglevel
  85. '''
  86. self.temp_log_level = log_level
  87. class ObjectView(object): # pylint: disable=too-few-public-methods
  88. '''
  89. Dict object view
  90. '''
  91. def __init__(self, d):
  92. self.__dict__ = d
  93. @destructiveTest
  94. @skip_if_not_root
  95. class LogSettingsParserTests(TestCase):
  96. '''
  97. Unit Tests for Log Level Mixin with Salt parsers
  98. '''
  99. args = []
  100. skip_console_logging_config = False
  101. log_setup = None
  102. # Set config option names
  103. loglevel_config_setting_name = 'log_level'
  104. logfile_config_setting_name = 'log_file'
  105. logfile_loglevel_config_setting_name = 'log_level_logfile' # pylint: disable=invalid-name
  106. def setup_log(self):
  107. '''
  108. Mock logger functions
  109. '''
  110. self.log_setup = LogSetupMock()
  111. patcher = patch.multiple(
  112. log,
  113. setup_console_logger=self.log_setup.setup_console_logger,
  114. setup_extended_logging=self.log_setup.setup_extended_logging,
  115. setup_logfile_logger=self.log_setup.setup_logfile_logger,
  116. get_multiprocessing_logging_queue=self.log_setup.get_multiprocessing_logging_queue,
  117. setup_multiprocessing_logging_listener=self.log_setup.setup_multiprocessing_logging_listener,
  118. setup_temp_logger=self.log_setup.setup_temp_logger
  119. )
  120. patcher.start()
  121. self.addCleanup(patcher.stop)
  122. self.addCleanup(setattr, self, 'log_setup', None)
  123. # log level configuration tests
  124. def test_get_log_level_cli(self):
  125. '''
  126. Tests that log level match command-line specified value
  127. '''
  128. # Set defaults
  129. default_log_level = self.default_config[self.loglevel_config_setting_name]
  130. # Set log level in CLI
  131. log_level = 'critical'
  132. args = ['--log-level', log_level] + self.args
  133. parser = self.parser()
  134. with patch(self.config_func, MagicMock(return_value=self.default_config)):
  135. parser.parse_args(args)
  136. with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)):
  137. parser.setup_logfile_logger()
  138. console_log_level = getattr(parser.options, self.loglevel_config_setting_name)
  139. # Check console log level setting
  140. self.assertEqual(console_log_level, log_level)
  141. # Check console loggger log level
  142. self.assertEqual(self.log_setup.log_level, log_level)
  143. self.assertEqual(self.log_setup.config[self.loglevel_config_setting_name],
  144. log_level)
  145. self.assertEqual(self.log_setup.temp_log_level, log_level)
  146. # Check log file logger log level
  147. self.assertEqual(self.log_setup.log_level_logfile, default_log_level)
  148. def test_get_log_level_config(self):
  149. '''
  150. Tests that log level match the configured value
  151. '''
  152. args = self.args
  153. # Set log level in config
  154. log_level = 'info'
  155. opts = self.default_config.copy()
  156. opts.update({self.loglevel_config_setting_name: log_level})
  157. parser = self.parser()
  158. with patch(self.config_func, MagicMock(return_value=opts)):
  159. parser.parse_args(args)
  160. with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)):
  161. parser.setup_logfile_logger()
  162. console_log_level = getattr(parser.options, self.loglevel_config_setting_name)
  163. # Check console log level setting
  164. self.assertEqual(console_log_level, log_level)
  165. # Check console loggger log level
  166. self.assertEqual(self.log_setup.log_level, log_level)
  167. self.assertEqual(self.log_setup.config[self.loglevel_config_setting_name],
  168. log_level)
  169. self.assertEqual(self.log_setup.temp_log_level, 'error')
  170. # Check log file logger log level
  171. self.assertEqual(self.log_setup.log_level_logfile, log_level)
  172. def test_get_log_level_default(self):
  173. '''
  174. Tests that log level match the default value
  175. '''
  176. # Set defaults
  177. log_level = default_log_level = self.default_config[self.loglevel_config_setting_name]
  178. args = self.args
  179. parser = self.parser()
  180. with patch(self.config_func, MagicMock(return_value=self.default_config)):
  181. parser.parse_args(args)
  182. with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)):
  183. parser.setup_logfile_logger()
  184. console_log_level = getattr(parser.options, self.loglevel_config_setting_name)
  185. # Check log level setting
  186. self.assertEqual(console_log_level, log_level)
  187. # Check console loggger log level
  188. self.assertEqual(self.log_setup.log_level, log_level)
  189. # Check extended logger
  190. self.assertEqual(self.log_setup.config[self.loglevel_config_setting_name],
  191. log_level)
  192. self.assertEqual(self.log_setup.temp_log_level, 'error')
  193. # Check log file logger
  194. self.assertEqual(self.log_setup.log_level_logfile, default_log_level)
  195. # Check help message
  196. self.assertIn('Default: \'{0}\'.'.format(default_log_level),
  197. parser.get_option('--log-level').help)
  198. # log file configuration tests
  199. def test_get_log_file_cli(self):
  200. '''
  201. Tests that log file match command-line specified value
  202. '''
  203. # Set defaults
  204. log_level = self.default_config[self.loglevel_config_setting_name]
  205. # Set log file in CLI
  206. log_file = '{0}_cli.log'.format(self.log_file)
  207. args = ['--log-file', log_file] + self.args
  208. parser = self.parser()
  209. with patch(self.config_func, MagicMock(return_value=self.default_config)):
  210. parser.parse_args(args)
  211. with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)):
  212. parser.setup_logfile_logger()
  213. log_file_option = getattr(parser.options, self.logfile_config_setting_name)
  214. if not self.skip_console_logging_config:
  215. # Check console loggger
  216. self.assertEqual(self.log_setup.log_level, log_level)
  217. # Check extended logger
  218. self.assertEqual(self.log_setup.config[self.loglevel_config_setting_name],
  219. log_level)
  220. self.assertEqual(self.log_setup.config[self.logfile_config_setting_name],
  221. log_file)
  222. # Check temp logger
  223. self.assertEqual(self.log_setup.temp_log_level, 'error')
  224. # Check log file setting
  225. self.assertEqual(log_file_option, log_file)
  226. # Check log file logger
  227. self.assertEqual(self.log_setup.log_file, log_file)
  228. def test_get_log_file_config(self):
  229. '''
  230. Tests that log file match the configured value
  231. '''
  232. # Set defaults
  233. log_level = self.default_config[self.loglevel_config_setting_name]
  234. args = self.args
  235. # Set log file in config
  236. log_file = '{0}_config.log'.format(self.log_file)
  237. opts = self.default_config.copy()
  238. opts.update({self.logfile_config_setting_name: log_file})
  239. parser = self.parser()
  240. with patch(self.config_func, MagicMock(return_value=opts)):
  241. parser.parse_args(args)
  242. with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)):
  243. parser.setup_logfile_logger()
  244. log_file_option = getattr(parser.options, self.logfile_config_setting_name)
  245. if not self.skip_console_logging_config:
  246. # Check console loggger
  247. self.assertEqual(self.log_setup.log_level, log_level)
  248. # Check extended logger
  249. self.assertEqual(self.log_setup.config[self.loglevel_config_setting_name],
  250. log_level)
  251. self.assertEqual(self.log_setup.config[self.logfile_config_setting_name],
  252. log_file)
  253. # Check temp logger
  254. self.assertEqual(self.log_setup.temp_log_level, 'error')
  255. # Check log file setting
  256. self.assertEqual(log_file_option, log_file)
  257. # Check log file logger
  258. self.assertEqual(self.log_setup.log_file, log_file)
  259. def test_get_log_file_default(self):
  260. '''
  261. Tests that log file match the default value
  262. '''
  263. # Set defaults
  264. log_level = self.default_config[self.loglevel_config_setting_name]
  265. log_file = default_log_file = self.default_config[self.logfile_config_setting_name]
  266. args = self.args
  267. parser = self.parser()
  268. with patch(self.config_func, MagicMock(return_value=self.default_config)):
  269. parser.parse_args(args)
  270. with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)):
  271. parser.setup_logfile_logger()
  272. log_file_option = getattr(parser.options, self.logfile_config_setting_name)
  273. if not self.skip_console_logging_config:
  274. # Check console loggger
  275. self.assertEqual(self.log_setup.log_level, log_level)
  276. # Check extended logger
  277. self.assertEqual(self.log_setup.config[self.loglevel_config_setting_name],
  278. log_level)
  279. self.assertEqual(self.log_setup.config[self.logfile_config_setting_name],
  280. log_file)
  281. # Check temp logger
  282. self.assertEqual(self.log_setup.temp_log_level, 'error')
  283. # Check log file setting
  284. self.assertEqual(log_file_option, log_file)
  285. # Check log file logger
  286. self.assertEqual(self.log_setup.log_file, log_file)
  287. # Check help message
  288. self.assertIn('Default: \'{0}\'.'.format(default_log_file),
  289. parser.get_option('--log-file').help)
  290. # log file log level configuration tests
  291. def test_get_log_file_level_cli(self):
  292. '''
  293. Tests that file log level match command-line specified value
  294. '''
  295. # Set defaults
  296. default_log_level = self.default_config[self.loglevel_config_setting_name]
  297. # Set log file level in CLI
  298. log_level_logfile = 'error'
  299. args = ['--log-file-level', log_level_logfile] + self.args
  300. parser = self.parser()
  301. with patch(self.config_func, MagicMock(return_value=self.default_config)):
  302. parser.parse_args(args)
  303. with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)):
  304. parser.setup_logfile_logger()
  305. log_level_logfile_option = getattr(parser.options,
  306. self.logfile_loglevel_config_setting_name)
  307. if not self.skip_console_logging_config:
  308. # Check console loggger
  309. self.assertEqual(self.log_setup.log_level, default_log_level)
  310. # Check extended logger
  311. self.assertEqual(self.log_setup.config[self.loglevel_config_setting_name],
  312. default_log_level)
  313. self.assertEqual(self.log_setup.config[self.logfile_loglevel_config_setting_name],
  314. log_level_logfile)
  315. # Check temp logger
  316. self.assertEqual(self.log_setup.temp_log_level, 'error')
  317. # Check log file level setting
  318. self.assertEqual(log_level_logfile_option, log_level_logfile)
  319. # Check log file logger
  320. self.assertEqual(self.log_setup.log_level_logfile, log_level_logfile)
  321. def test_get_log_file_level_config(self):
  322. '''
  323. Tests that log file level match the configured value
  324. '''
  325. # Set defaults
  326. log_level = self.default_config[self.loglevel_config_setting_name]
  327. args = self.args
  328. # Set log file level in config
  329. log_level_logfile = 'info'
  330. opts = self.default_config.copy()
  331. opts.update({self.logfile_loglevel_config_setting_name: log_level_logfile})
  332. parser = self.parser()
  333. with patch(self.config_func, MagicMock(return_value=opts)):
  334. parser.parse_args(args)
  335. with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)):
  336. parser.setup_logfile_logger()
  337. log_level_logfile_option = getattr(parser.options,
  338. self.logfile_loglevel_config_setting_name)
  339. if not self.skip_console_logging_config:
  340. # Check console loggger
  341. self.assertEqual(self.log_setup.log_level, log_level)
  342. # Check extended logger
  343. self.assertEqual(self.log_setup.config[self.loglevel_config_setting_name],
  344. log_level)
  345. self.assertEqual(self.log_setup.config[self.logfile_loglevel_config_setting_name],
  346. log_level_logfile)
  347. # Check temp logger
  348. self.assertEqual(self.log_setup.temp_log_level, 'error')
  349. # Check log file level setting
  350. self.assertEqual(log_level_logfile_option, log_level_logfile)
  351. # Check log file logger
  352. self.assertEqual(self.log_setup.log_level_logfile, log_level_logfile)
  353. def test_get_log_file_level_default(self):
  354. '''
  355. Tests that log file level match the default value
  356. '''
  357. # Set defaults
  358. default_log_level = self.default_config[self.loglevel_config_setting_name]
  359. log_level = default_log_level
  360. log_level_logfile = default_log_level
  361. args = self.args
  362. parser = self.parser()
  363. with patch(self.config_func, MagicMock(return_value=self.default_config)):
  364. parser.parse_args(args)
  365. with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)):
  366. parser.setup_logfile_logger()
  367. log_level_logfile_option = getattr(parser.options,
  368. self.logfile_loglevel_config_setting_name)
  369. if not self.skip_console_logging_config:
  370. # Check console loggger
  371. self.assertEqual(self.log_setup.log_level, log_level)
  372. # Check extended logger
  373. self.assertEqual(self.log_setup.config[self.loglevel_config_setting_name],
  374. log_level)
  375. self.assertEqual(self.log_setup.config[self.logfile_loglevel_config_setting_name],
  376. log_level_logfile)
  377. # Check temp logger
  378. self.assertEqual(self.log_setup.temp_log_level, 'error')
  379. # Check log file level setting
  380. self.assertEqual(log_level_logfile_option, log_level_logfile)
  381. # Check log file logger
  382. self.assertEqual(self.log_setup.log_level_logfile, log_level_logfile)
  383. # Check help message
  384. self.assertIn('Default: \'{0}\'.'.format(default_log_level),
  385. parser.get_option('--log-file-level').help)
  386. def test_get_console_log_level_with_file_log_level(self): # pylint: disable=invalid-name
  387. '''
  388. Tests that both console log level and log file level setting are working together
  389. '''
  390. log_level = 'critical'
  391. log_level_logfile = 'debug'
  392. args = ['--log-file-level', log_level_logfile] + self.args
  393. opts = self.default_config.copy()
  394. opts.update({self.loglevel_config_setting_name: log_level})
  395. parser = self.parser()
  396. with patch(self.config_func, MagicMock(return_value=opts)):
  397. parser.parse_args(args)
  398. with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)):
  399. parser.setup_logfile_logger()
  400. log_level_logfile_option = getattr(parser.options,
  401. self.logfile_loglevel_config_setting_name)
  402. if not self.skip_console_logging_config:
  403. # Check console loggger
  404. self.assertEqual(self.log_setup.log_level, log_level)
  405. # Check extended logger
  406. self.assertEqual(self.log_setup.config[self.loglevel_config_setting_name],
  407. log_level)
  408. self.assertEqual(self.log_setup.config[self.logfile_loglevel_config_setting_name],
  409. log_level_logfile)
  410. # Check temp logger
  411. self.assertEqual(self.log_setup.temp_log_level, 'error')
  412. # Check log file level setting
  413. self.assertEqual(log_level_logfile_option, log_level_logfile)
  414. # Check log file logger
  415. self.assertEqual(self.log_setup.log_level_logfile, log_level_logfile)
  416. @skipIf(salt.utils.platform.is_windows(), 'Windows uses a logging listener')
  417. def test_log_created(self):
  418. '''
  419. Tests that log file is created
  420. '''
  421. args = self.args
  422. log_file = self.log_file
  423. log_file_name = self.logfile_config_setting_name
  424. opts = self.default_config.copy()
  425. opts.update({'log_file': log_file})
  426. if log_file_name != 'log_file':
  427. opts.update({log_file_name: getattr(self, log_file_name)})
  428. if log_file_name == 'key_logfile':
  429. self.skipTest('salt-key creates log file outside of parse_args.')
  430. parser = self.parser()
  431. with patch(self.config_func, MagicMock(return_value=opts)):
  432. parser.parse_args(args)
  433. if log_file_name == 'log_file':
  434. self.assertEqual(os.path.getsize(log_file), 0)
  435. else:
  436. self.assertEqual(os.path.getsize(getattr(self, log_file_name)), 0)
  437. @skipIf(NO_MOCK, NO_MOCK_REASON)
  438. @skipIf(salt.utils.platform.is_windows(), 'Windows uses a logging listener')
  439. class MasterOptionParserTestCase(LogSettingsParserTests):
  440. '''
  441. Tests parsing Salt Master options
  442. '''
  443. def setUp(self):
  444. '''
  445. Setting up
  446. '''
  447. # Set defaults
  448. self.default_config = salt.config.DEFAULT_MASTER_OPTS
  449. self.addCleanup(delattr, self, 'default_config')
  450. # Log file
  451. self.log_file = '/tmp/salt_master_parser_test'
  452. # Function to patch
  453. self.config_func = 'salt.config.master_config'
  454. # Mock log setup
  455. self.setup_log()
  456. # Assign parser
  457. self.parser = salt.utils.parsers.MasterOptionParser
  458. self.addCleanup(delattr, self, 'parser')
  459. @skipIf(NO_MOCK, NO_MOCK_REASON)
  460. @skipIf(salt.utils.platform.is_windows(), 'Windows uses a logging listener')
  461. class MinionOptionParserTestCase(LogSettingsParserTests):
  462. '''
  463. Tests parsing Salt Minion options
  464. '''
  465. def setUp(self):
  466. '''
  467. Setting up
  468. '''
  469. # Set defaults
  470. self.default_config = salt.config.DEFAULT_MINION_OPTS
  471. self.addCleanup(delattr, self, 'default_config')
  472. # Log file
  473. self.log_file = '/tmp/salt_minion_parser_test'
  474. # Function to patch
  475. self.config_func = 'salt.config.minion_config'
  476. # Mock log setup
  477. self.setup_log()
  478. # Assign parser
  479. self.parser = salt.utils.parsers.MinionOptionParser
  480. self.addCleanup(delattr, self, 'parser')
  481. @skipIf(NO_MOCK, NO_MOCK_REASON)
  482. class ProxyMinionOptionParserTestCase(LogSettingsParserTests):
  483. '''
  484. Tests parsing Salt Proxy Minion options
  485. '''
  486. def setUp(self):
  487. '''
  488. Setting up
  489. '''
  490. # Set defaults
  491. self.default_config = salt.config.DEFAULT_MINION_OPTS.copy()
  492. self.default_config.update(salt.config.DEFAULT_PROXY_MINION_OPTS)
  493. self.addCleanup(delattr, self, 'default_config')
  494. # Log file
  495. self.log_file = '/tmp/salt_proxy_minion_parser_test'
  496. # Function to patch
  497. self.config_func = 'salt.config.proxy_config'
  498. # Mock log setup
  499. self.setup_log()
  500. # Assign parser
  501. self.parser = salt.utils.parsers.ProxyMinionOptionParser
  502. self.addCleanup(delattr, self, 'parser')
  503. @skipIf(NO_MOCK, NO_MOCK_REASON)
  504. @skipIf(salt.utils.platform.is_windows(), 'Windows uses a logging listener')
  505. class SyndicOptionParserTestCase(LogSettingsParserTests):
  506. '''
  507. Tests parsing Salt Syndic options
  508. '''
  509. def setUp(self):
  510. '''
  511. Setting up
  512. '''
  513. # Set config option names
  514. self.logfile_config_setting_name = 'syndic_log_file'
  515. # Set defaults
  516. self.default_config = salt.config.DEFAULT_MASTER_OPTS
  517. self.addCleanup(delattr, self, 'default_config')
  518. # Log file
  519. self.log_file = '/tmp/salt_syndic_parser_test'
  520. self.syndic_log_file = '/tmp/salt_syndic_log'
  521. # Function to patch
  522. self.config_func = 'salt.config.syndic_config'
  523. # Mock log setup
  524. self.setup_log()
  525. # Assign parser
  526. self.parser = salt.utils.parsers.SyndicOptionParser
  527. self.addCleanup(delattr, self, 'parser')
  528. @skipIf(NO_MOCK, NO_MOCK_REASON)
  529. class SaltCMDOptionParserTestCase(LogSettingsParserTests):
  530. '''
  531. Tests parsing Salt CLI options
  532. '''
  533. def setUp(self):
  534. '''
  535. Setting up
  536. '''
  537. # Set mandatory CLI options
  538. self.args = ['foo', 'bar.baz']
  539. # Set defaults
  540. self.default_config = salt.config.DEFAULT_MASTER_OPTS
  541. self.addCleanup(delattr, self, 'default_config')
  542. # Log file
  543. self.log_file = '/tmp/salt_cmd_parser_test'
  544. # Function to patch
  545. self.config_func = 'salt.config.client_config'
  546. # Mock log setup
  547. self.setup_log()
  548. # Assign parser
  549. self.parser = salt.utils.parsers.SaltCMDOptionParser
  550. self.addCleanup(delattr, self, 'parser')
  551. @skipIf(NO_MOCK, NO_MOCK_REASON)
  552. class SaltCPOptionParserTestCase(LogSettingsParserTests):
  553. '''
  554. Tests parsing salt-cp options
  555. '''
  556. def setUp(self):
  557. '''
  558. Setting up
  559. '''
  560. # Set mandatory CLI options
  561. self.args = ['foo', 'bar', 'baz']
  562. # Set defaults
  563. self.default_config = salt.config.DEFAULT_MASTER_OPTS
  564. self.addCleanup(delattr, self, 'default_config')
  565. # Log file
  566. self.log_file = '/tmp/salt_cp_parser_test'
  567. # Function to patch
  568. self.config_func = 'salt.config.master_config'
  569. # Mock log setup
  570. self.setup_log()
  571. # Assign parser
  572. self.parser = salt.utils.parsers.SaltCPOptionParser
  573. self.addCleanup(delattr, self, 'parser')
  574. @skipIf(NO_MOCK, NO_MOCK_REASON)
  575. class SaltKeyOptionParserTestCase(LogSettingsParserTests):
  576. '''
  577. Tests parsing salt-key options
  578. '''
  579. def setUp(self):
  580. '''
  581. Setting up
  582. '''
  583. self.skip_console_logging_config = True
  584. # Set config option names
  585. self.logfile_config_setting_name = 'key_logfile'
  586. # Set defaults
  587. self.default_config = salt.config.DEFAULT_MASTER_OPTS
  588. self.addCleanup(delattr, self, 'default_config')
  589. # Log file
  590. self.log_file = '/tmp/salt_key_parser_test'
  591. self.key_logfile = '/tmp/key_logfile'
  592. # Function to patch
  593. self.config_func = 'salt.config.master_config'
  594. # Mock log setup
  595. self.setup_log()
  596. # Assign parser
  597. self.parser = salt.utils.parsers.SaltKeyOptionParser
  598. self.addCleanup(delattr, self, 'parser')
  599. # log level configuration tests
  600. def test_get_log_level_cli(self):
  601. '''
  602. Tests that console log level option is not recognized
  603. '''
  604. # No console log level will be actually set
  605. log_level = default_log_level = None
  606. option = '--log-level'
  607. args = self.args + [option, 'error']
  608. parser = self.parser()
  609. mock_err = ErrorMock()
  610. with patch('salt.utils.parsers.OptionParser.error', mock_err.error):
  611. parser.parse_args(args)
  612. # Check error msg
  613. self.assertEqual(mock_err.msg, 'no such option: {0}'.format(option))
  614. # Check console loggger has not been set
  615. self.assertEqual(self.log_setup.log_level, log_level)
  616. self.assertNotIn(self.loglevel_config_setting_name, self.log_setup.config)
  617. # Check temp logger
  618. self.assertEqual(self.log_setup.temp_log_level, 'error')
  619. # Check log file logger log level
  620. self.assertEqual(self.log_setup.log_level_logfile, default_log_level)
  621. def test_get_log_level_config(self):
  622. '''
  623. Tests that log level set in config is ignored
  624. '''
  625. log_level = 'info'
  626. args = self.args
  627. # Set log level in config and set additional mocked opts keys
  628. opts = {self.loglevel_config_setting_name: log_level,
  629. self.logfile_config_setting_name: 'key_logfile',
  630. 'log_fmt_logfile': None,
  631. 'log_datefmt_logfile': None,
  632. 'log_rotate_max_bytes': None,
  633. 'log_rotate_backup_count': None}
  634. parser = self.parser()
  635. with patch(self.config_func, MagicMock(return_value=opts)):
  636. parser.parse_args(args)
  637. with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)):
  638. parser.setup_logfile_logger()
  639. # Check config name absence in options
  640. self.assertNotIn(self.loglevel_config_setting_name, parser.options.__dict__)
  641. # Check console loggger has not been set
  642. self.assertEqual(self.log_setup.log_level, None)
  643. self.assertNotIn(self.loglevel_config_setting_name, self.log_setup.config)
  644. # Check temp logger
  645. self.assertEqual(self.log_setup.temp_log_level, 'error')
  646. # Check log file logger log level
  647. self.assertEqual(self.log_setup.log_level_logfile, log_level)
  648. def test_get_log_level_default(self):
  649. '''
  650. Tests that log level default value is ignored
  651. '''
  652. # Set defaults
  653. default_log_level = self.default_config[self.loglevel_config_setting_name]
  654. log_level = None
  655. args = self.args
  656. parser = self.parser()
  657. parser.parse_args(args)
  658. with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)):
  659. parser.setup_logfile_logger()
  660. # Check config name absence in options
  661. self.assertNotIn(self.loglevel_config_setting_name, parser.options.__dict__)
  662. # Check console loggger has not been set
  663. self.assertEqual(self.log_setup.log_level, log_level)
  664. self.assertNotIn(self.loglevel_config_setting_name, self.log_setup.config)
  665. # Check temp logger
  666. self.assertEqual(self.log_setup.temp_log_level, 'error')
  667. # Check log file logger log level
  668. self.assertEqual(self.log_setup.log_level_logfile, default_log_level)
  669. @skipIf(NO_MOCK, NO_MOCK_REASON)
  670. class SaltCallOptionParserTestCase(LogSettingsParserTests):
  671. '''
  672. Tests parsing Salt Minion options
  673. '''
  674. def setUp(self):
  675. '''
  676. Setting up
  677. '''
  678. # Set mandatory CLI options
  679. self.args = ['foo.bar']
  680. # Set defaults
  681. self.default_config = salt.config.DEFAULT_MINION_OPTS
  682. self.addCleanup(delattr, self, 'default_config')
  683. # Log file
  684. self.log_file = '/tmp/salt_call_parser_test'
  685. # Function to patch
  686. self.config_func = 'salt.config.minion_config'
  687. # Mock log setup
  688. self.setup_log()
  689. # Assign parser
  690. self.parser = salt.utils.parsers.SaltCallOptionParser
  691. self.addCleanup(delattr, self, 'parser')
  692. @skipIf(NO_MOCK, NO_MOCK_REASON)
  693. class SaltRunOptionParserTestCase(LogSettingsParserTests):
  694. '''
  695. Tests parsing Salt Master options
  696. '''
  697. def setUp(self):
  698. '''
  699. Setting up
  700. '''
  701. # Set mandatory CLI options
  702. self.args = ['foo.bar']
  703. # Set defaults
  704. self.default_config = salt.config.DEFAULT_MASTER_OPTS
  705. self.addCleanup(delattr, self, 'default_config')
  706. # Log file
  707. self.log_file = '/tmp/salt_run_parser_test'
  708. # Function to patch
  709. self.config_func = 'salt.config.master_config'
  710. # Mock log setup
  711. self.setup_log()
  712. # Assign parser
  713. self.parser = salt.utils.parsers.SaltRunOptionParser
  714. self.addCleanup(delattr, self, 'parser')
  715. @skipIf(NO_MOCK, NO_MOCK_REASON)
  716. class SaltSSHOptionParserTestCase(LogSettingsParserTests):
  717. '''
  718. Tests parsing Salt Master options
  719. '''
  720. def setUp(self):
  721. '''
  722. Setting up
  723. '''
  724. # Set mandatory CLI options
  725. self.args = ['foo', 'bar.baz']
  726. # Set config option names
  727. self.logfile_config_setting_name = 'ssh_log_file'
  728. # Set defaults
  729. self.default_config = salt.config.DEFAULT_MASTER_OPTS
  730. self.addCleanup(delattr, self, 'default_config')
  731. # Log file
  732. self.log_file = '/tmp/salt_ssh_parser_test'
  733. self.ssh_log_file = '/tmp/ssh_logfile'
  734. # Function to patch
  735. self.config_func = 'salt.config.master_config'
  736. # Mock log setup
  737. self.setup_log()
  738. # Assign parser
  739. self.parser = salt.utils.parsers.SaltSSHOptionParser
  740. self.addCleanup(delattr, self, 'parser')
  741. @skipIf(NO_MOCK, NO_MOCK_REASON)
  742. class SaltCloudParserTestCase(LogSettingsParserTests):
  743. '''
  744. Tests parsing Salt Cloud options
  745. '''
  746. def setUp(self):
  747. '''
  748. Setting up
  749. '''
  750. # Set mandatory CLI options
  751. self.args = ['-p', 'foo', 'bar']
  752. # Set default configs
  753. # Cloud configs are merged with master configs in
  754. # config/__init__.py, so we'll do that here as well
  755. # As we need the 'user' key later on.
  756. self.default_config = salt.config.DEFAULT_MASTER_OPTS.copy()
  757. self.default_config.update(salt.config.DEFAULT_CLOUD_OPTS)
  758. self.addCleanup(delattr, self, 'default_config')
  759. # Log file
  760. self.log_file = '/tmp/salt_cloud_parser_test'
  761. # Function to patch
  762. self.config_func = 'salt.config.cloud_config'
  763. # Mock log setup
  764. self.setup_log()
  765. # Assign parser
  766. self.parser = salt.utils.parsers.SaltCloudParser
  767. self.addCleanup(delattr, self, 'parser')
  768. @skipIf(NO_MOCK, NO_MOCK_REASON)
  769. class SPMParserTestCase(LogSettingsParserTests):
  770. '''
  771. Tests parsing Salt Cloud options
  772. '''
  773. def setUp(self):
  774. '''
  775. Setting up
  776. '''
  777. # Set mandatory CLI options
  778. self.args = ['foo', 'bar']
  779. # Set config option names
  780. self.logfile_config_setting_name = 'spm_logfile'
  781. # Set defaults
  782. self.default_config = salt.config.DEFAULT_MASTER_OPTS.copy()
  783. self.default_config.update(salt.config.DEFAULT_SPM_OPTS)
  784. self.addCleanup(delattr, self, 'default_config')
  785. # Log file
  786. self.log_file = '/tmp/spm_parser_test'
  787. self.spm_logfile = '/tmp/spm_logfile'
  788. # Function to patch
  789. self.config_func = 'salt.config.spm_config'
  790. # Mock log setup
  791. self.setup_log()
  792. # Assign parser
  793. self.parser = salt.utils.parsers.SPMParser
  794. self.addCleanup(delattr, self, 'parser')
  795. @skipIf(NO_MOCK, NO_MOCK_REASON)
  796. class SaltAPIParserTestCase(LogSettingsParserTests):
  797. '''
  798. Tests parsing Salt Cloud options
  799. '''
  800. def setUp(self):
  801. '''
  802. Setting up
  803. '''
  804. # Set mandatory CLI options
  805. self.args = []
  806. # Set config option names
  807. self.logfile_config_setting_name = 'api_logfile'
  808. # Set defaults
  809. self.default_config = salt.config.DEFAULT_MASTER_OPTS.copy()
  810. self.default_config.update(salt.config.DEFAULT_API_OPTS)
  811. self.addCleanup(delattr, self, 'default_config')
  812. # Log file
  813. self.log_file = '/tmp/salt_api_parser_test'
  814. self.api_logfile = '/tmp/api_logfile'
  815. # Function to patch
  816. self.config_func = 'salt.config.api_config'
  817. # Mock log setup
  818. self.setup_log()
  819. # Assign parser
  820. self.parser = salt.utils.parsers.SaltAPIParser
  821. self.addCleanup(delattr, self, 'parser')
  822. @skipIf(not pytest, False)
  823. @skipIf(NO_MOCK, NO_MOCK_REASON)
  824. class DaemonMixInTestCase(TestCase):
  825. '''
  826. Tests the PIDfile deletion in the DaemonMixIn.
  827. '''
  828. def setUp(self):
  829. '''
  830. Setting up
  831. '''
  832. # Setup mixin
  833. self.daemon_mixin = salt.utils.parsers.DaemonMixIn()
  834. self.daemon_mixin.config = {}
  835. self.daemon_mixin.config['pidfile'] = '/some/fake.pid'
  836. def tearDown(self):
  837. '''
  838. Tear down test
  839. :return:
  840. '''
  841. del self.daemon_mixin
  842. @patch('os.unlink', MagicMock())
  843. @patch('os.path.isfile', MagicMock(return_value=True))
  844. @patch('salt.utils.parsers.logger', MagicMock())
  845. def test_pid_file_deletion(self):
  846. '''
  847. PIDfile deletion without exception.
  848. '''
  849. self.daemon_mixin._mixin_before_exit()
  850. assert salt.utils.parsers.os.unlink.call_count == 1
  851. salt.utils.parsers.logger.info.assert_not_called()
  852. salt.utils.parsers.logger.debug.assert_not_called()
  853. @patch('os.unlink', MagicMock(side_effect=OSError()))
  854. @patch('os.path.isfile', MagicMock(return_value=True))
  855. @patch('salt.utils.parsers.logger', MagicMock())
  856. def test_pid_deleted_oserror_as_root(self):
  857. '''
  858. PIDfile deletion with exception, running as root.
  859. '''
  860. if salt.utils.platform.is_windows():
  861. patch_args = ('salt.utils.win_functions.is_admin',
  862. MagicMock(return_value=True))
  863. else:
  864. patch_args = ('os.getuid', MagicMock(return_value=0))
  865. with patch(*patch_args):
  866. self.daemon_mixin._mixin_before_exit()
  867. assert salt.utils.parsers.os.unlink.call_count == 1
  868. salt.utils.parsers.logger.info.assert_called_with(
  869. 'PIDfile could not be deleted: %s',
  870. format(self.daemon_mixin.config['pidfile'])
  871. )
  872. salt.utils.parsers.logger.debug.assert_called()
  873. @patch('os.unlink', MagicMock(side_effect=OSError()))
  874. @patch('os.path.isfile', MagicMock(return_value=True))
  875. @patch('salt.utils.parsers.logger', MagicMock())
  876. def test_pid_deleted_oserror_as_non_root(self):
  877. '''
  878. PIDfile deletion with exception, running as non-root.
  879. '''
  880. if salt.utils.platform.is_windows():
  881. patch_args = ('salt.utils.win_functions.is_admin',
  882. MagicMock(return_value=False))
  883. else:
  884. patch_args = ('os.getuid', MagicMock(return_value=1000))
  885. with patch(*patch_args):
  886. self.daemon_mixin._mixin_before_exit()
  887. assert salt.utils.parsers.os.unlink.call_count == 1
  888. salt.utils.parsers.logger.info.assert_not_called()
  889. salt.utils.parsers.logger.debug.assert_not_called()
  890. # Hide the class from unittest framework when it searches for TestCase classes in the module
  891. del LogSettingsParserTests