text: # -*- coding: utf-8 -*-
  '''
  Minion data cache plugin for Consul key/value data store.
  
  .. versionadded:: 2016.11.2
  
  :depends: python-consul >= 0.2.0
  
  It is up to the system administrator to set up and configure the Consul
  infrastructure. All is needed for this plugin is a working Consul agent
  with a read-write access to the key-value store.
  
  The related documentation can be found in the `Consul documentation`_.
  
  To enable this cache plugin, the master will need the python client for
  Consul installed. This can be easily installed with pip:
  
  .. code-block: bash
  
      pip install python-consul
  
  Optionally, depending on the Consul agent configuration, the following values
  could be set in the master config. These are the defaults:
  
  .. code-block:: yaml
  
      consul.host: 127.0.0.1
      consul.port: 8500
      consul.token: None
      consul.scheme: http
      consul.consistency: default
      consul.dc: dc1
      consul.verify: True
  
  Related docs could be found in the `python-consul documentation`_.
  
  To use the consul as a minion data cache backend, set the master ``cache`` config
  value to ``consul``:
  
  .. code-block:: yaml
  
      cache: consul
  
  
  .. _`Consul documentation`: https://www.consul.io/docs/index.html
  .. _`python-consul documentation`: https://python-consul.readthedocs.io/en/latest/#consul
  
  '''
  from __future__ import absolute_import, print_function, unicode_literals
  import logging
  try:
      import consul
      HAS_CONSUL = True
  except ImportError:
      HAS_CONSUL = False
  
  from salt.exceptions import SaltCacheError
  
  log = logging.getLogger(__name__)
  api = None
  
  
  # Define the module's virtual name
  __virtualname__ = 'consul'
  
  __func_alias__ = {'list_': 'list'}
  
  
  def __virtual__():
      '''
      Confirm this python-consul package is installed
      '''
      if not HAS_CONSUL:
          return (False, "Please install python-consul package to use consul data cache driver")
  
      consul_kwargs = {
              'host': __opts__.get('consul.host', '127.0.0.1'),
              'port': __opts__.get('consul.port', 8500),
              'token': __opts__.get('consul.token', None),
              'scheme': __opts__.get('consul.scheme', 'http'),
              'consistency': __opts__.get('consul.consistency', 'default'),
              'dc': __opts__.get('consul.dc', None),
              'verify': __opts__.get('consul.verify', True),
              }
  
      try:
          global api
          api = consul.Consul(**consul_kwargs)
      except AttributeError:
          return (False, "Failed to invoke consul.Consul, please make sure you have python-consul >= 0.2.0 installed")
  
      return __virtualname__
  
  
  def store(bank, key, data):
      '''
      Store a key value.
      '''
      c_key = '{0}/{1}'.format(bank, key)
      try:
          c_data = __context__['serial'].dumps(data)
          api.kv.put(c_key, c_data)
      except Exception as exc:  # pylint: disable=broad-except
          raise SaltCacheError(
              'There was an error writing the key, {0}: {1}'.format(
                  c_key, exc
              )
          )
  
  
  def fetch(bank, key):
      '''
      Fetch a key value.
      '''
      c_key = '{0}/{1}'.format(bank, key)
      try:
          _, value = api.kv.get(c_key)
          if value is None:
              return {}
          return __context__['serial'].loads(value['Value'])
      except Exception as exc:  # pylint: disable=broad-except
          raise SaltCacheError(
              'There was an error reading the key, {0}: {1}'.format(
                  c_key, exc
              )
          )
  
  
  def flush(bank, key=None):
      '''
      Remove the key from the cache bank with all the key content.
      '''
      if key is None:
          c_key = bank
      else:
          c_key = '{0}/{1}'.format(bank, key)
      try:
          return api.kv.delete(c_key, recurse=key is None)
      except Exception as exc:  # pylint: disable=broad-except
          raise SaltCacheError(
              'There was an error removing the key, {0}: {1}'.format(
                  c_key, exc
              )
          )
  
  
  def list_(bank):
      '''
      Return an iterable object containing all entries stored in the specified bank.
      '''
      try:
          _, keys = api.kv.get(bank + '/', keys=True, separator='/')
      except Exception as exc:  # pylint: disable=broad-except
          raise SaltCacheError(
              'There was an error getting the key "{0}": {1}'.format(
                  bank, exc
              )
          )
      if keys is None:
          keys = []
      else:
          # Any key could be a branch and a leaf at the same time in Consul
          # so we have to return a list of unique names only.
          out = set()
          for key in keys:
              out.add(key[len(bank) + 1:].rstrip('/'))
          keys = list(out)
      return keys
  
  
  def contains(bank, key):
      '''
      Checks if the specified bank contains the specified key.
      '''
      if key is None:
          return True  # any key could be a branch and a leaf at the same time in Consul
      else:
          try:
              c_key = '{0}/{1}'.format(bank, key)
              _, value = api.kv.get(c_key)
          except Exception as exc:  # pylint: disable=broad-except
              raise SaltCacheError(
                  'There was an error getting the key, {0}: {1}'.format(
                      c_key, exc
                  )
              )
          return value is not None
  