docs: def install(name=None,
              refresh=False,
              fromrepo=None,
              pkgs=None,
              sources=None,
              downloadonly=None,
              skip_verify=False,
              version=None,
              ignore_repo_failure=False,
              no_recommends=False,
              root=None,
              **kwargs):
      '''
      .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0
          On minions running systemd>=205, `systemd-run(1)`_ is now used to
          isolate commands which modify installed packages from the
          ``salt-minion`` daemon's control group. This is done to keep systemd
          from killing any zypper commands spawned by Salt when the
          ``salt-minion`` service is restarted. (see ``KillMode`` in the
          `systemd.kill(5)`_ manpage for more information). If desired, usage of
          `systemd-run(1)`_ can be suppressed by setting a :mod:`config option
          <salt.modules.config.get>` called ``systemd.scope``, with a value of
          ``False`` (no quotes).
  
      .. _`systemd-run(1)`: https://www.freedesktop.org/software/systemd/man/systemd-run.html
      .. _`systemd.kill(5)`: https://www.freedesktop.org/software/systemd/man/systemd.kill.html
  
      Install the passed package(s), add refresh=True to force a 'zypper refresh'
      before package is installed.
  
      name
          The name of the package to be installed. Note that this parameter is
          ignored if either ``pkgs`` or ``sources`` is passed. Additionally,
          please note that this option can only be used to install packages from
          a software repository. To install a package file manually, use the
          ``sources`` option.
  
          CLI Example:
  
          .. code-block:: bash
  
              salt '*' pkg.install <package name>
  
      refresh
          force a refresh if set to True.
          If set to False (default) it depends on zypper if a refresh is
          executed.
  
      fromrepo
          Specify a package repository to install from.
  
      downloadonly
          Only download the packages, do not install.
  
      skip_verify
          Skip the GPG verification check (e.g., ``--no-gpg-checks``)
  
      version
          Can be either a version number, or the combination of a comparison
          operator (<, >, <=, >=, =) and a version number (ex. '>1.2.3-4').
          This parameter is ignored if ``pkgs`` or ``sources`` is passed.
  
      resolve_capabilities
          If this option is set to True zypper will take capabilities into
          account. In this case names which are just provided by a package
          will get installed. Default is False.
  
      Multiple Package Installation Options:
  
      pkgs
          A list of packages to install from a software repository. Must be
          passed as a python list. A specific version number can be specified
          by using a single-element dict representing the package and its
          version. As with the ``version`` parameter above, comparison operators
          can be used to target a specific version of a package.
  
          CLI Examples:
  
          .. code-block:: bash
  
              salt '*' pkg.install pkgs='["foo", "bar"]'
              salt '*' pkg.install pkgs='["foo", {"bar": "1.2.3-4"}]'
              salt '*' pkg.install pkgs='["foo", {"bar": "<1.2.3-4"}]'
  
      sources
          A list of RPM packages to install. Must be passed as a list of dicts,
          with the keys being package names, and the values being the source URI
          or local path to the package.
  
          CLI Example:
  
          .. code-block:: bash
  
              salt '*' pkg.install sources='[{"foo": "salt://foo.rpm"},{"bar": "salt://bar.rpm"}]'
  
      ignore_repo_failure
          Zypper returns error code 106 if one of the repositories are not available for various reasons.
          In case to set strict check, this parameter needs to be set to True. Default: False.
  
      no_recommends
          Do not install recommended packages, only required ones.
  
      root
          operate on a different root directory.
  
      diff_attr:
          If a list of package attributes is specified, returned value will
          contain them, eg.::
  
              {'<package>': {
                  'old': {
                      'version': '<old-version>',
                      'arch': '<old-arch>'},
  
                  'new': {
                      'version': '<new-version>',
                      'arch': '<new-arch>'}}}
  
          Valid attributes are: ``epoch``, ``version``, ``release``, ``arch``,
          ``install_date``, ``install_date_time_t``.
  
          If ``all`` is specified, all valid attributes will be returned.
  
          .. versionadded:: 2018.3.0
  
  
      Returns a dict containing the new package names and versions::
  
          {'<package>': {'old': '<old-version>',
                         'new': '<new-version>'}}
  
      If an attribute list is specified in ``diff_attr``, the dict will also contain
      any specified attribute, eg.::
  
          {'<package>': {
              'old': {
                  'version': '<old-version>',
                  'arch': '<old-arch>'},
  
              'new': {
                  'version': '<new-version>',
                  'arch': '<new-arch>'}}}
      '''
      if refresh:
          refresh_db(root)
  
      try:
          pkg_params, pkg_type = __salt__['pkg_resource.parse_targets'](name, pkgs, sources, **kwargs)
      except MinionError as exc:
          raise CommandExecutionError(exc)
  
      if not pkg_params:
          return {}
  
      version_num = Wildcard(__zypper__(root=root))(name, version)
  
      if version_num:
          if pkgs is None and sources is None:
              # Allow "version" to work for single package target
              pkg_params = {name: version_num}
          else:
              log.warning('"version" parameter will be ignored for multiple '
                          'package targets')
  
      if pkg_type == 'repository':
          targets = []
          for param, version_num in six.iteritems(pkg_params):
              if version_num is None:
                  log.debug('targeting package: %s', param)
                  targets.append(param)
              else:
                  prefix, verstr = salt.utils.pkg.split_comparison(version_num)
                  if not prefix:
                      prefix = '='
                  target = '{0}{1}{2}'.format(param, prefix, verstr)
                  log.debug('targeting package: %s', target)
                  targets.append(target)
      elif pkg_type == 'advisory':
          targets = []
          cur_patches = list_patches(root=root)
          for advisory_id in pkg_params:
              if advisory_id not in cur_patches:
                  raise CommandExecutionError('Advisory id "{0}" not found'.format(advisory_id))
              else:
                  targets.append('patch:{}'.format(advisory_id))
      else:
          targets = pkg_params
  
      diff_attr = kwargs.get("diff_attr")
  
      includes = _find_types(targets)
      old = list_pkgs(attr=diff_attr, root=root, includes=includes) if not downloadonly else list_downloaded(root)
  
      downgrades = []
      if fromrepo:
          fromrepoopt = ['--force', '--force-resolution', '--from', fromrepo]
          log.info('Targeting repo \'%s\'', fromrepo)
      else:
          fromrepoopt = ''
      cmd_install = ['install', '--auto-agree-with-licenses']
  
      cmd_install.append(kwargs.get('resolve_capabilities') and '--capability' or '--name')
  
      if not refresh:
          cmd_install.insert(0, '--no-refresh')
      if skip_verify:
          cmd_install.insert(0, '--no-gpg-checks')
      if downloadonly:
          cmd_install.append('--download-only')
      if fromrepo:
          cmd_install.extend(fromrepoopt)
      if no_recommends:
          cmd_install.append('--no-recommends')
  
      errors = []
  
      # Split the targets into batches of 500 packages each, so that
      # the maximal length of the command line is not broken
      systemd_scope = _systemd_scope()
      while targets:
          cmd = cmd_install + targets[:500]
          targets = targets[500:]
          for line in __zypper__(no_repo_failure=ignore_repo_failure, systemd_scope=systemd_scope, root=root).call(*cmd).splitlines():
              match = re.match(r"^The selected package '([^']+)'.+has lower version", line)
              if match:
                  downgrades.append(match.group(1))
  
      while downgrades:
          cmd = cmd_install + ['--force'] + downgrades[:500]
          downgrades = downgrades[500:]
          __zypper__(no_repo_failure=ignore_repo_failure, root=root).call(*cmd)
  
      _clean_cache()
      new = list_pkgs(attr=diff_attr, root=root, includes=includes) if not downloadonly else list_downloaded(root)
      ret = salt.utils.data.compare_dicts(old, new)
  
      # If something else from packages are included in the search,
      # better clean the cache.
      if includes:
          _clean_cache()
  
      if errors:
          raise CommandExecutionError(
              'Problem encountered {0} package(s)'.format(
                  'downloading' if downloadonly else 'installing'
              ),
              info={'errors': errors, 'changes': ret}
          )
  
      return ret