Add initial support for etcd

This commit is contained in:
Joseph Hall 2014-03-18 14:49:54 -06:00
parent 9890db1fc1
commit 585c2755fc
4 changed files with 371 additions and 0 deletions

136
salt/modules/etcd_mod.py Normal file
View File

@ -0,0 +1,136 @@
# -*- coding: utf-8 -*-
'''
Execution module to work with etcd
:depends: - python-etcd
In order to use an etcd server, a profile should be created in the master
configuration file:
.. code-block:: yaml
my_etd_config:
etcd.host: 127.0.0.1
etcd.port: 4001
It is technically possible to configure etcd without using a profile, but this
is not consided to be a best practice, especially when multiple etcd servers or
clusters are available.
.. code-block:: yaml
etcd.host: 127.0.0.1
etcd.port: 4001
'''
# Import python libs
import logging
# Import third party libs
try:
import salt.utils.etcd_util
HAS_LIBS = True
except Exception:
HAS_LIBS = False
__virtualname__ = 'etcd'
# Set up logging
log = logging.getLogger(__name__)
# Define a function alias in order not to shadow built-in's
__func_alias__ = {
'get_': 'get',
'set_': 'set',
'rm_': 'rm',
'ls_': 'ls'
}
def __virtual__():
'''
Only return if python-etcd is installed
'''
return __virtualname__ if HAS_LIBS else False
def get_(key, recurse=False, profile=None):
'''
Get a value from etcd, by direct path
CLI Examples:
salt myminion etcd.get /path/to/key
salt myminion etcd.get /path/to/key profile=my_etcd_config
salt myminion etcd.get /path/to/key recurse=True profile=my_etcd_config
'''
client = salt.utils.etcd_util.get_conn(__opts__, profile)
result = client.get(key)
if recurse:
return salt.utils.etcd_util.tree(client, key)
else:
return result.value
def set_(key, value, profile=None):
'''
Set a value in etcd, by direct path
CLI Example:
salt myminion etcd.set /path/to/key value
salt myminion etcd.set /path/to/key value profile=my_etcd_config
'''
client = salt.utils.etcd_util.get_conn(__opts__, profile)
return client.write(key, value)
def ls_(path='/', profile=None):
'''
Return all keys and dirs inside a specific path
CLI Example:
salt myminion etcd.ls /path/to/dir/
salt myminion etcd.ls /path/to/dir/ profile=my_etcd_config
'''
ret = {}
client = salt.utils.etcd_util.get_conn(__opts__, profile)
items = client.get(path)
for item in items.children:
if item.dir is True:
dir_name = '{0}/'.format(item.key)
ret[dir_name] = {}
else:
ret[item.key] = item.value
return {path: ret}
def rm_(key, recurse=False, profile=None):
'''
Delete a key from etcd
CLI Example:
salt myminion etcd.rm /path/to/key
salt myminion etcd.rm /path/to/key profile=my_etcd_config
salt myminion etcd.rm /path/to/dir recurse=True profile=my_etcd_config
'''
client = salt.utils.etcd_util.get_conn(__opts__, profile)
return client.delete(key, recursive=recurse)
def tree(path='/', profile=None):
'''
Recurse through etcd and return all values
CLI Example:
salt myminion etcd.tree
salt myminion etcd.tree profile=my_etcd_config
salt myminion etcd.tree /path/to/keys profile=my_etcd_config
'''
client = salt.utils.etcd_util.get_conn(__opts__, profile)
return salt.utils.etcd_util.tree(client, path)

View File

@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
'''
Use etcd data as a Pillar source
:depends: - python-etcd
In order to use an etcd server, a profile must be created in the master
configuration file:
.. code-block:: yaml
my_etd_config:
etcd.host: 127.0.0.1
etcd.port: 4001
After the profile is created, configure the external pillar system to use it.
Optionally, a root may be specified.
.. code-block:: yaml
ext_pillar:
- etcd: my_etcd_config
ext_pillar:
- etcd: my_etcd_config root=/salt
Using these configuration profiles, multiple etcd sources may also be used:
.. code-block:: yaml
ext_pillar:
- etcd: my_etcd_config
- etcd: my_other_etcd_config
'''
# Import python libs
import logging
# Import third party libs
try:
import salt.utils.etcd_util
HAS_LIBS = True
except Exception:
HAS_LIBS = False
__virtualname__ = 'etcd'
# Set up logging
log = logging.getLogger(__name__)
def __virtual__():
'''
Only return if python-etcd is installed
'''
return __virtualname__ if HAS_LIBS else False
def ext_pillar(minion_id, pillar, conf): # pylint: disable=W0613
'''
Check etcd for all data
'''
comps = conf.split()
profile = None
if comps[0]:
profile = comps[0]
client = salt.utils.etcd_util.get_conn(__opts__, profile)
path = '/'
if len(comps) > 1 and comps[1].startswith('root='):
path = comps[1].replace('root=', '')
return salt.utils.etcd_util.tree(client, path)

View File

@ -0,0 +1,82 @@
# -*- coding: utf-8 -*-
'''
Return data to an etcd server or cluster
:depends: - python-etcd
In order to return to an etcd server, a profile should be created in the master
configuration file:
.. code-block:: yaml
my_etd_config:
etcd.host: 127.0.0.1
etcd.port: 4001
It is technically possible to configure etcd without using a profile, but this
is not consided to be a best practice, especially when multiple etcd servers or
clusters are available.
.. code-block:: yaml
etcd.host: 127.0.0.1
etcd.port: 4001
Additionally, two more options must be specified in the top-level configuration
in order to use the etcd returner:
.. code-block:: yaml
etcd.returner: my_etcd_config
etcd.returner_root: /salt/return
The ``etcd.returner`` option specifies which configuration profile to use. The
``etcd.returner_root`` option specifies the path inside etcd to use as the root
of the returner system.
Once the etcd options are configured, the returner may be used:
CLI Example:
salt '*' test.ping --return etcd
'''
# Import python libs
import logging
# Import third party libs
try:
import salt.utils.etcd_util
HAS_LIBS = True
except Exception:
HAS_LIBS = False
log = logging.getLogger(__name__)
# Define the module's virtual name
__virtualname__ = 'etcd'
def __virtual__():
'''
Only return if python-etcd is installed
'''
return __virtualname__ if HAS_LIBS else False
def returner(ret):
'''
Return data to an etcd server or cluster
'''
profile = __opts__.get('etcd.returner', None)
path = __opts__.get('etcd.returner_root', '/salt/return')
client = salt.utils.etcd_util.get_conn(__opts__, profile)
for field in ret.keys():
dest = '/'.join((
path,
ret['jid'],
ret['id'],
field
))
client.write(dest, ret[field])

79
salt/utils/etcd_util.py Normal file
View File

@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
'''
Utilities for working with etcd
:depends: - python-etcd
This library sets up a client object for etcd, using the configuration passed
into the client() function. Normally, this is __opts__. Optionally, a profile
may be passed in. The following configurations are both valid:
.. code-block:: yaml
# No profile name
etcd.host: 127.0.0.1
etcd.port: 4001
# One or more profiles defined
my_etcd_config:
etcd.host: 127.0.0.1
etcd.port: 4001
Once configured, the client() function is passed a set of opts, and optionally,
the name of a profile to be used.
.. code-block:: python
import salt.utils.etcd_utils
client = salt.utils.etcd_utils.client(__opts__, profile='my_etcd_config')
It should be noted that some usages of etcd require a profile to be specified,
rather than top-level configurations. This being the case, it is better to
always use a named configuration profile, as shown above.
'''
# Import python libs
import logging
# Import third party libs
try:
import etcd
HAS_LIBS = True
except Exception:
HAS_LIBS = False
# Set up logging
log = logging.getLogger(__name__)
def get_conn(opts, profile=None):
'''
Return a client object for accessing etcd
'''
if profile:
conf = opts.get(profile, {})
else:
conf = opts
host = conf.get('etcd.host', '127.0.0.1')
port = conf.get('etcd.port', 4001)
return etcd.Client(host, port)
def tree(client, path):
'''
Recurse through etcd and return all values
'''
ret = {}
items = client.get(path)
for item in items.children:
comps = str(item.key).split('/')
if item.dir is True:
if item.key == path:
continue
ret[comps[-1]] = tree(client, item.key)
else:
ret[comps[-1]] = item.value
return ret