text: # -*- coding: utf-8 -*-
  '''
  VirtualBox Guest Additions installer
  '''
  from __future__ import absolute_import
  
  # Import python libs
  import contextlib
  import functools
  import glob
  import logging
  import os
  import re
  import tempfile
  
  
  log = logging.getLogger(__name__)
  __virtualname__ = 'vbox_guest'
  _additions_dir_prefix = 'VBoxGuestAdditions'
  _shared_folders_group = 'vboxsf'
  
  
  def __virtual__():
      '''
      Set the vbox_guest module if the OS Linux
      '''
      if __grains__.get('kernel', '') not in ('Linux', ):
          return False
      return __virtualname__
  
  
  def additions_mount():
      '''
      Mount VirtualBox Guest Additions CD to the temp directory.
  
      To connect VirtualBox Guest Additions via VirtualBox graphical interface
      press 'Host+D' ('Host' is usually 'Right Ctrl').
  
      CLI Example:
  
      .. code-block:: bash
  
          salt '*' vbox_guest.additions_mount
  
      :return: True or OSError exception
      '''
      mount_point = tempfile.mkdtemp()
      ret = __salt__['mount.mount'](mount_point, '/dev/cdrom')
      if ret is True:
          return mount_point
      else:
          raise OSError(ret)
  
  
  def additions_umount(mount_point):
      '''
      Unmount VirtualBox Guest Additions CD from the temp directory.
  
      CLI Example:
  
      .. code-block:: bash
  
          salt '*' vbox_guest.additions_umount
  
      :param mount_point: directory VirtualBox Guest Additions is mounted to
      :return: True or an string with error
      '''
      ret = __salt__['mount.umount'](mount_point)
      if ret:
          os.rmdir(mount_point)
      return ret
  
  
  @contextlib.contextmanager
  def _additions_mounted():
      mount_point = additions_mount()
      yield mount_point
      additions_umount(mount_point)
  
  
  def _return_mount_error(f):
      @functools.wraps(f)
      def wrapper(*args, **kwargs):
          try:
              return f(*args, **kwargs)
          except OSError as e:
              return str(e)
      return wrapper
  
  
  def _additions_install_program_path(mount_point):
      return os.path.join(mount_point, {
          'Linux': 'VBoxLinuxAdditions.run',
          'Solaris': 'VBoxSolarisAdditions.pkg',
          'Windows': 'VBoxWindowsAdditions.exe'
      }[__grains__.get('kernel', '')])
  
  
  def _additions_install_opensuse(**kwargs):
      kernel_type = re.sub(
          r'^(\d|\.|-)*', '', __grains__.get('kernelrelease', ''))
      kernel_devel = 'kernel-{0}-devel'.format(kernel_type)
      ret = __salt__['state.single']('pkg.installed', 'devel packages',
                                     pkgs=['make', 'gcc', kernel_devel])
      return ret
  
  
  def _additions_install_ubuntu(**kwargs):
      ret = __salt__['state.single']('pkg.installed', 'devel packages',
                                     pkgs=['dkms', ])
      return ret
  
  
  def _additions_install_fedora(**kwargs):
      ret = __salt__['state.single']('pkg.installed', 'devel packages',
                                     pkgs=['dkms', 'gcc'])
      return ret
  
  
  def _additions_install_linux(mount_point, **kwargs):
      reboot = kwargs.pop('reboot', False)
      restart_x11 = kwargs.pop('restart_x11', False)
      upgrade_os = kwargs.pop('upgrade_os', True)
      if upgrade_os:
          __salt__['pkg.upgrade']()
      # dangerous: do not call variable `os` as it will hide os module
      guest_os = __grains__.get('os', '')
      if guest_os == 'openSUSE':
          _additions_install_opensuse(**kwargs)
      elif guest_os == 'ubuntu':
          _additions_install_ubuntu(**kwargs)
      elif guest_os == 'fedora':
          _additions_install_fedora(**kwargs)
      else:
          log.warning("{0} is not fully supported yet.".format(guest_os))
      installer_path = _additions_install_program_path(mount_point)
      installer_ret = __salt__['cmd.run_all'](installer_path)
      if installer_ret['retcode'] in (0, 1):
          if reboot:
              __salt__['system.reboot']()
          elif restart_x11:
              raise NotImplementedError("Restarting x11 is not supported yet.")
          else:
              # VirtualBox script enables module itself, need to restart OS
              # anyway, probably don't need that.
              # for service in ('vboxadd', 'vboxadd-service', 'vboxadd-x11'):
              #     __salt__['service.start'](service)
              pass
          return additions_version()
      elif installer_ret['retcode'] in (127, '127'):
          return ("'{0}' not found on CD. Make sure that VirtualBox Guest "
                  "Additions CD is attached to the CD IDE Controller.".format(
                      os.path.basename(installer_path)))
      else:
          return installer_ret['stderr']
  
  
  @_return_mount_error
  def additions_install(**kwargs):
      '''
      Install VirtualBox Guest Additions. Uses the CD, connected by VirtualBox.
  
      To connect VirtualBox Guest Additions via VirtualBox graphical interface
      press 'Host+D' ('Host' is usually 'Right Ctrl').
  
      See https://www.virtualbox.org/manual/ch04.html#idp52733088 for more details.
  
      CLI Example:
  
      .. code-block:: bash
  
          salt '*' vbox_guest.additions_install
          salt '*' vbox_guest.additions_install reboot=True
          salt '*' vbox_guest.additions_install upgrade_os=False
  
      :param reboot: reboot computer to complete installation
      :type reboot: bool
      :param upgrade_os: upgrade OS (to ensure the latests version of kernel and developer tools are installed)
      :type upgrade_os: bool
      :return: version of VirtualBox Guest Additions or string with error
      '''
      with _additions_mounted() as mount_point:
          kernel = __grains__.get('kernel', '')
          if kernel == 'Linux':
              return _additions_install_linux(mount_point, **kwargs)
  
  
  def _additions_dir():
      root = '/opt'
      dirs = glob.glob(os.path.join(root, _additions_dir_prefix) + '*')
      if dirs:
          return dirs[0]
      else:
          raise EnvironmentError('No VirtualBox Guest Additions dirs found!')
  
  
  def _additions_remove_linux_run(cmd):
      uninstaller_ret = __salt__['cmd.run_all'](cmd)
      return uninstaller_ret['retcode'] in (0, )
  
  
  def _additions_remove_linux(**kwargs):
      try:
          return _additions_remove_linux_run(
                  os.path.join(_additions_dir(), 'uninstall.sh'))
      except EnvironmentError:
          return False
  
  
  def _additions_remove_linux_use_cd(mount_point, **kwargs):
      force = kwargs.pop('force', False)
      args = ''
      if force:
          args += '--force'
      return _additions_remove_linux_run('{program} uninstall {args}'.format(
          program=_additions_install_program_path(mount_point), args=args))
  
  
  @_return_mount_error
  def _additions_remove_use_cd(**kwargs):
      '''
      Remove VirtualBox Guest Additions.
  
      It uses the CD, connected by VirtualBox.
      '''
  
      with _additions_mounted() as mount_point:
          kernel = __grains__.get('kernel', '')
          if kernel == 'Linux':
              return _additions_remove_linux_use_cd(mount_point, **kwargs)
  
  
  def additions_remove(**kwargs):
      '''
      Remove VirtualBox Guest Additions.
  
      Firstly it tries to uninstall itself by executing
      '/opt/VBoxGuestAdditions-VERSION/uninstall.run uninstall'.
      It uses the CD, connected by VirtualBox if it failes.
  
      CLI Example:
  
      .. code-block:: bash
  
          salt '*' vbox_guest.additions_remove
          salt '*' vbox_guest.additions_remove force=True
  
      :param force: force VirtualBox Guest Additions removing
      :type force: bool
      :return: True if VirtualBox Guest Additions were removed successfully else False
  
      '''
      kernel = __grains__.get('kernel', '')
      if kernel == 'Linux':
          ret = _additions_remove_linux()
      if not ret:
          ret = _additions_remove_use_cd(**kwargs)
      return ret
  
  
  def additions_version():
      '''
      Check VirtualBox Guest Additions version.
  
      CLI Example:
  
      .. code-block:: bash
  
          salt '*' vbox_guest.additions_version
  
      :return: version of VirtualBox Guest Additions or False if they are not installed
      '''
      try:
          d = _additions_dir()
      except EnvironmentError:
          return False
      if d and len(os.listdir(d)) > 0:
          return re.sub(r'^{0}-'.format(_additions_dir_prefix), '',
                  os.path.basename(d))
      return False
  
  
  def grant_access_to_shared_folders_to(name, users=None):
      '''
      Grant access to auto-mounted shared folders to the users.
  
      User is specified by it's name. To grant access for several users use argument `users`.
      Access will be denied to the users not listed in `users` argument.
  
      See https://www.virtualbox.org/manual/ch04.html#sf_mount_auto for more details.
  
      CLI Example:
  
      .. code-block:: bash
  
          salt '*' vbox_guest.grant_access_to_shared_folders_to fred
          salt '*' vbox_guest.grant_access_to_shared_folders_to users ['fred', 'roman']
  
      :param name: name of the user to grant access to auto-mounted shared folders to
      :type name: str
      :param users: list of names of users to grant access to auto-mounted shared folders to (if specified, `name` will not be taken into account)
      :type users: list of str
      :return: list of users who have access to auto-mounted shared folders
      '''
      if users is None:
          users = [name]
      if __salt__['group.members'](_shared_folders_group, ','.join(users)):
          return users
      else:
          if not __salt__['group.info'](_shared_folders_group):
              if not additions_version:
                  return ("VirtualBox Guest Additions are not installed. Ιnstall "
                          "them firstly. You can do it with the help of command "
                          "vbox_guest.additions_install.")
              else:
                  return (
                      "VirtualBox Guest Additions seems to be installed, but "
                      "group '{0}' not found. Check your installation and fix "
                      "it. You can uninstall VirtualBox Guest Additions with "
                      "the help of command :py:func:`vbox_guest.additions_remove "
                      "<salt.modules.vbox_guest.additions_remove> (it has "
                      "`force` argument to fix complex situations; use "
                      "it with care) and then install it again. You can do "
                      "it with the help of :py:func:`vbox_guest.additions_install "
                      "<salt.modules.vbox_guest.additions_install>`."
                      "".format(_shared_folders_group))
          else:
              return ("Cannot replace members of the '{0}' group."
                      "".format(_shared_folders_group))
  
  
  def list_shared_folders_users():
      '''
      List users who have access to auto-mounted shared folders.
  
      See https://www.virtualbox.org/manual/ch04.html#sf_mount_auto for more details.
  
      CLI Example:
  
      .. code-block:: bash
  
          salt '*' vbox_guest.list_shared_folders_users
  
      :return: list of users who have access to auto-mounted shared folders
      '''
      try:
          return __salt__['group.info'](_shared_folders_group)['members']
      except KeyError:
          return []
  