diff --git a/salt/grains/core.py b/salt/grains/core.py index 2c64710a2d..b9ee7918f9 100644 --- a/salt/grains/core.py +++ b/salt/grains/core.py @@ -33,6 +33,8 @@ _supported_dists += ('arch', 'mageia', 'meego', 'vmware', 'bluewhite64', import salt.log import salt.utils import salt.utils.network +if salt.utils.is_windows(): + import salt.utils.win_osinfo # Solve the Chicken and egg problem where grains need to run before any # of the modules are loaded and are generally available for any usage. @@ -821,6 +823,10 @@ def _windows_platform_data(): Use the platform module for as much as we can. ''' # Provides: + # kernelrelease + # osversion + # osrelease + # osservicepack # osmanufacturer # manufacturer # productname @@ -831,6 +837,7 @@ def _windows_platform_data(): # windowsdomain # motherboard.productname # motherboard.serialnumber + # virtual if not HAS_WMI: return {} @@ -839,7 +846,7 @@ def _windows_platform_data(): wmi_c = wmi.WMI() # http://msdn.microsoft.com/en-us/library/windows/desktop/aa394102%28v=vs.85%29.aspx systeminfo = wmi_c.Win32_ComputerSystem()[0] - # http://msdn.microsoft.com/en-us/library/windows/desktop/aa394239%28v=vs.85%29.aspx + # https://msdn.microsoft.com/en-us/library/aa394239(v=vs.85).aspx osinfo = wmi_c.Win32_OperatingSystem()[0] # http://msdn.microsoft.com/en-us/library/windows/desktop/aa394077(v=vs.85).aspx biosinfo = wmi_c.Win32_BIOS()[0] @@ -847,23 +854,49 @@ def _windows_platform_data(): timeinfo = wmi_c.Win32_TimeZone()[0] # http://msdn.microsoft.com/en-us/library/windows/desktop/aa394072(v=vs.85).aspx - motherboard = {} - motherboard['product'] = None - motherboard['serial'] = None + motherboard = {'product': None, + 'serial': None} try: motherboardinfo = wmi_c.Win32_BaseBoard()[0] motherboard['product'] = motherboardinfo.Product motherboard['serial'] = motherboardinfo.SerialNumber except IndexError: - log.debug('Motherboard info not available on this sytem') + log.debug('Motherboard info not available on this system') - # the name of the OS comes with a bunch of other data about the install - # location. For example: - # 'Microsoft Windows Server 2008 R2 Standard |C:\\Windows|\\Device\\Harddisk0\\Partition2' - (osfullname, _) = osinfo.Name.split('|', 1) - osfullname = osfullname.strip() + os_release = platform.release() + info = salt.utils.win_osinfo.get_os_version_info() + + # Starting with Python 2.7.12 and 3.5.2 the `platform.uname()` function + # started reporting the Desktop version instead of the Server version on + # Server versions of Windows, so we need to look those up + # Check for Python >=2.7.12 or >=3.5.2 + ver = pythonversion()['pythonversion'] + if ((six.PY2 and + salt.utils.compare_versions(ver, '>=', [2, 7, 12, 'final', 0])) + or + (six.PY3 and + salt.utils.compare_versions(ver, '>=', [3, 5, 2, 'final', 0]))): + # (Product Type 1 is Desktop, Everything else is Server) + if info['ProductType'] > 1: + server = {'Vista': '2008Server', + '7': '2008ServerR2', + '8': '2012Server', + '8.1': '2012ServerR2', + '10': '2016Server'} + os_release = server.get(os_release, + 'Grain not found. Update lookup table ' + 'in the `_windows_platform_data` ' + 'function in `grains\\core.py`') + + service_pack = None + if info['ServicePackMajor'] > 0: + service_pack = ''.join(['SP', str(info['ServicePackMajor'])]) grains = { + 'kernelrelease': osinfo.Version, + 'osversion': osinfo.Version, + 'osrelease': os_release, + 'osservicepack': service_pack, 'osmanufacturer': osinfo.Manufacturer, 'manufacturer': systeminfo.Manufacturer, 'productname': systeminfo.Model, @@ -871,12 +904,12 @@ def _windows_platform_data(): # 'PhoenixBIOS 4.0 Release 6.0 ' 'biosversion': biosinfo.Name.strip(), 'serialnumber': biosinfo.SerialNumber, - 'osfullname': osfullname, + 'osfullname': osinfo.Caption, 'timezone': timeinfo.Description, 'windowsdomain': systeminfo.Domain, 'motherboard': { 'productname': motherboard['product'], - 'serialnumber': motherboard['serial'] + 'serialnumber': motherboard['serial'], } } @@ -1067,10 +1100,6 @@ def os_data(): grains['os'] = 'proxy' grains['os_family'] = 'proxy' elif salt.utils.is_windows(): - with salt.utils.winapi.Com(): - wmi_c = wmi.WMI() - grains['osrelease'] = grains['kernelrelease'] - grains['osversion'] = grains['kernelrelease'] = wmi_c.Win32_OperatingSystem()[0].Version grains['os'] = 'Windows' grains['os_family'] = 'Windows' grains.update(_memdata(grains)) diff --git a/salt/utils/win_osinfo.py b/salt/utils/win_osinfo.py new file mode 100644 index 0000000000..7ad789c2df --- /dev/null +++ b/salt/utils/win_osinfo.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +''' +Get Versioning information from Windows +''' +# http://stackoverflow.com/questions/32300004/python-ctypes-getting-0-with-getversionex-function +from __future__ import absolute_import + +import ctypes +from ctypes.wintypes import BYTE, WORD, DWORD, WCHAR + +kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) + + +class OSVERSIONINFO(ctypes.Structure): + _fields_ = (('dwOSVersionInfoSize', DWORD), + ('dwMajorVersion', DWORD), + ('dwMinorVersion', DWORD), + ('dwBuildNumber', DWORD), + ('dwPlatformId', DWORD), + ('szCSDVersion', WCHAR * 128)) + + def __init__(self, *args, **kwds): + super(OSVERSIONINFO, self).__init__(*args, **kwds) + self.dwOSVersionInfoSize = ctypes.sizeof(self) + kernel32.GetVersionExW(ctypes.byref(self)) + + +class OSVERSIONINFOEX(OSVERSIONINFO): + _fields_ = (('wServicePackMajor', WORD), + ('wServicePackMinor', WORD), + ('wSuiteMask', WORD), + ('wProductType', BYTE), + ('wReserved', BYTE)) + + +def errcheck_bool(result, func, args): + if not result: + raise ctypes.WinError(ctypes.get_last_error()) + return args + +kernel32.GetVersionExW.errcheck = errcheck_bool +kernel32.GetVersionExW.argtypes = (ctypes.POINTER(OSVERSIONINFO),) + + +def get_os_version_info(): + info = OSVERSIONINFOEX() + ret = {'MajorVersion': info.dwMajorVersion, + 'MinorVersion': info.dwMinorVersion, + 'BuildNumber': info.dwBuildNumber, + 'PlatformID': info.dwPlatformId, + 'ServicePackMajor': info.wServicePackMajor, + 'ServicePackMinor': info.wServicePackMinor, + 'SuiteMask': info.wSuiteMask, + 'ProductType': info.wProductType} + + return ret