PageRenderTime 606ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/salt/grains/core.py

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