docs: def os_data():
      '''
      Return grains pertaining to the operating system
      '''
      grains = {
          'num_gpus': 0,
          'gpus': [],
          }
  
      # Windows Server 2008 64-bit
      # ('Windows', 'MINIONNAME', '2008ServerR2', '6.1.7601', 'AMD64',
      #  'Intel64 Fam ily 6 Model 23 Stepping 6, GenuineIntel')
      # Ubuntu 10.04
      # ('Linux', 'MINIONNAME', '2.6.32-38-server',
      # '#83-Ubuntu SMP Wed Jan 4 11:26:59 UTC 2012', 'x86_64', '')
  
      # pylint: disable=unpacking-non-sequence
      (grains['kernel'], grains['nodename'],
       grains['kernelrelease'], grains['kernelversion'], grains['cpuarch'], _) = platform.uname()
      # pylint: enable=unpacking-non-sequence
  
      if salt.utils.platform.is_proxy():
          grains['kernel'] = 'proxy'
          grains['kernelrelease'] = 'proxy'
          grains['kernelversion'] = 'proxy'
          grains['osrelease'] = 'proxy'
          grains['os'] = 'proxy'
          grains['os_family'] = 'proxy'
          grains['osfullname'] = 'proxy'
      elif salt.utils.platform.is_windows():
          grains['os'] = 'Windows'
          grains['os_family'] = 'Windows'
          grains.update(_memdata(grains))
          grains.update(_windows_platform_data())
          grains.update(_windows_cpudata())
          grains.update(_windows_virtual(grains))
          grains.update(_ps(grains))
  
          if 'Server' in grains['osrelease']:
              osrelease_info = grains['osrelease'].split('Server', 1)
              osrelease_info[1] = osrelease_info[1].lstrip('R')
          else:
              osrelease_info = grains['osrelease'].split('.')
  
          for idx, value in enumerate(osrelease_info):
              if not value.isdigit():
                  continue
              osrelease_info[idx] = int(value)
          grains['osrelease_info'] = tuple(osrelease_info)
  
          grains['osfinger'] = '{os}-{ver}'.format(
              os=grains['os'],
              ver=grains['osrelease'])
  
          grains['init'] = 'Windows'
  
          return grains
      elif salt.utils.platform.is_linux():
          # Add SELinux grain, if you have it
          if _linux_bin_exists('selinuxenabled'):
              log.trace('Adding selinux grains')
              grains['selinux'] = {}
              grains['selinux']['enabled'] = __salt__['cmd.retcode'](
                  'selinuxenabled'
              ) == 0
              if _linux_bin_exists('getenforce'):
                  grains['selinux']['enforced'] = __salt__['cmd.run'](
                      'getenforce'
                  ).strip()
  
          # Add systemd grain, if you have it
          if _linux_bin_exists('systemctl') and _linux_bin_exists('localectl'):
              log.trace('Adding systemd grains')
              grains['systemd'] = {}
              systemd_info = __salt__['cmd.run'](
                  'systemctl --version'
              ).splitlines()
              grains['systemd']['version'] = systemd_info[0].split()[1]
              grains['systemd']['features'] = systemd_info[1]
  
          # Add init grain
          grains['init'] = 'unknown'
          log.trace('Adding init grain')
          try:
              os.stat('/run/systemd/system')
              grains['init'] = 'systemd'
          except (OSError, IOError):
              try:
                  with salt.utils.files.fopen('/proc/1/cmdline') as fhr:
                      init_cmdline = fhr.read().replace('\x00', ' ').split()
              except (IOError, OSError):
                  pass
              else:
                  try:
                      init_bin = salt.utils.path.which(init_cmdline[0])
                  except IndexError:
                      # Emtpy init_cmdline
                      init_bin = None
                      log.warning('Unable to fetch data from /proc/1/cmdline')
                  if init_bin is not None and init_bin.endswith('bin/init'):
                      supported_inits = (b'upstart', b'sysvinit', b'systemd')
                      edge_len = max(len(x) for x in supported_inits) - 1
                      try:
                          buf_size = __opts__['file_buffer_size']
                      except KeyError:
                          # Default to the value of file_buffer_size for the minion
                          buf_size = 262144
                      try:
                          with salt.utils.files.fopen(init_bin, 'rb') as fp_:
                              edge = b''
                              buf = fp_.read(buf_size).lower()
                              while buf:
                                  buf = edge + buf
                                  for item in supported_inits:
                                      if item in buf:
                                          if six.PY3:
                                              item = item.decode('utf-8')
                                          grains['init'] = item
                                          buf = b''
                                          break
                                  edge = buf[-edge_len:]
                                  buf = fp_.read(buf_size).lower()
                      except (IOError, OSError) as exc:
                          log.error(
                              'Unable to read from init_bin (%s): %s',
                              init_bin, exc
                          )
                  elif salt.utils.path.which('supervisord') in init_cmdline:
                      grains['init'] = 'supervisord'
                  elif salt.utils.path.which('dumb-init') in init_cmdline:
                      # https://github.com/Yelp/dumb-init
                      grains['init'] = 'dumb-init'
                  elif salt.utils.path.which('tini') in init_cmdline:
                      # https://github.com/krallin/tini
                      grains['init'] = 'tini'
                  elif init_cmdline == ['runit']:
                      grains['init'] = 'runit'
                  elif '/sbin/my_init' in init_cmdline:
                      # Phusion Base docker container use runit for srv mgmt, but
                      # my_init as pid1
                      grains['init'] = 'runit'
                  else:
                      log.debug(
                          'Could not determine init system from command line: (%s)',
                          ' '.join(init_cmdline)
                      )
  
          # Add lsb grains on any distro with lsb-release. Note that this import
          # can fail on systems with lsb-release installed if the system package
          # does not install the python package for the python interpreter used by
          # Salt (i.e. python2 or python3)
          try:
              log.trace('Getting lsb_release distro information')
              import lsb_release  # pylint: disable=import-error
              release = lsb_release.get_distro_information()
              for key, value in six.iteritems(release):
                  key = key.lower()
                  lsb_param = 'lsb_{0}{1}'.format(
                      '' if key.startswith('distrib_') else 'distrib_',
                      key
                  )
                  grains[lsb_param] = value
          # Catch a NameError to workaround possible breakage in lsb_release
          # See https://github.com/saltstack/salt/issues/37867
          except (ImportError, NameError):
              # if the python library isn't available, try to parse
              # /etc/lsb-release using regex
              log.trace('lsb_release python bindings not available')
              grains.update(_parse_lsb_release())
  
              if grains.get('lsb_distrib_description', '').lower().startswith('antergos'):
                  # Antergos incorrectly configures their /etc/lsb-release,
                  # setting the DISTRIB_ID to "Arch". This causes the "os" grain
                  # to be incorrectly set to "Arch".
                  grains['osfullname'] = 'Antergos Linux'
              elif 'lsb_distrib_id' not in grains:
                  log.trace(
                      'Failed to get lsb_distrib_id, trying to parse os-release'
                  )
                  os_release = _parse_os_release('/etc/os-release', '/usr/lib/os-release')
                  if os_release:
                      if 'NAME' in os_release:
                          grains['lsb_distrib_id'] = os_release['NAME'].strip()
                      if 'VERSION_ID' in os_release:
                          grains['lsb_distrib_release'] = os_release['VERSION_ID']
                      if 'VERSION_CODENAME' in os_release:
                          grains['lsb_distrib_codename'] = os_release['VERSION_CODENAME']
                      elif 'PRETTY_NAME' in os_release:
                          codename = os_release['PRETTY_NAME']
                          # https://github.com/saltstack/salt/issues/44108
                          if os_release['ID'] == 'debian':
                              codename_match = re.search(r'\((\w+)\)$', codename)
                              if codename_match:
                                  codename = codename_match.group(1)
                          grains['lsb_distrib_codename'] = codename
                      if 'CPE_NAME' in os_release:
                          cpe = _parse_cpe_name(os_release['CPE_NAME'])
                          if not cpe:
                              log.error('Broken CPE_NAME format in /etc/os-release!')
                          elif cpe.get('vendor', '').lower() in ['suse', 'opensuse']:
                              grains['os'] = "SUSE"
                              # openSUSE `osfullname` grain normalization
                              if os_release.get("NAME") == "openSUSE Leap":
                                  grains['osfullname'] = "Leap"
                              elif os_release.get("VERSION") == "Tumbleweed":
                                  grains['osfullname'] = os_release["VERSION"]
                              # Override VERSION_ID, if CPE_NAME around
                              if cpe.get('version') and cpe.get('vendor') == 'opensuse':  # Keep VERSION_ID for SLES
                                  grains['lsb_distrib_release'] = cpe['version']
  
                  elif os.path.isfile('/etc/SuSE-release'):
                      log.trace('Parsing distrib info from /etc/SuSE-release')
                      grains['lsb_distrib_id'] = 'SUSE'
                      version = ''
                      patch = ''
                      with salt.utils.files.fopen('/etc/SuSE-release') as fhr:
                          for line in fhr:
                              if 'enterprise' in line.lower():
                                  grains['lsb_distrib_id'] = 'SLES'
                                  grains['lsb_distrib_codename'] = re.sub(r'\(.+\)', '', line).strip()
                              elif 'version' in line.lower():
                                  version = re.sub(r'[^0-9]', '', line)
                              elif 'patchlevel' in line.lower():
                                  patch = re.sub(r'[^0-9]', '', line)
                      grains['lsb_distrib_release'] = version
                      if patch:
                          grains['lsb_distrib_release'] += '.' + patch
                          patchstr = 'SP' + patch
                          if grains['lsb_distrib_codename'] and patchstr not in grains['lsb_distrib_codename']:
                              grains['lsb_distrib_codename'] += ' ' + patchstr
                      if not grains.get('lsb_distrib_codename'):
                          grains['lsb_distrib_codename'] = 'n.a'
                  elif os.path.isfile('/etc/altlinux-release'):
                      log.trace('Parsing distrib info from /etc/altlinux-release')
                      # ALT Linux
                      grains['lsb_distrib_id'] = 'altlinux'
                      with salt.utils.files.fopen('/etc/altlinux-release') as ifile:
                          # This file is symlinked to from:
                          #     /etc/fedora-release
                          #     /etc/redhat-release
                          #     /etc/system-release
                          for line in ifile:
                              # ALT Linux Sisyphus (unstable)
                              comps = line.split()
                              if comps[0] == 'ALT':
                                  grains['lsb_distrib_release'] = comps[2]
                                  grains['lsb_distrib_codename'] = \
                                      comps[3].replace('(', '').replace(')', '')
                  elif os.path.isfile('/etc/centos-release'):
                      log.trace('Parsing distrib info from /etc/centos-release')
                      # Maybe CentOS Linux; could also be SUSE Expanded Support.
                      # SUSE ES has both, centos-release and redhat-release.
                      if os.path.isfile('/etc/redhat-release'):
                          with salt.utils.files.fopen('/etc/redhat-release') as ifile:
                              for line in ifile:
                                  if "red hat enterprise linux server" in line.lower():
                                      # This is a SUSE Expanded Support Rhel installation
                                      grains['lsb_distrib_id'] = 'RedHat'
                                      break
                      grains.setdefault('lsb_distrib_id', 'CentOS')
                      with salt.utils.files.fopen('/etc/centos-release') as ifile:
                          for line in ifile:
                              # Need to pull out the version and codename
                              # in the case of custom content in /etc/centos-release
                              find_release = re.compile(r'\d+\.\d+')
                              find_codename = re.compile(r'(?<=\()(.*?)(?=\))')
                              release = find_release.search(line)
                              codename = find_codename.search(line)
                              if release is not None:
                                  grains['lsb_distrib_release'] = release.group()
                              if codename is not None:
                                  grains['lsb_distrib_codename'] = codename.group()
                  elif os.path.isfile('/etc.defaults/VERSION') \
                          and os.path.isfile('/etc.defaults/synoinfo.conf'):
                      grains['osfullname'] = 'Synology'
                      log.trace(
                          'Parsing Synology distrib info from /etc/.defaults/VERSION'
                      )
                      with salt.utils.files.fopen('/etc.defaults/VERSION', 'r') as fp_:
                          synoinfo = {}
                          for line in fp_:
                              try:
                                  key, val = line.rstrip('\n').split('=')
                              except ValueError:
                                  continue
                              if key in ('majorversion', 'minorversion',
                                         'buildnumber'):
                                  synoinfo[key] = val.strip('"')
                          if len(synoinfo) != 3:
                              log.warning(
                                  'Unable to determine Synology version info. '
                                  'Please report this, as it is likely a bug.'
                              )
                          else:
                              grains['osrelease'] = (
                                  '{majorversion}.{minorversion}-{buildnumber}'
                                  .format(**synoinfo)
                              )
  
          # Use the already intelligent platform module to get distro info
          # (though apparently it's not intelligent enough to strip quotes)
          log.trace(
              'Getting OS name, release, and codename from '
              'distro.linux_distribution()'
          )
          (osname, osrelease, oscodename) = \
              [x.strip('"').strip("'") for x in
               linux_distribution(supported_dists=_supported_dists)]
          # Try to assign these three names based on the lsb info, they tend to
          # be more accurate than what python gets from /etc/DISTRO-release.
          # It's worth noting that Ubuntu has patched their Python distribution
          # so that linux_distribution() does the /etc/lsb-release parsing, but
          # we do it anyway here for the sake for full portability.
          if 'osfullname' not in grains:
              # If NI Linux RT distribution, set the grains['osfullname'] to 'nilrt'
              if grains.get('lsb_distrib_id', '').lower().startswith('nilrt'):
                  grains['osfullname'] = 'nilrt'
              else:
                  grains['osfullname'] = grains.get('lsb_distrib_id', osname).strip()
          if 'osrelease' not in grains:
              # NOTE: This is a workaround for CentOS 7 os-release bug
              # https://bugs.centos.org/view.php?id=8359
              # /etc/os-release contains no minor distro release number so we fall back to parse
              # /etc/centos-release file instead.
              # Commit introducing this comment should be reverted after the upstream bug is released.
              if 'CentOS Linux 7' in grains.get('lsb_distrib_codename', ''):
                  grains.pop('lsb_distrib_release', None)
              grains['osrelease'] = grains.get('lsb_distrib_release', osrelease).strip()
          grains['oscodename'] = grains.get('lsb_distrib_codename', '').strip() or oscodename
          if 'Red Hat' in grains['oscodename']:
              grains['oscodename'] = oscodename
          distroname = _REPLACE_LINUX_RE.sub('', grains['osfullname']).strip()
          # return the first ten characters with no spaces, lowercased
          shortname = distroname.replace(' ', '').lower()[:10]
          # this maps the long names from the /etc/DISTRO-release files to the
          # traditional short names that Salt has used.
          if 'os' not in grains:
              grains['os'] = _OS_NAME_MAP.get(shortname, distroname)
          grains.update(_linux_cpudata())
          grains.update(_linux_gpu_data())
      elif grains['kernel'] == 'SunOS':
          if salt.utils.platform.is_smartos():
              # See https://github.com/joyent/smartos-live/issues/224
              if HAS_UNAME:
                  uname_v = os.uname()[3]  # format: joyent_20161101T004406Z
              else:
                  uname_v = os.name
              uname_v = uname_v[uname_v.index('_')+1:]
              grains['os'] = grains['osfullname'] = 'SmartOS'
              # store a parsed version of YYYY.MM.DD as osrelease
              grains['osrelease'] = ".".join([
                  uname_v.split('T')[0][0:4],
                  uname_v.split('T')[0][4:6],
                  uname_v.split('T')[0][6:8],
              ])
              # store a untouched copy of the timestamp in osrelease_stamp
              grains['osrelease_stamp'] = uname_v
          elif os.path.isfile('/etc/release'):
              with salt.utils.files.fopen('/etc/release', 'r') as fp_:
                  rel_data = fp_.read()
                  try:
                      release_re = re.compile(
                          r'((?:Open|Oracle )?Solaris|OpenIndiana|OmniOS) (Development)?'
                          r'\s*(\d+\.?\d*|v\d+)\s?[A-Z]*\s?(r\d+|\d+\/\d+|oi_\S+|snv_\S+)?'
                      )
                      osname, development, osmajorrelease, osminorrelease = release_re.search(rel_data).groups()
                  except AttributeError:
                      # Set a blank osrelease grain and fallback to 'Solaris'
                      # as the 'os' grain.
                      grains['os'] = grains['osfullname'] = 'Solaris'
                      grains['osrelease'] = ''
                  else:
                      if development is not None:
                          osname = ' '.join((osname, development))
                      if HAS_UNAME:
                          uname_v = os.uname()[3]
                      else:
                          uname_v = os.name
                      grains['os'] = grains['osfullname'] = osname
                      if osname in ['Oracle Solaris'] and uname_v.startswith(osmajorrelease):
                          # Oracla Solars 11 and up have minor version in uname
                          grains['osrelease'] = uname_v
                      elif osname in ['OmniOS']:
                          # OmniOS
                          osrelease = []
                          osrelease.append(osmajorrelease[1:])
                          osrelease.append(osminorrelease[1:])
                          grains['osrelease'] = ".".join(osrelease)
                          grains['osrelease_stamp'] = uname_v
                      else:
                          # Sun Solaris 10 and earlier/comparable
                          osrelease = []
                          osrelease.append(osmajorrelease)
                          if osminorrelease:
                              osrelease.append(osminorrelease)
                          grains['osrelease'] = ".".join(osrelease)
                          grains['osrelease_stamp'] = uname_v
  
          grains.update(_sunos_cpudata())
      elif grains['kernel'] == 'VMkernel':
          grains['os'] = 'ESXi'
      elif grains['kernel'] == 'Darwin':
          osrelease = __salt__['cmd.run']('sw_vers -productVersion')
          osname = __salt__['cmd.run']('sw_vers -productName')
          osbuild = __salt__['cmd.run']('sw_vers -buildVersion')
          grains['os'] = 'MacOS'
          grains['os_family'] = 'MacOS'
          grains['osfullname'] = "{0} {1}".format(osname, osrelease)
          grains['osrelease'] = osrelease
          grains['osbuild'] = osbuild
          grains['init'] = 'launchd'
          grains.update(_bsd_cpudata(grains))
          grains.update(_osx_gpudata())
          grains.update(_osx_platform_data())
      elif grains['kernel'] == 'AIX':
          osrelease = __salt__['cmd.run']('oslevel')
          osrelease_techlevel = __salt__['cmd.run']('oslevel -r')
          osname = __salt__['cmd.run']('uname')
          grains['os'] = 'AIX'
          grains['osfullname'] = osname
          grains['osrelease'] = osrelease
          grains['osrelease_techlevel'] = osrelease_techlevel
          grains.update(_aix_cpudata())
      else:
          grains['os'] = grains['kernel']
      if grains['kernel'] == 'FreeBSD':
          try:
              grains['osrelease'] = __salt__['cmd.run']('freebsd-version -u').split('-')[0]
          except salt.exceptions.CommandExecutionError:
              # freebsd-version was introduced in 10.0.
              # derive osrelease from kernelversion prior to that
              grains['osrelease'] = grains['kernelrelease'].split('-')[0]
          grains.update(_bsd_cpudata(grains))
      if grains['kernel'] in ('OpenBSD', 'NetBSD'):
          grains.update(_bsd_cpudata(grains))
          grains['osrelease'] = grains['kernelrelease'].split('-')[0]
          if grains['kernel'] == 'NetBSD':
              grains.update(_netbsd_gpu_data())
      if not grains['os']:
          grains['os'] = 'Unknown {0}'.format(grains['kernel'])
          grains['os_family'] = 'Unknown'
      else:
          # this assigns family names based on the os name
          # family defaults to the os name if not found
          grains['os_family'] = _OS_FAMILY_MAP.get(grains['os'],
                                                   grains['os'])
  
      # Build the osarch grain. This grain will be used for platform-specific
      # considerations such as package management. Fall back to the CPU
      # architecture.
      if grains.get('os_family') == 'Debian':
          osarch = __salt__['cmd.run']('dpkg --print-architecture').strip()
      elif grains.get('os_family') in ['RedHat', 'Suse']:
          osarch = salt.utils.pkg.rpm.get_osarch()
      elif grains.get('os_family') in ('NILinuxRT', 'Poky'):
          archinfo = {}
          for line in __salt__['cmd.run']('opkg print-architecture').splitlines():
              if line.startswith('arch'):
                  _, arch, priority = line.split()
                  archinfo[arch.strip()] = int(priority.strip())
  
          # Return osarch in priority order (higher to lower)
          osarch = sorted(archinfo, key=archinfo.get, reverse=True)
      else:
          osarch = grains['cpuarch']
      grains['osarch'] = osarch
  
      grains.update(_memdata(grains))
  
      # Get the hardware and bios data
      grains.update(_hw_data(grains))
  
      # Load the virtual machine info
      grains.update(_virtual(grains))
      grains.update(_virtual_hv(grains))
      grains.update(_ps(grains))
  
      if grains.get('osrelease', ''):
          osrelease_info = grains['osrelease'].split('.')
          for idx, value in enumerate(osrelease_info):
              if not value.isdigit():
                  continue
              osrelease_info[idx] = int(value)
          grains['osrelease_info'] = tuple(osrelease_info)
          try:
              grains['osmajorrelease'] = int(grains['osrelease_info'][0])
          except (IndexError, TypeError, ValueError):
              log.debug(
                  'Unable to derive osmajorrelease from osrelease_info \'%s\'. '
                  'The osmajorrelease grain will not be set.',
                  grains['osrelease_info']
              )
          os_name = grains['os' if grains.get('os') in (
              'Debian', 'FreeBSD', 'OpenBSD', 'NetBSD', 'Mac', 'Raspbian') else 'osfullname']
          grains['osfinger'] = '{0}-{1}'.format(
              os_name, grains['osrelease'] if os_name in ('Ubuntu',) else grains['osrelease_info'][0])
  
      return grains