PageRenderTime 60ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/salt/grains/core.py

https://github.com/vitaliyf/salt
Python | 1523 lines | 1409 code | 22 blank | 92 comment | 61 complexity | e53adb65f2c502105895c2e6fdd2a955 MD5 | raw file
Possible License(s): Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. # -*- coding: utf-8 -*-
  2. '''
  3. The static grains, these are the core, or built in grains.
  4. When grains are loaded they are not loaded in the same way that modules are
  5. loaded, grain functions are detected and executed, the functions MUST
  6. return a dict which will be applied to the main grains dict. This module
  7. will always be executed first, so that any grains loaded here in the core
  8. module can be overwritten just by returning dict keys with the same value
  9. as those returned here
  10. '''
  11. # Import python libs
  12. import os
  13. import socket
  14. import sys
  15. import re
  16. import platform
  17. import logging
  18. import locale
  19. # Extend the default list of supported distros. This will be used for the
  20. # /etc/DISTRO-release checking that is part of platform.linux_distribution()
  21. from platform import _supported_dists
  22. _supported_dists += ('arch', 'mageia', 'meego', 'vmware', 'bluewhite64',
  23. 'slamd64', 'ovs', 'system', 'mint', 'oracle')
  24. # Import salt libs
  25. import salt.log
  26. import salt.utils
  27. import salt.utils.network
  28. from salt._compat import string_types
  29. # Solve the Chicken and egg problem where grains need to run before any
  30. # of the modules are loaded and are generally available for any usage.
  31. import salt.modules.cmdmod
  32. __salt__ = {
  33. 'cmd.run': salt.modules.cmdmod._run_quiet,
  34. 'cmd.retcode': salt.modules.cmdmod._retcode_quiet,
  35. 'cmd.run_all': salt.modules.cmdmod._run_all_quiet
  36. }
  37. log = logging.getLogger(__name__)
  38. HAS_WMI = False
  39. if salt.utils.is_windows():
  40. # attempt to import the python wmi module
  41. # the Windows minion uses WMI for some of its grains
  42. try:
  43. import wmi
  44. import salt.utils.winapi
  45. HAS_WMI = True
  46. except ImportError:
  47. log.exception(
  48. 'Unable to import Python wmi module, some core grains '
  49. 'will be missing'
  50. )
  51. def _windows_cpudata():
  52. '''
  53. Return some CPU information on Windows minions
  54. '''
  55. # Provides:
  56. # num_cpus
  57. # cpu_model
  58. grains = {}
  59. if 'NUMBER_OF_PROCESSORS' in os.environ:
  60. # Cast to int so that the logic isn't broken when used as a
  61. # conditional in templating. Also follows _linux_cpudata()
  62. try:
  63. grains['num_cpus'] = int(os.environ['NUMBER_OF_PROCESSORS'])
  64. except ValueError:
  65. grains['num_cpus'] = 1
  66. grains['cpu_model'] = platform.processor()
  67. return grains
  68. def _linux_cpudata():
  69. '''
  70. Return some CPU information for Linux minions
  71. '''
  72. # Provides:
  73. # num_cpus
  74. # cpu_model
  75. # cpu_flags
  76. grains = {}
  77. cpuinfo = '/proc/cpuinfo'
  78. # Parse over the cpuinfo file
  79. if os.path.isfile(cpuinfo):
  80. with salt.utils.fopen(cpuinfo, 'r') as _fp:
  81. for line in _fp:
  82. comps = line.split(':')
  83. if not len(comps) > 1:
  84. continue
  85. key = comps[0].strip()
  86. val = comps[1].strip()
  87. if key == 'processor':
  88. grains['num_cpus'] = int(val) + 1
  89. elif key == 'model name':
  90. grains['cpu_model'] = val
  91. elif key == 'flags':
  92. grains['cpu_flags'] = val.split()
  93. elif key == 'Features':
  94. grains['cpu_flags'] = val.split()
  95. # ARM support - /proc/cpuinfo
  96. #
  97. # Processor : ARMv6-compatible processor rev 7 (v6l)
  98. # BogoMIPS : 697.95
  99. # Features : swp half thumb fastmult vfp edsp java tls
  100. # CPU implementer : 0x41
  101. # CPU architecture: 7
  102. # CPU variant : 0x0
  103. # CPU part : 0xb76
  104. # CPU revision : 7
  105. #
  106. # Hardware : BCM2708
  107. # Revision : 0002
  108. # Serial : 00000000XXXXXXXX
  109. elif key == 'Processor':
  110. grains['cpu_model'] = val.split('-')[0]
  111. grains['num_cpus'] = 1
  112. if 'num_cpus' not in grains:
  113. grains['num_cpus'] = 0
  114. if 'cpu_model' not in grains:
  115. grains['cpu_model'] = 'Unknown'
  116. if 'cpu_flags' not in grains:
  117. grains['cpu_flags'] = []
  118. return grains
  119. def _linux_gpu_data():
  120. '''
  121. num_gpus: int
  122. gpus:
  123. - vendor: nvidia|amd|ati|...
  124. model: string
  125. '''
  126. if __opts__.get('enable_gpu_grains', True) is False:
  127. return {}
  128. lspci = salt.utils.which('lspci')
  129. if not lspci:
  130. log.debug(
  131. 'The `lspci` binary is not available on the system. GPU grains '
  132. 'will not be available.'
  133. )
  134. return {}
  135. # dominant gpu vendors to search for (MUST be lowercase for matching below)
  136. known_vendors = ['nvidia', 'amd', 'ati', 'intel']
  137. devs = []
  138. try:
  139. lspci_out = __salt__['cmd.run']('lspci -vmm')
  140. cur_dev = {}
  141. error = False
  142. # Add a blank element to the lspci_out.splitlines() list,
  143. # otherwise the last device is not evaluated as a cur_dev and ignored.
  144. lspci_list = lspci_out.splitlines()
  145. lspci_list.append('')
  146. for line in lspci_list:
  147. # check for record-separating empty lines
  148. if line == '':
  149. if cur_dev.get('Class', '') == 'VGA compatible controller':
  150. devs.append(cur_dev)
  151. # XXX; may also need to search for "3D controller"
  152. cur_dev = {}
  153. continue
  154. if re.match(r'^\w+:\s+.*', line):
  155. key, val = line.split(':', 1)
  156. cur_dev[key.strip()] = val.strip()
  157. else:
  158. error = True
  159. log.debug('Unexpected lspci output: {0!r}'.format(line))
  160. if error:
  161. log.warn(
  162. 'Error loading grains, unexpected linux_gpu_data output, '
  163. 'check that you have a valid shell configured and '
  164. 'permissions to run lspci command'
  165. )
  166. except OSError:
  167. pass
  168. gpus = []
  169. for gpu in devs:
  170. vendor_strings = gpu['Vendor'].lower().split()
  171. # default vendor to 'unknown', overwrite if we match a known one
  172. vendor = 'unknown'
  173. for name in known_vendors:
  174. # search for an 'expected' vendor name in the list of strings
  175. if name in vendor_strings:
  176. vendor = name
  177. break
  178. gpus.append({'vendor': vendor, 'model': gpu['Device']})
  179. grains = {}
  180. grains['num_gpus'] = len(gpus)
  181. grains['gpus'] = gpus
  182. return grains
  183. def _netbsd_gpu_data():
  184. '''
  185. num_gpus: int
  186. gpus:
  187. - vendor: nvidia|amd|ati|...
  188. model: string
  189. '''
  190. known_vendors = ['nvidia', 'amd', 'ati', 'intel', 'cirrus logic', 'vmware']
  191. gpus = []
  192. try:
  193. pcictl_out = __salt__['cmd.run']('pcictl pci0 list')
  194. for line in pcictl_out.splitlines():
  195. for vendor in known_vendors:
  196. m = re.match(
  197. r'[0-9:]+ ({0}) (.+) \(VGA .+\)'.format(vendor),
  198. line,
  199. re.IGNORECASE
  200. )
  201. if m:
  202. gpus.append({'vendor': m.group(1), 'model': m.group(2)})
  203. except OSError:
  204. pass
  205. grains = {}
  206. grains['num_gpus'] = len(gpus)
  207. grains['gpus'] = gpus
  208. return grains
  209. def _osx_gpudata():
  210. '''
  211. num_gpus: int
  212. gpus:
  213. - vendor: nvidia|amd|ati|...
  214. model: string
  215. '''
  216. gpus = []
  217. try:
  218. pcictl_out = __salt__['cmd.run']('system_profiler SPDisplaysDataType')
  219. for line in pcictl_out.splitlines():
  220. fieldname, _, fieldval = line.partition(': ')
  221. if fieldname.strip() == "Chipset Model":
  222. vendor, _, model = fieldval.partition(' ')
  223. vendor = vendor.lower()
  224. gpus.append({'vendor': vendor, 'model': model})
  225. except OSError:
  226. pass
  227. grains = {}
  228. grains['num_gpus'] = len(gpus)
  229. grains['gpus'] = gpus
  230. return grains
  231. def _bsd_cpudata(osdata):
  232. '''
  233. Return CPU information for BSD-like systems
  234. '''
  235. # Provides:
  236. # cpuarch
  237. # num_cpus
  238. # cpu_model
  239. # cpu_flags
  240. sysctl = salt.utils.which('sysctl')
  241. arch = salt.utils.which('arch')
  242. cmds = {}
  243. if sysctl:
  244. cmds.update({
  245. 'num_cpus': '{0} -n hw.ncpu'.format(sysctl),
  246. 'cpuarch': '{0} -n hw.machine'.format(sysctl),
  247. 'cpu_model': '{0} -n hw.model'.format(sysctl),
  248. })
  249. if arch and osdata['kernel'] == 'OpenBSD':
  250. cmds['cpuarch'] = '{0} -s'.format(arch)
  251. if osdata['kernel'] == 'Darwin':
  252. cmds['cpu_model'] = '{0} -n machdep.cpu.brand_string'.format(sysctl)
  253. cmds['cpu_flags'] = '{0} -n machdep.cpu.features'.format(sysctl)
  254. grains = dict([(k, __salt__['cmd.run'](v)) for k, v in cmds.items()])
  255. if 'cpu_flags' in grains and isinstance(grains['cpu_flags'], string_types):
  256. grains['cpu_flags'] = grains['cpu_flags'].split(' ')
  257. if osdata['kernel'] == 'NetBSD':
  258. grains['cpu_flags'] = []
  259. for line in __salt__['cmd.run']('cpuctl identify 0').splitlines():
  260. m = re.match(r'cpu[0-9]:\ features[0-9]?\ .+<(.+)>', line)
  261. if m:
  262. flag = m.group(1).split(',')
  263. grains['cpu_flags'].extend(flag)
  264. if osdata['kernel'] == 'FreeBSD' and os.path.isfile('/var/run/dmesg.boot'):
  265. grains['cpu_flags'] = []
  266. # TODO: at least it needs to be tested for BSD other then FreeBSD
  267. with salt.utils.fopen('/var/run/dmesg.boot', 'r') as _fp:
  268. cpu_here = False
  269. for line in _fp:
  270. if line.startswith('CPU: '):
  271. cpu_here = True # starts CPU descr
  272. continue
  273. if cpu_here:
  274. if not line.startswith(' '):
  275. break # game over
  276. if 'Features' in line:
  277. start = line.find('<')
  278. end = line.find('>')
  279. if start > 0 and end > 0:
  280. flag = line[start + 1:end].split(',')
  281. grains['cpu_flags'].extend(flag)
  282. try:
  283. grains['num_cpus'] = int(grains['num_cpus'])
  284. except ValueError:
  285. grains['num_cpus'] = 1
  286. return grains
  287. def _sunos_cpudata():
  288. '''
  289. Return the CPU information for Solaris-like systems
  290. '''
  291. # Provides:
  292. # cpuarch
  293. # num_cpus
  294. # cpu_model
  295. # cpu_flags
  296. grains = {}
  297. grains['cpu_flags'] = []
  298. grains['cpuarch'] = __salt__['cmd.run']('uname -p')
  299. psrinfo = '/usr/sbin/psrinfo 2>/dev/null'
  300. grains['num_cpus'] = len(__salt__['cmd.run'](psrinfo).splitlines())
  301. kstat_info = 'kstat -p cpu_info:0:*:brand'
  302. for line in __salt__['cmd.run'](kstat_info).splitlines():
  303. match = re.match(r'(\w+:\d+:\w+\d+:\w+)\s+(.+)', line)
  304. if match:
  305. grains['cpu_model'] = match.group(2)
  306. isainfo = 'isainfo -n -v'
  307. for line in __salt__['cmd.run'](isainfo).splitlines():
  308. match = re.match(r'^\s+(.+)', line)
  309. if match:
  310. cpu_flags = match.group(1).split()
  311. grains['cpu_flags'].extend(cpu_flags)
  312. return grains
  313. def _memdata(osdata):
  314. '''
  315. Gather information about the system memory
  316. '''
  317. # Provides:
  318. # mem_total
  319. grains = {'mem_total': 0}
  320. if osdata['kernel'] == 'Linux':
  321. meminfo = '/proc/meminfo'
  322. if os.path.isfile(meminfo):
  323. with salt.utils.fopen(meminfo, 'r') as ifile:
  324. for line in ifile:
  325. comps = line.rstrip('\n').split(':')
  326. if not len(comps) > 1:
  327. continue
  328. if comps[0].strip() == 'MemTotal':
  329. grains['mem_total'] = int(comps[1].split()[0]) / 1024
  330. elif osdata['kernel'] in ('FreeBSD', 'OpenBSD', 'NetBSD', 'Darwin'):
  331. sysctl = salt.utils.which('sysctl')
  332. if sysctl:
  333. if osdata['kernel'] == 'Darwin':
  334. mem = __salt__['cmd.run']('{0} -n hw.memsize'.format(sysctl))
  335. else:
  336. mem = __salt__['cmd.run']('{0} -n hw.physmem'.format(sysctl))
  337. if osdata['kernel'] == 'NetBSD' and mem.startswith('-'):
  338. mem = __salt__['cmd.run']('{0} -n hw.physmem64'.format(sysctl))
  339. grains['mem_total'] = int(mem) / 1024 / 1024
  340. elif osdata['kernel'] == 'SunOS':
  341. prtconf = '/usr/sbin/prtconf 2>/dev/null'
  342. for line in __salt__['cmd.run'](prtconf).splitlines():
  343. comps = line.split(' ')
  344. if comps[0].strip() == 'Memory' and comps[1].strip() == 'size:':
  345. grains['mem_total'] = int(comps[2].strip())
  346. elif osdata['kernel'] == 'Windows' and HAS_WMI:
  347. with salt.utils.winapi.Com():
  348. wmi_c = wmi.WMI()
  349. # this is a list of each stick of ram in a system
  350. # WMI returns it as the string value of the number of bytes
  351. tot_bytes = sum(map(lambda x: int(x.Capacity),
  352. wmi_c.Win32_PhysicalMemory()), 0)
  353. # return memory info in gigabytes
  354. grains['mem_total'] = int(tot_bytes / (1024 ** 2))
  355. return grains
  356. def _virtual(osdata):
  357. '''
  358. Returns what type of virtual hardware is under the hood, kvm or physical
  359. '''
  360. # This is going to be a monster, if you are running a vm you can test this
  361. # grain with please submit patches!
  362. # Provides:
  363. # virtual
  364. # virtual_subtype
  365. grains = {'virtual': 'physical'}
  366. for command in ('dmidecode', 'lspci', 'dmesg'):
  367. args = []
  368. if osdata['kernel'] == 'Darwin':
  369. command = 'system_profiler'
  370. args = ['SPDisplaysDataType']
  371. cmd = salt.utils.which(command)
  372. if not cmd:
  373. continue
  374. cmd = '{0} {1}'.format(command, ' '.join(args))
  375. ret = __salt__['cmd.run_all'](cmd)
  376. if ret['retcode'] > 0:
  377. if salt.log.is_logging_configured():
  378. if salt.utils.is_windows():
  379. continue
  380. log.warn(
  381. 'Although {0!r} was found in path, the current user '
  382. 'cannot execute it. Grains output might not be '
  383. 'accurate.'.format(command)
  384. )
  385. continue
  386. output = ret['stdout']
  387. if command == "system_profiler":
  388. macoutput = output.lower()
  389. if '0x1ab8' in macoutput:
  390. grains['virtual'] = 'Parallels'
  391. if 'parallels' in macoutput:
  392. grains['virtual'] = 'Parallels'
  393. if 'vmware' in macoutput:
  394. grains['virtual'] = 'VMware'
  395. if '0x15ad' in macoutput:
  396. grains['virtual'] = 'VMware'
  397. if 'virtualbox' in macoutput:
  398. grains['virtual'] = 'VirtualBox'
  399. # Break out of the loop so the next log message is not issued
  400. break
  401. elif command == 'dmidecode' or command == 'dmesg':
  402. # Product Name: VirtualBox
  403. if 'Vendor: QEMU' in output:
  404. # FIXME: Make this detect between kvm or qemu
  405. grains['virtual'] = 'kvm'
  406. if 'Vendor: Bochs' in output:
  407. grains['virtual'] = 'kvm'
  408. if 'BHYVE BVXSDT' in output:
  409. grains['virtual'] = 'bhyve'
  410. # Product Name: (oVirt) www.ovirt.org
  411. # Red Hat Community virtualization Project based on kvm
  412. elif 'Manufacturer: oVirt' in output:
  413. grains['virtual'] = 'kvm'
  414. elif 'VirtualBox' in output:
  415. grains['virtual'] = 'VirtualBox'
  416. # Product Name: VMware Virtual Platform
  417. elif 'VMware' in output:
  418. grains['virtual'] = 'VMware'
  419. # Manufacturer: Microsoft Corporation
  420. # Product Name: Virtual Machine
  421. elif ': Microsoft' in output and 'Virtual Machine' in output:
  422. grains['virtual'] = 'VirtualPC'
  423. # Manufacturer: Parallels Software International Inc.
  424. elif 'Parallels Software' in output:
  425. grains['virtual'] = 'Parallels'
  426. # Break out of the loop, lspci parsing is not necessary
  427. break
  428. elif command == 'lspci':
  429. # dmidecode not available or the user does not have the necessary
  430. # permissions
  431. model = output.lower()
  432. if 'vmware' in model:
  433. grains['virtual'] = 'VMware'
  434. # 00:04.0 System peripheral: InnoTek Systemberatung GmbH
  435. # VirtualBox Guest Service
  436. elif 'virtualbox' in model:
  437. grains['virtual'] = 'VirtualBox'
  438. elif 'qemu' in model:
  439. grains['virtual'] = 'kvm'
  440. elif 'virtio' in model:
  441. grains['virtual'] = 'kvm'
  442. # Break out of the loop so the next log message is not issued
  443. break
  444. else:
  445. log.warn(
  446. 'The tools \'dmidecode\', \'lspci\' and \'dmesg\' failed to '
  447. 'execute because they do not exist on the system of the user '
  448. 'running this instance or the user does not have the necessary '
  449. 'permissions to execute them. Grains output might not be accurate.'
  450. )
  451. choices = ('Linux', 'OpenBSD', 'HP-UX')
  452. isdir = os.path.isdir
  453. sysctl = salt.utils.which('sysctl')
  454. if osdata['kernel'] in choices:
  455. if os.path.isfile('/proc/1/cgroup'):
  456. try:
  457. if ':/lxc/' in salt.utils.fopen(
  458. '/proc/1/cgroup', 'r'
  459. ).read():
  460. grains['virtual_subtype'] = 'LXC'
  461. if ':/docker/' in salt.utils.fopen(
  462. '/proc/1/cgroup', 'r'
  463. ).read():
  464. grains['virtual_subtype'] = 'Docker'
  465. except IOError:
  466. pass
  467. if isdir('/proc/vz'):
  468. if os.path.isfile('/proc/vz/version'):
  469. grains['virtual'] = 'openvzhn'
  470. elif os.path.isfile('/proc/vz/veinfo'):
  471. grains['virtual'] = 'openvzve'
  472. # Provide additional detection for OpenVZ
  473. if os.path.isfile('/proc/self/status'):
  474. with salt.utils.fopen('/proc/self/status') as status_file:
  475. for line in status_file:
  476. vz_re = re.compile(r'^envID:\s+(\d+)$')
  477. vz_match = vz_re.match(line.rstrip('\n'))
  478. if vz_match and int(vz_match.groups()[0]) != 0:
  479. grains['virtual'] = 'openvzve'
  480. elif vz_match and int(vz_match.groups()[0]) == 0:
  481. grains['virtual'] = 'openvzhn'
  482. elif isdir('/proc/sys/xen') or \
  483. isdir('/sys/bus/xen') or isdir('/proc/xen'):
  484. if os.path.isfile('/proc/xen/xsd_kva'):
  485. # Tested on CentOS 5.3 / 2.6.18-194.26.1.el5xen
  486. # Tested on CentOS 5.4 / 2.6.18-164.15.1.el5xen
  487. grains['virtual_subtype'] = 'Xen Dom0'
  488. else:
  489. if grains.get('productname', '') == 'HVM domU':
  490. # Requires dmidecode!
  491. grains['virtual_subtype'] = 'Xen HVM DomU'
  492. elif os.path.isfile('/proc/xen/capabilities') and \
  493. os.access('/proc/xen/capabilities', os.R_OK):
  494. caps = salt.utils.fopen('/proc/xen/capabilities')
  495. if 'control_d' not in caps.read():
  496. # Tested on CentOS 5.5 / 2.6.18-194.3.1.el5xen
  497. grains['virtual_subtype'] = 'Xen PV DomU'
  498. else:
  499. # Shouldn't get to this, but just in case
  500. grains['virtual_subtype'] = 'Xen Dom0'
  501. caps.close()
  502. # Tested on Fedora 10 / 2.6.27.30-170.2.82 with xen
  503. # Tested on Fedora 15 / 2.6.41.4-1 without running xen
  504. elif isdir('/sys/bus/xen'):
  505. if 'xen:' in __salt__['cmd.run']('dmesg').lower():
  506. grains['virtual_subtype'] = 'Xen PV DomU'
  507. elif os.listdir('/sys/bus/xen/drivers'):
  508. # An actual DomU will have several drivers
  509. # whereas a paravirt ops kernel will not.
  510. grains['virtual_subtype'] = 'Xen PV DomU'
  511. # If a Dom0 or DomU was detected, obviously this is xen
  512. if 'dom' in grains.get('virtual_subtype', '').lower():
  513. grains['virtual'] = 'xen'
  514. if os.path.isfile('/proc/cpuinfo'):
  515. if 'QEMU Virtual CPU' in \
  516. salt.utils.fopen('/proc/cpuinfo', 'r').read():
  517. grains['virtual'] = 'kvm'
  518. elif osdata['kernel'] == 'FreeBSD':
  519. kenv = salt.utils.which('kenv')
  520. if kenv:
  521. product = __salt__['cmd.run'](
  522. '{0} smbios.system.product'.format(kenv)
  523. )
  524. maker = __salt__['cmd.run']('{0} smbios.system.maker'.format(kenv))
  525. if product.startswith('VMware'):
  526. grains['virtual'] = 'VMware'
  527. if maker.startswith('Xen'):
  528. grains['virtual_subtype'] = '{0} {1}'.format(maker, product)
  529. grains['virtual'] = 'xen'
  530. if sysctl:
  531. model = __salt__['cmd.run']('{0} hw.model'.format(sysctl))
  532. jail = __salt__['cmd.run'](
  533. '{0} -n security.jail.jailed'.format(sysctl)
  534. )
  535. if jail == '1':
  536. grains['virtual_subtype'] = 'jail'
  537. if 'QEMU Virtual CPU' in model:
  538. grains['virtual'] = 'kvm'
  539. elif osdata['kernel'] == 'SunOS':
  540. # Check if it's a "regular" zone. (i.e. Solaris 10/11 zone)
  541. zonename = salt.utils.which('zonename')
  542. if zonename:
  543. zone = __salt__['cmd.run']('{0}'.format(zonename))
  544. if zone != 'global':
  545. grains['virtual'] = 'zone'
  546. if osdata['os'] == 'SmartOS':
  547. grains.update(_smartos_zone_data())
  548. # Check if it's a branded zone (i.e. Solaris 8/9 zone)
  549. if isdir('/.SUNWnative'):
  550. grains['virtual'] = 'zone'
  551. elif osdata['kernel'] == 'NetBSD':
  552. if sysctl:
  553. if 'QEMU Virtual CPU' in __salt__['cmd.run'](
  554. '{0} -n machdep.cpu_brand'.format(sysctl)):
  555. grains['virtual'] = 'kvm'
  556. elif 'invalid' not in __salt__['cmd.run'](
  557. '{0} -n machdep.xen.suspend'.format(sysctl)):
  558. grains['virtual'] = 'Xen PV DomU'
  559. elif 'VMware' in __salt__['cmd.run'](
  560. '{0} -n machdep.dmi.system-vendor'.format(sysctl)):
  561. grains['virtual'] = 'VMware'
  562. # NetBSD has Xen dom0 support
  563. elif __salt__['cmd.run'](
  564. '{0} -n machdep.idle-mechanism'.format(sysctl)) == 'xen':
  565. if os.path.isfile('/var/run/xenconsoled.pid'):
  566. grains['virtual_subtype'] = 'Xen Dom0'
  567. return grains
  568. def _ps(osdata):
  569. '''
  570. Return the ps grain
  571. '''
  572. grains = {}
  573. bsd_choices = ('FreeBSD', 'NetBSD', 'OpenBSD', 'MacOS')
  574. if osdata['os'] in bsd_choices:
  575. grains['ps'] = 'ps auxwww'
  576. elif osdata['os_family'] == 'Solaris':
  577. grains['ps'] = '/usr/ucb/ps auxwww'
  578. elif osdata['os'] == 'Windows':
  579. grains['ps'] = 'tasklist.exe'
  580. elif osdata.get('virtual', '') == 'openvzhn':
  581. grains['ps'] = (
  582. 'ps -fH -p $(grep -l \"^envID:[[:space:]]*0\\$\" '
  583. '/proc/[0-9]*/status | sed -e \"s=/proc/\\([0-9]*\\)/.*=\\1=\") '
  584. '| awk \'{ $7=\"\"; print }\''
  585. )
  586. elif osdata['os_family'] == 'Debian':
  587. grains['ps'] = 'ps -efHww'
  588. else:
  589. grains['ps'] = 'ps -efH'
  590. return grains
  591. def _windows_platform_data():
  592. '''
  593. Use the platform module for as much as we can.
  594. '''
  595. # Provides:
  596. # osmanufacturer
  597. # manufacturer
  598. # productname
  599. # biosversion
  600. # osfullname
  601. # timezone
  602. # windowsdomain
  603. if not HAS_WMI:
  604. return {}
  605. with salt.utils.winapi.Com():
  606. wmi_c = wmi.WMI()
  607. # http://msdn.microsoft.com/en-us/library/windows/desktop/aa394102%28v=vs.85%29.aspx
  608. systeminfo = wmi_c.Win32_ComputerSystem()[0]
  609. # http://msdn.microsoft.com/en-us/library/windows/desktop/aa394239%28v=vs.85%29.aspx
  610. osinfo = wmi_c.Win32_OperatingSystem()[0]
  611. # http://msdn.microsoft.com/en-us/library/windows/desktop/aa394077(v=vs.85).aspx
  612. biosinfo = wmi_c.Win32_BIOS()[0]
  613. # http://msdn.microsoft.com/en-us/library/windows/desktop/aa394498(v=vs.85).aspx
  614. timeinfo = wmi_c.Win32_TimeZone()[0]
  615. # the name of the OS comes with a bunch of other data about the install
  616. # location. For example:
  617. # 'Microsoft Windows Server 2008 R2 Standard |C:\\Windows|\\Device\\Harddisk0\\Partition2'
  618. (osfullname, _) = osinfo.Name.split('|', 1)
  619. osfullname = osfullname.strip()
  620. grains = {
  621. 'osmanufacturer': osinfo.Manufacturer,
  622. 'manufacturer': systeminfo.Manufacturer,
  623. 'productname': systeminfo.Model,
  624. # bios name had a bunch of whitespace appended to it in my testing
  625. # 'PhoenixBIOS 4.0 Release 6.0 '
  626. 'biosversion': biosinfo.Name.strip(),
  627. 'osfullname': osfullname,
  628. 'timezone': timeinfo.Description,
  629. 'windowsdomain': systeminfo.Domain,
  630. }
  631. # test for virtualized environments
  632. # I only had VMware available so the rest are unvalidated
  633. if 'VRTUAL' in biosinfo.Version: # (not a typo)
  634. grains['virtual'] = 'HyperV'
  635. elif 'A M I' in biosinfo.Version:
  636. grains['virtual'] = 'VirtualPC'
  637. elif 'VMware' in systeminfo.Model:
  638. grains['virtual'] = 'VMware'
  639. elif 'VirtualBox' in systeminfo.Model:
  640. grains['virtual'] = 'VirtualBox'
  641. elif 'Xen' in biosinfo.Version:
  642. grains['virtual'] = 'Xen'
  643. if 'HVM domU' in systeminfo.Model:
  644. grains['virtual_subtype'] = 'HVM domU'
  645. return grains
  646. def id_():
  647. '''
  648. Return the id
  649. '''
  650. return {'id': __opts__.get('id', '')}
  651. _REPLACE_LINUX_RE = re.compile(r'linux', re.IGNORECASE)
  652. # This maps (at most) the first ten characters (no spaces, lowercased) of
  653. # 'osfullname' to the 'os' grain that Salt traditionally uses.
  654. # Please see os_data() and _supported_dists.
  655. # If your system is not detecting properly it likely needs an entry here.
  656. _OS_NAME_MAP = {
  657. 'redhatente': 'RedHat',
  658. 'gentoobase': 'Gentoo',
  659. 'archarm': 'Arch ARM',
  660. 'arch': 'Arch',
  661. 'debian': 'Debian',
  662. 'debiangnu/': 'Debian',
  663. 'raspbiangn': 'Raspbian',
  664. 'fedoraremi': 'Fedora',
  665. 'amazonami': 'Amazon',
  666. 'alt': 'ALT',
  667. 'enterprise': 'OEL',
  668. 'oracleserv': 'OEL',
  669. 'cloudserve': 'CloudLinux',
  670. 'pidora': 'Fedora',
  671. 'scientific': 'ScientificLinux'
  672. }
  673. # Map the 'os' grain to the 'os_family' grain
  674. # These should always be capitalized entries as the lookup comes
  675. # post-_OS_NAME_MAP. If your system is having trouble with detection, please
  676. # make sure that the 'os' grain is capitalized and working correctly first.
  677. _OS_FAMILY_MAP = {
  678. 'Ubuntu': 'Debian',
  679. 'Fedora': 'RedHat',
  680. 'CentOS': 'RedHat',
  681. 'GoOSe': 'RedHat',
  682. 'Scientific': 'RedHat',
  683. 'Amazon': 'RedHat',
  684. 'CloudLinux': 'RedHat',
  685. 'OVS': 'RedHat',
  686. 'OEL': 'RedHat',
  687. 'XCP': 'RedHat',
  688. 'XenServer': 'RedHat',
  689. 'Mandrake': 'Mandriva',
  690. 'ESXi': 'VMWare',
  691. 'Mint': 'Debian',
  692. 'VMWareESX': 'VMWare',
  693. 'Bluewhite64': 'Bluewhite',
  694. 'Slamd64': 'Slackware',
  695. 'SLES': 'Suse',
  696. 'SUSE Enterprise Server': 'Suse',
  697. 'SUSE Enterprise Server': 'Suse',
  698. 'SLED': 'Suse',
  699. 'openSUSE': 'Suse',
  700. 'SUSE': 'Suse',
  701. 'Solaris': 'Solaris',
  702. 'SmartOS': 'Solaris',
  703. 'OpenIndiana Development': 'Solaris',
  704. 'OpenIndiana': 'Solaris',
  705. 'OpenSolaris Development': 'Solaris',
  706. 'OpenSolaris': 'Solaris',
  707. 'Arch ARM': 'Arch',
  708. 'ALT': 'RedHat',
  709. 'Trisquel': 'Debian',
  710. 'GCEL': 'Debian',
  711. 'Linaro': 'Debian',
  712. 'elementary OS': 'Debian',
  713. 'ScientificLinux': 'RedHat',
  714. 'Raspbian': 'Debian'
  715. }
  716. def _linux_bin_exists(binary):
  717. '''
  718. Does a binary exist in linux (depends on which)
  719. '''
  720. return __salt__['cmd.retcode'](
  721. 'which {0}'.format(binary)
  722. ) == 0
  723. def os_data():
  724. '''
  725. Return grains pertaining to the operating system
  726. '''
  727. grains = {
  728. 'num_gpus': 0,
  729. 'gpus': [],
  730. }
  731. # Windows Server 2008 64-bit
  732. # ('Windows', 'MINIONNAME', '2008ServerR2', '6.1.7601', 'AMD64',
  733. # 'Intel64 Fam ily 6 Model 23 Stepping 6, GenuineIntel')
  734. # Ubuntu 10.04
  735. # ('Linux', 'MINIONNAME', '2.6.32-38-server',
  736. # '#83-Ubuntu SMP Wed Jan 4 11:26:59 UTC 2012', 'x86_64', '')
  737. # pylint: disable=unpacking-non-sequence
  738. (grains['kernel'], grains['nodename'],
  739. grains['kernelrelease'], version, grains['cpuarch'], _) = platform.uname()
  740. # pylint: enable=unpacking-non-sequence
  741. if salt.utils.is_windows():
  742. grains['osrelease'] = grains['kernelrelease']
  743. grains['osversion'] = grains['kernelrelease'] = version
  744. grains['os'] = 'Windows'
  745. grains['os_family'] = 'Windows'
  746. grains.update(_memdata(grains))
  747. grains.update(_windows_platform_data())
  748. grains.update(_windows_cpudata())
  749. grains.update(_ps(grains))
  750. return grains
  751. elif salt.utils.is_linux():
  752. # Add SELinux grain, if you have it
  753. if _linux_bin_exists('selinuxenabled'):
  754. grains['selinux'] = {}
  755. grains['selinux']['enabled'] = __salt__['cmd.run'](
  756. 'selinuxenabled; echo $?'
  757. ).strip() == '0'
  758. if _linux_bin_exists('getenforce'):
  759. grains['selinux']['enforced'] = __salt__['cmd.run'](
  760. 'getenforce'
  761. ).strip()
  762. # Add lsb grains on any distro with lsb-release
  763. try:
  764. import lsb_release
  765. release = lsb_release.get_distro_information()
  766. for key, value in release.iteritems():
  767. key = key.lower()
  768. lsb_param = 'lsb_{0}{1}'.format(
  769. '' if key.startswith('distrib_') else 'distrib_',
  770. key
  771. )
  772. grains[lsb_param] = value
  773. except ImportError:
  774. # if the python library isn't available, default to regex
  775. if os.path.isfile('/etc/lsb-release'):
  776. # Matches any possible format:
  777. # DISTRIB_ID="Ubuntu"
  778. # DISTRIB_ID='Mageia'
  779. # DISTRIB_ID=Fedora
  780. # DISTRIB_RELEASE='10.10'
  781. # DISTRIB_CODENAME='squeeze'
  782. # DISTRIB_DESCRIPTION='Ubuntu 10.10'
  783. regex = re.compile((
  784. '^(DISTRIB_(?:ID|RELEASE|CODENAME|DESCRIPTION))=(?:\'|")?'
  785. '([\\w\\s\\.-_]+)(?:\'|")?'
  786. ))
  787. with salt.utils.fopen('/etc/lsb-release') as ifile:
  788. for line in ifile:
  789. match = regex.match(line.rstrip('\n'))
  790. if match:
  791. # Adds:
  792. # lsb_distrib_{id,release,codename,description}
  793. grains[
  794. 'lsb_{0}'.format(match.groups()[0].lower())
  795. ] = match.groups()[1].rstrip()
  796. elif os.path.isfile('/etc/os-release'):
  797. # Arch ARM Linux
  798. with salt.utils.fopen('/etc/os-release') as ifile:
  799. # Imitate lsb-release
  800. for line in ifile:
  801. # NAME="Arch Linux ARM"
  802. # ID=archarm
  803. # ID_LIKE=arch
  804. # PRETTY_NAME="Arch Linux ARM"
  805. # ANSI_COLOR="0;36"
  806. # HOME_URL="http://archlinuxarm.org/"
  807. # SUPPORT_URL="https://archlinuxarm.org/forum"
  808. # BUG_REPORT_URL=
  809. # "https://github.com/archlinuxarm/PKGBUILDs/issues"
  810. regex = re.compile(
  811. '^([\\w]+)=(?:\'|")?([\\w\\s\\.-_]+)(?:\'|")?'
  812. )
  813. match = regex.match(line.rstrip('\n'))
  814. if match:
  815. name, value = match.groups()
  816. if name.lower() == 'name':
  817. grains['lsb_distrib_id'] = value.strip()
  818. elif os.path.isfile('/etc/SuSE-release'):
  819. grains['lsb_distrib_id'] = 'SUSE'
  820. rel = open('/etc/SuSE-release').read().split('\n')[1]
  821. patch = open('/etc/SuSE-release').read().split('\n')[2]
  822. rel = re.sub("[^0-9]", "", rel)
  823. patch = re.sub("[^0-9]", "", patch)
  824. release = rel + " SP" + patch
  825. grains['lsb_distrib_release'] = release
  826. grains['lsb_distrib_codename'] = "n.a"
  827. elif os.path.isfile('/etc/altlinux-release'):
  828. # ALT Linux
  829. grains['lsb_distrib_id'] = 'altlinux'
  830. with salt.utils.fopen('/etc/altlinux-release') as ifile:
  831. # This file is symlinked to from:
  832. # /etc/fedora-release
  833. # /etc/redhat-release
  834. # /etc/system-release
  835. for line in ifile:
  836. # ALT Linux Sisyphus (unstable)
  837. comps = line.split()
  838. if comps[0] == 'ALT':
  839. grains['lsb_distrib_release'] = comps[2]
  840. grains['lsb_distrib_codename'] = \
  841. comps[3].replace('(', '').replace(')', '')
  842. elif os.path.isfile('/etc/centos-release'):
  843. # CentOS Linux
  844. grains['lsb_distrib_id'] = 'CentOS'
  845. with salt.utils.fopen('/etc/centos-release') as ifile:
  846. for line in ifile:
  847. # Need to pull out the version and codename
  848. # in the case of custom content in /etc/centos-release
  849. find_release = re.compile(r'\d+\.\d+')
  850. find_codename = re.compile(r'(?<=\()(.*?)(?=\))')
  851. release = find_release.search(line)
  852. codename = find_codename.search(line)
  853. if release is not None:
  854. grains['lsb_distrib_release'] = release.group()
  855. if codename is not None:
  856. grains['lsb_distrib_codename'] = codename.group()
  857. # Use the already intelligent platform module to get distro info
  858. # (though apparently it's not intelligent enough to strip quotes)
  859. (osname, osrelease, oscodename) = \
  860. [x.strip('"').strip("'") for x in
  861. platform.linux_distribution(supported_dists=_supported_dists)]
  862. # Try to assign these three names based on the lsb info, they tend to
  863. # be more accurate than what python gets from /etc/DISTRO-release.
  864. # It's worth noting that Ubuntu has patched their Python distribution
  865. # so that platform.linux_distribution() does the /etc/lsb-release
  866. # parsing, but we do it anyway here for the sake for full portability.
  867. grains['osfullname'] = grains.get('lsb_distrib_id', osname).strip()
  868. grains['osrelease'] = grains.get('lsb_distrib_release',
  869. osrelease).strip()
  870. grains['oscodename'] = grains.get('lsb_distrib_codename',
  871. oscodename).strip()
  872. distroname = _REPLACE_LINUX_RE.sub('', grains['osfullname']).strip()
  873. # return the first ten characters with no spaces, lowercased
  874. shortname = distroname.replace(' ', '').lower()[:10]
  875. # this maps the long names from the /etc/DISTRO-release files to the
  876. # traditional short names that Salt has used.
  877. grains['os'] = _OS_NAME_MAP.get(shortname, distroname)
  878. grains.update(_linux_cpudata())
  879. grains.update(_linux_gpu_data())
  880. elif grains['kernel'] == 'SunOS':
  881. grains['os_family'] = 'Solaris'
  882. uname_v = __salt__['cmd.run']('uname -v')
  883. if 'joyent_' in uname_v:
  884. # See https://github.com/joyent/smartos-live/issues/224
  885. grains['os'] = grains['osfullname'] = 'SmartOS'
  886. grains['osrelease'] = uname_v
  887. elif os.path.isfile('/etc/release'):
  888. with salt.utils.fopen('/etc/release', 'r') as fp_:
  889. rel_data = fp_.read()
  890. try:
  891. release_re = re.compile(
  892. r'((?:Open)?Solaris|OpenIndiana) (Development)?'
  893. r'\s*(\d+ \d+\/\d+|oi_\S+|snv_\S+)?'
  894. )
  895. osname, development, osrelease = \
  896. release_re.search(rel_data).groups()
  897. except AttributeError:
  898. # Set a blank osrelease grain and fallback to 'Solaris'
  899. # as the 'os' grain.
  900. grains['os'] = grains['osfullname'] = 'Solaris'
  901. grains['osrelease'] = ''
  902. else:
  903. if development is not None:
  904. osname = ' '.join((osname, development))
  905. grains['os'] = grains['osfullname'] = osname
  906. grains['osrelease'] = osrelease
  907. grains.update(_sunos_cpudata())
  908. elif grains['kernel'] == 'VMkernel':
  909. grains['os'] = 'ESXi'
  910. elif grains['kernel'] == 'Darwin':
  911. osrelease = __salt__['cmd.run']('sw_vers -productVersion')
  912. grains['os'] = 'MacOS'
  913. grains['osrelease'] = osrelease
  914. grains.update(_bsd_cpudata(grains))
  915. grains.update(_osx_gpudata())
  916. else:
  917. grains['os'] = grains['kernel']
  918. if grains['kernel'] in ('FreeBSD', 'OpenBSD', 'NetBSD'):
  919. grains.update(_bsd_cpudata(grains))
  920. grains['osrelease'] = grains['kernelrelease'].split('-')[0]
  921. if grains['kernel'] == 'NetBSD':
  922. grains.update(_netbsd_gpu_data())
  923. if not grains['os']:
  924. grains['os'] = 'Unknown {0}'.format(grains['kernel'])
  925. grains['os_family'] = 'Unknown'
  926. else:
  927. # this assigns family names based on the os name
  928. # family defaults to the os name if not found
  929. grains['os_family'] = _OS_FAMILY_MAP.get(grains['os'],
  930. grains['os'])
  931. # Build the osarch grain. This grain will be used for platform-specific
  932. # considerations such as package management. Fall back to the CPU
  933. # architecture.
  934. if grains.get('os_family') == 'Debian':
  935. osarch = __salt__['cmd.run']('dpkg --print-architecture').strip()
  936. else:
  937. osarch = grains['cpuarch']
  938. grains['osarch'] = osarch
  939. grains.update(_memdata(grains))
  940. # Get the hardware and bios data
  941. grains.update(_hw_data(grains))
  942. # Load the virtual machine info
  943. grains.update(_virtual(grains))
  944. grains.update(_ps(grains))
  945. # Load additional OS family grains
  946. if grains['os_family'] == "RedHat":
  947. grains['osmajorrelease'] = grains['osrelease'].split('.', 1)
  948. grains['osfinger'] = '{os}-{ver}'.format(
  949. os=grains['osfullname'],
  950. ver=grains['osrelease'].partition('.')[0])
  951. elif grains.get('osfullname') == 'Ubuntu':
  952. grains['osfinger'] = '{os}-{ver}'.format(
  953. os=grains['osfullname'],
  954. ver=grains['osrelease'])
  955. return grains
  956. def locale_info():
  957. '''
  958. Provides
  959. defaultlanguage
  960. defaultencoding
  961. '''
  962. grains = {}
  963. if 'proxyminion' in __opts__:
  964. return grains
  965. try:
  966. (
  967. grains['defaultlanguage'],
  968. grains['defaultencoding']
  969. ) = locale.getdefaultlocale()
  970. except Exception:
  971. # locale.getdefaultlocale can ValueError!! Catch anything else it
  972. # might do, per #2205
  973. grains['defaultlanguage'] = 'unknown'
  974. grains['defaultencoding'] = 'unknown'
  975. return grains
  976. def hostname():
  977. '''
  978. Return fqdn, hostname, domainname
  979. '''
  980. # This is going to need some work
  981. # Provides:
  982. # fqdn
  983. # host
  984. # localhost
  985. # domain
  986. grains = {}
  987. if 'proxyminion' in __opts__:
  988. return grains
  989. grains['localhost'] = socket.gethostname()
  990. grains['fqdn'] = salt.utils.network.get_fqhostname()
  991. (grains['host'], grains['domain']) = grains['fqdn'].partition('.')[::2]
  992. return grains
  993. def append_domain():
  994. '''
  995. Return append_domain if set
  996. '''
  997. grain = {}
  998. if 'proxyminion' in __opts__:
  999. return grain
  1000. if 'append_domain' in __opts__:
  1001. grain['append_domain'] = __opts__['append_domain']
  1002. return grain
  1003. def ip4():
  1004. '''
  1005. Return a list of ipv4 addrs
  1006. '''
  1007. if 'proxyminion' in __opts__:
  1008. return {}
  1009. return {'ipv4': salt.utils.network.ip_addrs(include_loopback=True)}
  1010. def fqdn_ip4():
  1011. '''
  1012. Return a list of ipv4 addrs of fqdn
  1013. '''
  1014. if 'proxyminion' in __opts__:
  1015. return {}
  1016. try:
  1017. info = socket.getaddrinfo(hostname()['fqdn'], None, socket.AF_INET)
  1018. addrs = list(set(item[4][0] for item in info))
  1019. except socket.error:
  1020. addrs = []
  1021. return {'fqdn_ip4': addrs}
  1022. def ip6():
  1023. '''
  1024. Return a list of ipv6 addrs
  1025. '''
  1026. if 'proxyminion' in __opts__:
  1027. return {}
  1028. return {'ipv6': salt.utils.network.ip_addrs6(include_loopback=True)}
  1029. def fqdn_ip6():
  1030. '''
  1031. Return a list of ipv6 addrs of fqdn
  1032. '''
  1033. if 'proxyminion' in __opts__:
  1034. return {}
  1035. try:
  1036. info = socket.getaddrinfo(hostname()['fqdn'], None, socket.AF_INET6)
  1037. addrs = list(set(item[4][0] for item in info))
  1038. except socket.error:
  1039. addrs = []
  1040. return {'fqdn_ip6': addrs}
  1041. def ip_interfaces():
  1042. '''
  1043. Provide a dict of the connected interfaces and their ip addresses
  1044. '''
  1045. # Provides:
  1046. # ip_interfaces
  1047. if 'proxyminion' in __opts__:
  1048. return {}
  1049. ret = {}
  1050. ifaces = salt.utils.network.interfaces()
  1051. for face in ifaces:
  1052. iface_ips = []
  1053. for inet in ifaces[face].get('inet', []):
  1054. if 'address' in inet:
  1055. iface_ips.append(inet['address'])
  1056. for secondary in ifaces[face].get('secondary', []):
  1057. if 'address' in secondary:
  1058. iface_ips.append(secondary['address'])
  1059. ret[face] = iface_ips
  1060. return {'ip_interfaces': ret}
  1061. def hwaddr_interfaces():
  1062. '''
  1063. Provide a dict of the connected interfaces and their
  1064. hw addresses (Mac Address)
  1065. '''
  1066. # Provides:
  1067. # hwaddr_interfaces
  1068. ret = {}
  1069. ifaces = salt.utils.network.interfaces()
  1070. for face in ifaces:
  1071. if 'hwaddr' in ifaces[face]:
  1072. ret[face] = ifaces[face]['hwaddr']
  1073. return {'hwaddr_interfaces': ret}
  1074. def get_machine_id():
  1075. '''
  1076. Provide the machine-id
  1077. '''
  1078. # Provides:
  1079. # machine-id
  1080. locations = ['/etc/machine-id', '/var/lib/dbus/machine-id']
  1081. existing_locations = [loc for loc in locations if os.path.exists(loc)]
  1082. if not existing_locations:
  1083. return {}
  1084. else:
  1085. with salt.utils.fopen(existing_locations[0]) as machineid:
  1086. return {'machine_id': machineid.read().strip()}
  1087. def path():
  1088. '''
  1089. Return the path
  1090. '''
  1091. # Provides:
  1092. # path
  1093. return {'path': os.environ['PATH'].strip()}
  1094. def pythonversion():
  1095. '''
  1096. Return the Python version
  1097. '''
  1098. # Provides:
  1099. # pythonversion
  1100. return {'pythonversion': list(sys.version_info)}
  1101. def pythonpath():
  1102. '''
  1103. Return the Python path
  1104. '''
  1105. # Provides:
  1106. # pythonpath
  1107. return {'pythonpath': sys.path}
  1108. def pythonexecutable():
  1109. '''
  1110. Return the python executable in use
  1111. '''
  1112. # Provides:
  1113. # pythonexecutable
  1114. return {'pythonexecutable': sys.executable}
  1115. def saltpath():
  1116. '''
  1117. Return the path of the salt module
  1118. '''
  1119. # Provides:
  1120. # saltpath
  1121. salt_path = os.path.abspath(os.path.join(__file__, os.path.pardir))
  1122. return {'saltpath': os.path.dirname(salt_path)}
  1123. def saltversion():
  1124. '''
  1125. Return the version of salt
  1126. '''
  1127. # Provides:
  1128. # saltversion
  1129. from salt.version import __version__
  1130. return {'saltversion': __version__}
  1131. def zmqversion():
  1132. '''
  1133. Return the zeromq version
  1134. '''
  1135. # Provides:
  1136. # zmqversion
  1137. try:
  1138. import zmq
  1139. return {'zmqversion': zmq.zmq_version()}
  1140. except ImportError:
  1141. return {}
  1142. def saltversioninfo():
  1143. '''
  1144. Return the version_info of salt
  1145. .. versionadded:: 0.17.0
  1146. '''
  1147. # Provides:
  1148. # saltversioninfo
  1149. from salt.version import __version_info__
  1150. return {'saltversioninfo': __version_info__}
  1151. # Relatively complex mini-algorithm to iterate over the various
  1152. # sections of dmidecode output and return matches for specific
  1153. # lines containing data we want, but only in the right section.
  1154. def _dmidecode_data(regex_dict):
  1155. '''
  1156. Parse the output of dmidecode in a generic fashion that can
  1157. be used for the multiple system types which have dmidecode.
  1158. '''
  1159. ret = {}
  1160. if 'proxyminion' in __opts__:
  1161. return {}
  1162. # No use running if dmidecode/smbios isn't in the path
  1163. if salt.utils.which('dmidecode'):
  1164. out = __salt__['cmd.run']('dmidecode')
  1165. elif salt.utils.which('smbios'):
  1166. out = __salt__['cmd.run']('smbios')
  1167. else:
  1168. log.debug(
  1169. 'The `dmidecode` binary is not available on the system. GPU '
  1170. 'grains will not be available.'
  1171. )
  1172. return ret
  1173. for section in regex_dict:
  1174. section_found = False
  1175. # Look at every line for the right section
  1176. for line in out.splitlines():
  1177. if not line:
  1178. continue
  1179. # We've found it, woohoo!
  1180. if re.match(section, line):
  1181. section_found = True
  1182. continue
  1183. if not section_found:
  1184. continue
  1185. # Now that a section has been found, find the data
  1186. for item in regex_dict[section]:
  1187. # Examples:
  1188. # Product Name: 64639SU
  1189. # Version: 7LETC1WW (2.21 )
  1190. regex = re.compile(r'\s+{0}\s+(.*)$'.format(item))
  1191. grain = regex_dict[section][item]
  1192. # Skip to the next iteration if this grain
  1193. # has been found in the dmidecode output.
  1194. if grain in ret:
  1195. continue
  1196. match = regex.match(line)
  1197. # Finally, add the matched data to the grains returned
  1198. if match:
  1199. ret[grain] = match.group(1).strip()
  1200. return ret
  1201. def _hw_data(osdata):
  1202. '''
  1203. Get system specific hardware data from dmidecode
  1204. Provides
  1205. biosversion
  1206. productname
  1207. manufacturer
  1208. serialnumber
  1209. biosreleasedate
  1210. .. versionadded:: 0.9.5
  1211. '''
  1212. if 'proxyminion' in __opts__:
  1213. return {}
  1214. grains = {}
  1215. # TODO: *BSD dmidecode output
  1216. if osdata['kernel'] == 'Linux':
  1217. linux_dmi_regex = {
  1218. 'BIOS [Ii]nformation': {
  1219. '[Vv]ersion:': 'biosversion',
  1220. '[Rr]elease [Dd]ate:': 'biosreleasedate',
  1221. },
  1222. '[Ss]ystem [Ii]nformation': {
  1223. 'Manufacturer:': 'manufacturer',
  1224. 'Product(?: Name)?:': 'productname',
  1225. 'Serial Number:': 'serialnumber',
  1226. },
  1227. }
  1228. grains.update(_dmidecode_data(linux_dmi_regex))
  1229. elif osdata['kernel'] == 'SunOS':
  1230. sunos_dmi_regex = {
  1231. r'(.+)SMB_TYPE_BIOS\s\(BIOS [Ii]nformation\)': {
  1232. '[Vv]ersion [Ss]tring:': 'biosversion',
  1233. '[Rr]elease [Dd]ate:': 'biosreleasedate',
  1234. },
  1235. r'(.+)SMB_TYPE_SYSTEM\s\([Ss]ystem [Ii]nformation\)': {
  1236. 'Manufacturer:': 'manufacturer',
  1237. 'Product(?: Name)?:': 'productname',
  1238. 'Serial Number:': 'serialnumber',
  1239. },
  1240. }
  1241. grains.update(_dmidecode_data(sunos_dmi_regex))
  1242. # On FreeBSD /bin/kenv (already in base system)
  1243. # can be used instead of dmidecode
  1244. elif osdata['kernel'] == 'FreeBSD':
  1245. kenv = salt.utils.which('kenv')
  1246. if kenv:
  1247. # In theory, it will be easier to add new fields to this later
  1248. fbsd_hwdata = {
  1249. 'biosversion': 'smbios.bios.version',
  1250. 'manufacturer': 'smbios.system.maker',
  1251. 'serialnumber': 'smbios.system.serial',
  1252. 'productname': 'smbios.system.product',
  1253. 'biosreleasedate': 'smbios.bios.reldate',
  1254. }
  1255. for key, val in fbsd_hwdata.items():
  1256. grains[key] = __salt__['cmd.run']('{0} {1}'.format(kenv, val))
  1257. elif osdata['kernel'] == 'OpenBSD':
  1258. sysctl = salt.utils.which('sysctl')
  1259. hwdata = {'biosversion': 'hw.version',
  1260. 'manufacturer': 'hw.vendor',
  1261. 'productname': 'hw.product',
  1262. 'serialnumber': 'hw.serialno'}
  1263. for key, oid in hwdata.items():
  1264. value = __salt__['cmd.run']('{0} -n {1}'.format(sysctl, oid))
  1265. if not value.endswith(' value is not available'):
  1266. grains[key] = value
  1267. elif osdata['kernel'] == 'NetBSD':
  1268. sysctl = salt.utils.which('sysctl')
  1269. nbsd_hwdata = {

Large files files are truncated, but you can click here to view the full file