Merge remote-tracking branch 'upstream/2014.7' into 2014.7_sam6

Conflicts:
	salt/daemons/flo/worker.py
This commit is contained in:
Samuel M Smith 2014-10-02 15:47:07 -06:00
commit 8c713a3d82
48 changed files with 477 additions and 214 deletions

View File

@ -1,5 +1,5 @@
Jinja2
msgpack-python > 0.1.13
msgpack-python > 0.3
PyYAML
MarkupSafe
requests

View File

@ -24,7 +24,6 @@ import os
import time
import copy
import logging
import zmq
import errno
from datetime import datetime
from salt._compat import string_types
@ -44,6 +43,12 @@ from salt.exceptions import (
EauthAuthenticationError, SaltInvocationError, SaltReqTimeoutError
)
# Import third party libs
try:
import zmq
except ImportError:
pass
# Try to import range from https://github.com/ytoolshed/range
HAS_RANGE = False
try:
@ -786,17 +791,11 @@ class LocalClient(object):
if event is None:
event = self.event
while True:
try:
raw = event.get_event_noblock()
if raw and raw.get('tag', '').startswith(jid):
yield raw
else:
yield None
except zmq.ZMQError as ex:
if ex.errno == errno.EAGAIN or ex.errno == errno.EINTR:
yield None
else:
raise
raw = event.get_event_noblock()
if raw and raw.get('tag', '').startswith(jid):
yield raw
else:
yield None
def get_iter_returns(
self,
@ -846,6 +845,9 @@ class LocalClient(object):
# iterator for the info of this job
jinfo_iter = []
jinfo_timeout = time.time() + timeout
# are there still minions running the job out there
# start as True so that we ping at least once
minions_running = True
while True:
# Process events until timeout is reached or all minions have returned
for raw in ret_iter:
@ -867,7 +869,7 @@ class LocalClient(object):
else:
found.add(raw['data']['id'])
ret = {raw['data']['id']: {'ret': raw['data']['return']}}
if 'out' in raw:
if 'out' in raw['data']:
ret[raw['data']['id']]['out'] = raw['data']['out']
log.debug('jid {0} return from {1}'.format(jid, raw['data']['id']))
yield ret
@ -886,15 +888,14 @@ class LocalClient(object):
break
# let start the timeouts for all remaining minions
missing_minions = minions - found
for id_ in missing_minions:
for id_ in minions - found:
# if we have a new minion in the list, make sure it has a timeout
if id_ not in minion_timeouts:
minion_timeouts[id_] = time.time() + timeout
# if we don't have the job info iterator (or its timed out),
# lets make it assuming we have more minions to ping for
if time.time() > jinfo_timeout and missing_minions:
# if the jinfo has timed out and some minions are still running the job
# re-do the ping
if time.time() > jinfo_timeout and minions_running:
# need our own event listener, so we don't clobber the class one
event = salt.utils.event.get_event(
'master',
@ -904,7 +905,9 @@ class LocalClient(object):
listen=not self.opts.get('__worker', False))
# start listening for new events, before firing off the pings
event.connect_pub()
# since this is a new ping, no one has responded yet
jinfo = self.gather_job_info(jid, tgt, tgt_type)
minions_running = False
# if we weren't assigned any jid that means the master thinks
# we have nothing to send
if 'jid' not in jinfo:
@ -938,13 +941,15 @@ class LocalClient(object):
minions.add(raw['data']['id'])
# update this minion's timeout, as long as the job is still running
minion_timeouts[raw['data']['id']] = time.time() + timeout
# a minion returned, so we know its running somewhere
minions_running = True
# if we have hit gather_job_timeout (after firing the job) AND
# if we have hit all minion timeouts, lets call it
now = time.time()
# if we have finished pinging all the minions
done = now > jinfo_timeout
# only check minion timeouts if the ping job is all done
# if we have finished waiting, and no minions are running the job
# then we need to see if each minion has timedout
done = (now > jinfo_timeout) and not minions_running
if done:
# if all minions have timeod out
for id_ in minions - found:

View File

@ -53,9 +53,9 @@ class LocalClient(salt.client.LocalClient):
**kwargs)
kind = self.opts['__role']
if kind == 'master':
lanename = 'master' #self.opts.value.get('id', self.main.data.lanename)
else: # workers currently are only supported for masters
emsg =("Invalid application kind '{0}' for client.".format())
lanename = 'master' # self.opts.value.get('id', self.main.data.lanename)
else: # workers currently are only supported for masters
emsg = ("Invalid application kind '{0}' for client.".format(kind))
log.error(emsg + '\n')
raise ValueError(emsg)
name = 'client' + nacling.uuid(size=18)

View File

@ -915,7 +915,7 @@ class SaltRaetEventer(ioflo.base.deeding.Deed):
Register event requests
Iterate over the registered event yards and fire!
'''
while self.event_req.value: # event subscription requests are msg with routes
while self.event_req.value: # event subscription requests are msg with routes
self._register_event_yard(
self.event_req.value.popleft()
)

View File

@ -118,7 +118,7 @@ class SaltRaetWorkerSetup(ioflo.base.deeding.Deed):
if kind == 'master':
lanename = 'master' #self.local.data.lanename
else: # workers currently are only supported for masters
emsg =("Invalid application kind '{0}' for worker.".format())
emsg = ("Invalid application kind '{0}' for worker.".format(kind))
log.error(emsg + '\n')
raise ValueError(emsg)

View File

@ -840,9 +840,7 @@ class LocalClient(Client):
if not path:
return {}
ret = {}
with salt.utils.fopen(path, 'rb') as ifile:
ret['hsum'] = getattr(hashlib, self.opts['hash_type'])(
ifile.read()).hexdigest()
ret['hsum'] = salt.utils.get_hash(path, self.opts['hash_type'])
ret['hash_type'] = self.opts['hash_type']
return ret
@ -1008,15 +1006,11 @@ class RemoteClient(Client):
# Master has prompted a file verification, if the
# verification fails, re-download the file. Try 3 times
d_tries += 1
with salt.utils.fopen(dest, 'rb') as fp_:
hsum = getattr(
hashlib,
data.get('hash_type', 'md5')
)(fp_.read()).hexdigest()
if hsum != data['hsum']:
log.warn('Bad download of file {0}, attempt {1} '
'of 3'.format(path, d_tries))
continue
hsum = salt.utils.get_hash(dest, data.get('hash_type', 'md5'))
if hsum != data['hsum']:
log.warn('Bad download of file {0}, attempt {1} '
'of 3'.format(path, d_tries))
continue
break
if not fn_:
with self._cache_loc(data['dest'], saltenv) as cache_dest:

View File

@ -1284,9 +1284,7 @@ def file_hash(load, fnd):
if not os.path.isfile(hashdest):
if not os.path.exists(os.path.dirname(hashdest)):
os.makedirs(os.path.dirname(hashdest))
with salt.utils.fopen(path, 'rb') as fp_:
ret['hsum'] = getattr(hashlib, __opts__['hash_type'])(
fp_.read()).hexdigest()
ret['hsum'] = salt.utils.get_hash(path, __opts__['hash_type'])
with salt.utils.fopen(hashdest, 'w+') as fp_:
fp_.write(ret['hsum'])
return ret

View File

@ -550,9 +550,7 @@ def file_hash(load, fnd):
'{0}.hash.{1}'.format(relpath,
__opts__['hash_type']))
if not os.path.isfile(hashdest):
with salt.utils.fopen(path, 'rb') as fp_:
ret['hsum'] = getattr(hashlib, __opts__['hash_type'])(
fp_.read()).hexdigest()
ret['hsum'] = salt.utils.get_hash(path, __opts__['hash_type'])
with salt.utils.fopen(hashdest, 'w+') as fp_:
fp_.write(ret['hsum'])
return ret

View File

@ -66,7 +66,6 @@ structure::
# Import python libs
import datetime
import os
import hashlib
import time
import pickle
import urllib
@ -522,12 +521,10 @@ def _get_file_from_s3(metadata, saltenv, bucket_name, path, cached_file_path):
if file_etag.find('-') == -1:
file_md5 = file_etag
cached_file_hash = hashlib.md5()
with salt.utils.fopen(cached_file_path, 'rb') as fp_:
cached_file_hash.update(fp_.read())
cached_md5 = salt.utils.get_hash(cached_file_path, 'md5')
# hashes match we have a cache hit
if cached_file_hash.hexdigest() == file_md5:
if cached_md5 == file_md5:
return
else:
cached_file_stat = os.stat(cached_file_path)

View File

@ -12,7 +12,6 @@ import shutil
import fnmatch
import hashlib
import json
import msgpack
import logging
# Import salt libs
@ -22,8 +21,15 @@ import salt.utils.event
import salt.daemons.masterapi
from salt.utils.event import tagify
# Import third party libs
try:
import msgpack
except ImportError:
pass
log = logging.getLogger(__name__)
class KeyCLI(object):
'''
Manage key CLI operations

View File

@ -250,17 +250,15 @@ def _run(cmd,
py_code = 'import os, json;' \
'print(json.dumps(os.environ.__dict__))'
if __grains__['os'] in ['MacOS', 'Darwin']:
env_cmd = ('sudo -i -u {1} -- "{2}"'
).format(shell, runas, sys.executable)
env_cmd = ('sudo', '-i', '-u', runas, '--',
sys.executable)
elif __grains__['os'] in ['FreeBSD']:
env_cmd = ('su - {1} -c "{0} -c \'{2}\'"'
).format(shell, runas, sys.executable)
env_cmd = ('su', '-', runas, '-c',
"{0} -c {1}".format(shell, sys.executable))
else:
env_cmd = ('su -s {0} - {1} -c "{2}"'
).format(shell, runas, sys.executable)
env_cmd = ('su', '-s', shell, '-', runas, '-c', sys.executable)
env_json = subprocess.Popen(
env_cmd,
shell=python_shell,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE
).communicate(py_code)[0]

View File

@ -83,7 +83,7 @@ def fire(data, tag):
salt '*' event.fire '{"data":"my event data"}' 'tag'
'''
try:
event = salt.utils.event.get_event('minion', # was __opts__['id']
event = salt.utils.event.get_event('minion', # was __opts__['id']
sock_dir=__opts__['sock_dir'],
transport=__opts__['transport'],
opts=__opts__,

View File

@ -9,7 +9,6 @@ Interact with virtual machine images via libguestfs
import os
import tempfile
import hashlib
import random
# Import Salt libs
import salt.utils
@ -49,7 +48,7 @@ def mount(location, access='rw'):
if os.listdir(root):
# Stuf is in there, don't use it
hash_type = getattr(hashlib, __opts__.get('hash_type', 'md5'))
rand = hash_type(str(random.randint(1, 1000000))).hexdigest()
rand = hash_type(os.urandom(32)).hexdigest()
root = os.path.join(
tempfile.gettempdir(),
'guest',

View File

@ -144,7 +144,7 @@ def db_create(name, user=None, password=None, host=None, port=None):
salt '*' influxdb.db_create <name>
salt '*' influxdb.db_create <name> <user> <password> <host> <port>
"""
if db_exists(name):
if db_exists(name, user, password, host, port):
log.info('DB {0!r} already exists'.format(name))
return False
client = _client(user=user, password=password, host=host, port=port)
@ -177,16 +177,19 @@ def db_remove(name, user=None, password=None, host=None, port=None):
salt '*' influxdb.db_remove <name>
salt '*' influxdb.db_remove <name> <user> <password> <host> <port>
"""
if not db_exists(name):
if not db_exists(name, user, password, host, port):
log.info('DB {0!r} does not exist'.format(name))
return False
client = _client(user=user, password=password, host=host, port=port)
return client.delete_database(name)
def user_list(database, user=None, password=None, host=None, port=None):
def user_list(database=None, user=None, password=None, host=None, port=None):
"""
List users of a InfluxDB database
List cluster admins or database users.
If a database is specified: it will return database users list.
If a database is not specified: it will return cluster admins list.
database
The database to list the users from
@ -207,18 +210,24 @@ def user_list(database, user=None, password=None, host=None, port=None):
.. code-block:: bash
salt '*' influxdb.user_list
salt '*' influxdb.user_list <database>
salt '*' influxdb.user_list <database> <user> <password> <host> <port>
"""
client = _client(user=user, password=password, host=host, port=port)
client.switch_db(database)
return client.get_database_users()
if database:
client.switch_db(database)
return client.get_database_users()
return client.get_list_cluster_admins()
def user_exists(
name, database, user=None, password=None, host=None, port=None):
name, database=None, user=None, password=None, host=None, port=None):
'''
Checks if a user exists for a InfluxDB database
Checks if a cluster admin or database user exists.
If a database is specified: it will check for database user existence.
If a database is not specified: it will check for cluster admin existence.
name
User name
@ -242,6 +251,7 @@ def user_exists(
.. code-block:: bash
salt '*' influxdb.user_exists <name>
salt '*' influxdb.user_exists <name> <database>
salt '*' influxdb.user_exists <name> <database> <user> <password> <host> <port>
'''
@ -251,10 +261,13 @@ def user_exists(
return name in [u['name'] for u in users]
def user_create(name, passwd, database, user=None, password=None, host=None,
port=None):
def user_create(name, passwd, database=None, user=None, password=None,
host=None, port=None):
"""
Create a InfluxDB user for a specific database
Create a cluster admin or a database user.
If a database is specified: it will create database user.
If a database is not specified: it will create a cluster admin.
name
User name for the new user to create
@ -281,24 +294,33 @@ def user_create(name, passwd, database, user=None, password=None, host=None,
.. code-block:: bash
salt '*' influxdb.user_create <name> <passwd>
salt '*' influxdb.user_create <name> <passwd> <database>
salt '*' influxdb.user_create <name> <passwd> <database> <user> <password> <host> <port>
"""
if user_exists(name, database):
log.info('User {0!r} already exists for DB {0!r}'.format(
name, database))
if user_exists(name, database, user, password, host, port):
if database:
log.info('User {0!r} already exists for DB {0!r}'.format(
name, database))
else:
log.info('Cluster admin {0!r} already exists'.format(name))
return False
client = _client(user=user, password=password, host=host, port=port)
client.switch_db(database)
return client.add_database_user(name, passwd)
if database:
client.switch_db(database)
return client.add_database_user(name, passwd)
return client.add_cluster_admin(name, passwd)
def user_chpass(dbuser, passwd, database, user=None, password=None, host=None,
port=None):
def user_chpass(name, passwd, database=None, user=None, password=None,
host=None, port=None):
"""
Change password for a InfluxDB database user
Change password for a cluster admin or a database user.
dbuser
If a database is specified: it will update database user password.
If a database is not specified: it will update cluster admin password.
name
User name for whom to change the password
passwd
@ -323,22 +345,31 @@ def user_chpass(dbuser, passwd, database, user=None, password=None, host=None,
.. code-block:: bash
salt '*' influxdb.user_chpass <dbuser> <passwd> <database>
salt '*' influxdb.user_chpass <dbuser> <passwd> <database> <user> <password> <host> <port>
salt '*' influxdb.user_chpass <name> <passwd>
salt '*' influxdb.user_chpass <name> <passwd> <database>
salt '*' influxdb.user_chpass <name> <passwd> <database> <user> <password> <host> <port>
"""
if not user_exists(dbuser, database):
log.info('User {0!r} does not exist for DB {0!r}'.format(
dbuser, database))
if not user_exists(name, database, user, password, host, port):
if database:
log.info('User {0!r} does not exist for DB {0!r}'.format(
name, database))
else:
log.info('Cluster admin {0!r} does not exist'.format(name))
return False
client = _client(user=user, password=password, host=host, port=port)
client.switch_db(database)
return client.update_database_user_password(dbuser, passwd)
if database:
client.switch_db(database)
return client.update_database_user_password(name, passwd)
return client.update_cluster_admin_password(name, passwd)
def user_remove(name, database, user=None, password=None, host=None,
def user_remove(name, database=None, user=None, password=None, host=None,
port=None):
"""
Remove a InfluxDB database user
Remove a cluster admin or a database user.
If a database is specified: it will remove the database user.
If a database is not specified: it will remove the cluster admin.
name
User name to remove
@ -365,16 +396,22 @@ def user_remove(name, database, user=None, password=None, host=None,
.. code-block:: bash
salt '*' influxdb.user_remove <name>
salt '*' influxdb.user_remove <name> <database>
salt '*' influxdb.user_remove <name> <database> <user> <password> <host> <port>
"""
if not user_exists(name, database):
log.info('User {0!r} does not exist for DB {0!r}'.format(
name, database))
if not user_exists(name, database, user, password, host, port):
if database:
log.info('User {0!r} does not exist for DB {0!r}'.format(
name, database))
else:
log.info('Cluster admin {0!r} does not exist'.format(name))
return False
client = _client(user=user, password=password, host=host, port=port)
client.switch_db(database)
return client.delete_database_user(user)
if database:
client.switch_db(database)
return client.delete_database_user(user)
return client.delete_cluster_admin(user)
def query(database, query, time_precision='s', chunked=False, user=None,

View File

@ -280,6 +280,9 @@ def _connect(**kwargs):
# Ensure MySQldb knows the format we use for queries with arguments
MySQLdb.paramstyle = 'pyformat'
if connargs.get('passwd', True) is None: # If present but set to None. (Extreme edge case.)
log.warning('MySQL password of None found. Attempting passwordless login.')
connargs.pop('passwd')
try:
dbc = MySQLdb.connect(**connargs)
except MySQLdb.OperationalError as exc:

View File

@ -9,7 +9,6 @@ minion.
# Import python libs
import os
import hashlib
import shutil
import signal
import logging
@ -112,14 +111,10 @@ def _sync(form, saltenv=None):
log.info('Copying {0!r} to {1!r}'.format(fn_, dest))
if os.path.isfile(dest):
# The file is present, if the sum differs replace it
hash_type = getattr(hashlib, __opts__.get('hash_type', 'md5'))
srch = hash_type(
salt.utils.fopen(fn_, 'r').read()
).hexdigest()
dsth = hash_type(
salt.utils.fopen(dest, 'r').read()
).hexdigest()
if srch != dsth:
hash_type = __opts__.get('hash_type', 'md5')
src_digest = salt.utils.get_hash(fn_, hash_type)
dst_digest = salt.utils.get_hash(dest, hash_type)
if src_digest != dst_digest:
# The downloaded file differs, replace!
shutil.copyfile(fn_, dest)
ret.append('{0}.{1}'.format(form, relname))

View File

@ -5,7 +5,6 @@ Module for managing timezone on POSIX-like systems.
# Import python libs
import os
import hashlib
import logging
import re
@ -164,17 +163,15 @@ def zone_compare(timezone):
if not os.path.exists(tzfile):
return 'Error: {0} does not exist.'.format(tzfile)
hash_type = getattr(hashlib, __opts__.get('hash_type', 'md5'))
hash_type = __opts__.get('hash_type', 'md5')
try:
with salt.utils.fopen(zonepath, 'r') as fp_:
usrzone = hash_type(fp_.read()).hexdigest()
usrzone = salt.utils.get_hash(zonepath, hash_type)
except IOError as exc:
raise SaltInvocationError('Invalid timezone {0!r}'.format(timezone))
try:
with salt.utils.fopen(tzfile, 'r') as fp_:
etczone = hash_type(fp_.read()).hexdigest()
etczone = salt.utils.get_hash(tzfile, hash_type)
except IOError as exc:
raise CommandExecutionError(
'Problem reading timezone file {0}: {1}'

View File

@ -384,7 +384,7 @@ class Pillar(object):
sls, exc
)
log.critical(msg)
errors.append(msg)
errors.append('Rendering SLS \'{0}\' failed. Please see master log for details.'.format(sls))
mods.add(sls)
nstate = None
if state:

View File

@ -14,7 +14,9 @@ import json
log = logging.getLogger(__name__)
def ext_pillar(minion_id, pillar, command):
def ext_pillar(minion_id, # pylint: disable=W0613
pillar, # pylint: disable=W0613
command):
'''
Execute a command and read the output as JSON
'''

View File

@ -16,7 +16,9 @@ import yaml
log = logging.getLogger(__name__)
def ext_pillar(minion_id, pillar, command):
def ext_pillar(minion_id, # pylint: disable=W0613
pillar, # pylint: disable=W0613
command):
'''
Execute a command and read the output as YAML
'''

View File

@ -19,7 +19,9 @@ from salt.utils.serializers.yamlex import deserialize
log = logging.getLogger(__name__)
def ext_pillar(minion_id, pillar, command):
def ext_pillar(minion_id, # pylint: disable=W0613
pillar, # pylint: disable=W0613
command):
'''
Execute a command and read the output as YAMLEX
'''

View File

@ -40,7 +40,10 @@ __opts__ = {'cobbler.url': 'http://localhost/cobbler_api',
log = logging.getLogger(__name__)
def ext_pillar(minion_id, pillar, key=None, only=()):
def ext_pillar(minion_id,
pillar, # pylint: disable=W0613
key=None,
only=()):
'''
Read pillar data from Cobbler via its API.
'''

View File

@ -114,16 +114,16 @@ def __virtual__():
return True
def ext_pillar(minion_id,
pillar,
def ext_pillar(minion_id, # pylint: disable=W0613
pillar, # pylint: disable=W0613
pillar_name,
project_path,
settings_module,
django_app,
env=None,
env_file=None,
*args,
**kwargs):
*args, # pylint: disable=W0613
**kwargs): # pylint: disable=W0613
'''
Connect to a Django database through the ORM and retrieve model fields

View File

@ -82,7 +82,9 @@ def __virtual__():
return __virtualname__ if HAS_LIBS else False
def ext_pillar(minion_id, pillar, conf): # pylint: disable=W0613
def ext_pillar(minion_id,
pillar, # pylint: disable=W0613
conf):
'''
Check etcd for all data
'''

View File

@ -56,7 +56,10 @@ __opts__ = {'foreman.url': 'http://foreman/api',
log = logging.getLogger(__name__)
def ext_pillar(minion_id, pillar, key=None, only=()):
def ext_pillar(minion_id,
pillar, # pylint: disable=W0613
key=None,
only=()):
'''
Read pillar data from Foreman via its API.
'''

View File

@ -25,7 +25,9 @@ def __virtual__():
return 'hiera' if salt.utils.which('hiera') else False
def ext_pillar(minion_id, pillar, conf):
def ext_pillar(minion_id, # pylint: disable=W0613
pillar, # pylint: disable=W0613
conf):
'''
Execute hiera and return the data
'''

View File

@ -14,7 +14,9 @@ import subprocess
import salt.utils
def ext_pillar(minion_id, pillar, command):
def ext_pillar(minion_id,
pillar, # pylint: disable=W0613
command): # pylint: disable=W0613
'''
Read in the generated libvirt keys
'''

View File

@ -84,7 +84,7 @@ log = logging.getLogger(__name__)
def ext_pillar(minion_id,
pillar,
pillar, # pylint: disable=W0613
collection='pillar',
id_field='_id',
re_pattern=None,

View File

@ -439,7 +439,10 @@ class merger(object):
d[k] = [d[k]]
def ext_pillar(minion_id, pillar, *args, **kwargs):
def ext_pillar(minion_id,
pillar, # pylint: disable=W0613
*args,
**kwargs):
'''
Execute queries, merge and return as a dict
'''

View File

@ -150,7 +150,9 @@ def _do_search(conf):
return result
def ext_pillar(minion_id, pillar, config_file):
def ext_pillar(minion_id, # pylint: disable=W0613
pillar, # pylint: disable=W0613
config_file):
'''
Execute LDAP searches and return the aggregated data
'''

View File

@ -16,7 +16,9 @@ import yaml
log = logging.getLogger(__name__)
def ext_pillar(minion_id, pillar, command):
def ext_pillar(minion_id,
pillar, # pylint: disable=W0613
command):
'''
Execute an unmodified puppet_node_classifier and read the output as YAML
'''

View File

@ -56,7 +56,9 @@ def ext_pillar(minion_id, pillar, function, **kwargs):
return globals()[function](minion_id, pillar, **kwargs)
def key_value(minion_id, pillar, pillar_key='redis_pillar'):
def key_value(minion_id,
pillar, # pylint: disable=W0613
pillar_key='redis_pillar'):
'''
Looks for key in redis matching minion_id, returns a structure based on the
data type of the redis key. String for string type, dict for hash type and
@ -89,7 +91,9 @@ def key_value(minion_id, pillar, pillar_key='redis_pillar'):
return {}
def key_json(minion_id, pillar, pillar_key=None):
def key_json(minion_id,
pillar, # pylint: disable=W0613
pillar_key=None):
'''
Pulls a string from redis and deserializes it from json. Deserialized
dictionary data loaded directly into top level if pillar_key is not set.

View File

@ -62,7 +62,6 @@ Multiple environment mode must have this bucket structure:
import logging
import os
import time
import hashlib
import pickle
import urllib
from copy import deepcopy
@ -88,8 +87,15 @@ class S3Credentials(object):
self.verify_ssl = verify_ssl
def ext_pillar(minion_id, pillar, bucket, key, keyid, verify_ssl,
multiple_env=False, environment='base', service_url=None):
def ext_pillar(minion_id,
pillar, # pylint: disable=W0613
bucket,
key,
keyid,
verify_ssl,
multiple_env=False,
environment='base',
service_url=None):
'''
Execute a command and read the output as YAML
'''
@ -330,12 +336,10 @@ def _get_file_from_s3(creds, metadata, saltenv, bucket, path,
file_md5 = filter(str.isalnum, file_meta['ETag']) \
if file_meta else None
cached_file_hash = hashlib.md5()
with salt.utils.fopen(cached_file_path, 'rb') as fp_:
cached_file_hash.update(fp_.read())
cached_md5 = salt.utils.get_hash(cached_file_path, 'md5')
# hashes match we have a cache hit
if cached_file_hash.hexdigest() == file_md5:
if cached_md5 == file_md5:
return
# ... or get the file from S3

View File

@ -153,7 +153,9 @@ def _extract_key_val(kv, delim='='):
return key, val
def ext_pillar(minion_id, pillar, repo_string):
def ext_pillar(minion_id,
pillar, # pylint: disable=W0613
repo_string):
'''
Execute a command and read the output as YAML
'''

View File

@ -174,7 +174,18 @@ class RunnerClient(mixins.SyncClientMixin, mixins.AsyncClientMixin, object):
self.opts['sock_dir'],
self.opts['transport'],
opts=self.opts)
job = self.master_call(**low)
# The master_call function here has a different function signature than
# on WheelClient. So extract all the eauth keys and the fun key and
# assume everything else is a kwarg to pass along to the runner
# function to be called.
auth_creds = dict([(i, low.pop(i))
for i in ['username', 'password', 'eauth', 'token'] if i in low])
reformatted_low = {'fun': low.pop('fun')}
reformatted_low.update(auth_creds)
reformatted_low['kwarg'] = low
job = self.master_call(**reformatted_low)
ret_tag = tagify('ret', base=job['tag'])
timelimit = time.time() + (timeout or 300)

10
salt/runners/test.py Normal file
View File

@ -0,0 +1,10 @@
def arg(*args, **kwargs):
'''
Output the given args and kwargs
'''
ret = {
'args': args,
'kwargs': kwargs,
}
print ret
return ret

View File

@ -10,10 +10,10 @@ Management of InfluxDB users
'''
def present(name, passwd, database, user=None, password=None, host=None,
def present(name, passwd, database=None, user=None, password=None, host=None,
port=None):
'''
Ensure that the user is present
Ensure that the cluster admin or database user is present.
name
The name of the user to manage
@ -43,7 +43,7 @@ def present(name, passwd, database, user=None, password=None, host=None,
'comment': ''}
# check if db does not exist
if not __salt__['influxdb.db_exists'](
if database and not __salt__['influxdb.db_exists'](
database, user, password, host, port):
ret['result'] = False
ret['comment'] = 'Database {0} does not exist'.format(database)
@ -73,9 +73,10 @@ def present(name, passwd, database, user=None, password=None, host=None,
return ret
def absent(name, database, user=None, password=None, host=None, port=None):
def absent(name, database=None, user=None, password=None, host=None,
port=None):
'''
Ensure that the named user is absent
Ensure that the named cluster admin or database user is absent.
name
The name of the user to remove

View File

@ -88,7 +88,7 @@ class RAETChannel(Channel):
not already setup such as in salt-call to communicate to-from the minion
'''
kind = self.opts.get('__role', '') # application kind 'master', 'minion', etc
kind = self.opts.get('__role', '') # application kind 'master', 'minion', etc
if not kind:
emsg = ("Missing opts['__role']. required to setup RAETChannel.")
log.error(emsg + "\n")
@ -101,14 +101,13 @@ class RAETChannel(Channel):
emsg = ("Missing opts['id']. required to setup RAETChannel.")
log.error(emsg + "\n")
raise ValueError(emsg)
lanename = role # add kind later
lanename = role # add kind later
else:
emsg = ("Unsupported application kind '{0}' for RAETChannel "
"Raet.".format(self.node))
log.error(emsg + '\n')
raise ValueError(emsg)
mid = self.opts.get('id', 'master')
uid = nacling.uuid(size=18)
name = 'channel' + uid

View File

@ -82,7 +82,6 @@ the following:
# Import python libs
from __future__ import print_function
import hashlib
import logging
import os
import re
@ -498,13 +497,8 @@ class PrintOption(Option):
result.append(gid)
elif arg == 'md5':
if stat.S_ISREG(fstat[stat.ST_MODE]):
with salt.utils.fopen(fullpath, 'rb') as ifile:
buf = ifile.read(8192)
md5hash = hashlib.md5()
while buf:
md5hash.update(buf)
buf = ifile.read(8192)
result.append(md5hash.hexdigest())
md5digest = salt.utils.get_hash(fullpath, 'md5')
result.append(md5digest)
else:
result.append('')

View File

@ -32,7 +32,7 @@ class SaltEvent(object):
'''
Set up the stack and remote yard
'''
self.node = node # application kind 'master', 'minion', 'syndic', 'call' etc
self.node = node # application kind 'master', 'minion', 'syndic', 'call' etc
self.sock_dir = sock_dir
self.listen = listen
if opts is None:
@ -44,14 +44,14 @@ class SaltEvent(object):
if self.node == 'master':
lanename = 'master'
if self.opts:
kind = self.opts.get('__role', '') # opts optional for master
kind = self.opts.get('__role', '') # opts optional for master
if kind and kind != self.node:
emsg = ("Mismatch between node '{}' and kind '{}' in setup "
"of SaltEvent on Raet.".format(self.node, kind))
emsg = ("Mismatch between node '{0}' and kind '{1}' in setup "
"of SaltEvent on Raet.".format(self.node, kind))
log.error(emsg + '\n')
raise ValueError(emsg)
elif self.node == 'minion':
role = self.opts.get('id', '') #opts required for minion
role = self.opts.get('id', '') # opts required for minion
if not role:
emsg = ("Missing opts['id'] required by SaltEvent on Raet with "
"node kind {0}.".format(self.node))
@ -59,11 +59,11 @@ class SaltEvent(object):
raise ValueError(emsg)
kind = self.opts.get('__role', '')
if kind != self.node:
emsg = ("Mismatch between node '{}' and kind '{}' in setup "
emsg = ("Mismatch between node '{0}' and kind '{1}' in setup "
"of SaltEvent on Raet.".format(self.node, kind))
log.error(emsg + '\n')
raise ValueError(emsg)
lanename = role # add '_minion'
lanename = role # add '_minion'
else:
emsg = ("Unsupported application node kind '{0}' for SaltEvent "
"Raet.".format(self.node))

View File

@ -55,6 +55,19 @@ def wrap_tmpl_func(render_str):
# We want explicit context to overwrite the **kws
kws.update(context)
context = kws
assert 'opts' in context
assert 'saltenv' in context
if 'sls' in context:
slspath = context['sls'].replace('.', '/')
if tmplpath is not None:
context['tplpath'] = tmplpath
if not tmplpath.lower().replace('\\', '/').endswith('/init.sls'):
slspath = os.path.dirname(slspath)
context['slsdotpath'] = slspath.replace('/', '.')
context['slscolonpath'] = slspath.replace('/', ':')
context['sls_path'] = slspath.replace('/', '_')
context['slspath'] = slspath
if isinstance(tmplsrc, string_types):
if from_str:

View File

@ -6,7 +6,6 @@ Wheel system wrapper for key system
# Import python libs
import os
import hashlib
import random
# Import salt libs
import salt.key
@ -79,7 +78,7 @@ def gen(id_=None, keysize=2048):
returned as a dict containing pub and priv keys
'''
if id_ is None:
id_ = hashlib.sha512(str(random.SystemRandom().randint(0, 99999999))).hexdigest()
id_ = hashlib.sha512(os.urandom(32)).hexdigest()
ret = {'priv': '',
'pub': ''}
priv = salt.crypt.gen_keys(__opts__['pki_dir'], id_, keysize)

223
setup.py
View File

@ -66,6 +66,7 @@ if 'USE_SETUPTOOLS' in os.environ or 'setuptools' in sys.modules:
from setuptools import setup
from setuptools.command.install import install
from setuptools.command.sdist import sdist
from setuptools.command.egg_info import egg_info
WITH_SETUPTOOLS = True
except ImportError:
WITH_SETUPTOOLS = False
@ -101,6 +102,10 @@ SALT_CLOUD_REQS = os.path.join(os.path.abspath(SETUP_DIRNAME), 'cloud-requiremen
SALT_RAET_REQS = os.path.join(os.path.abspath(SETUP_DIRNAME), 'raet-requirements.txt')
SALT_SYSPATHS = os.path.join(os.path.abspath(SETUP_DIRNAME), 'salt', 'syspaths.py')
# Salt SSH Packaging Detection
PACKAGED_FOR_SALT_SSH_FILE = os.path.join(os.path.abspath(SETUP_DIRNAME), '.salt-ssh-package')
PACKAGED_FOR_SALT_SSH = os.path.isfile(PACKAGED_FOR_SALT_SSH_FILE)
# pylint: disable=W0122
exec(compile(open(SALT_VERSION).read(), SALT_VERSION, 'exec'))
exec(compile(open(SALT_SYSPATHS).read(), SALT_SYSPATHS, 'exec'))
@ -150,8 +155,67 @@ class WriteSaltVersion(Command):
# pylint: enable=E0602
class WriteSaltSshPackaingFile(Command):
description = 'Write salt\'s ssh packaging file'
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
if not os.path.exists(PACKAGED_FOR_SALT_SSH_FILE):
# Write the salt-ssh packaging file
if getattr(self.distribution, 'salt_ssh_packaging_file', None) is None:
print 'This command is not meant to be called on it\'s own'
exit(1)
# pylint: disable=E0602
open(self.distribution.salt_ssh_packaging_file, 'w').write('Packaged for Salt-SSH\n')
# pylint: enable=E0602
if WITH_SETUPTOOLS is True:
class EggInfo(egg_info):
def finalize_options(self):
if getattr(self.distribution, 'packaged_for_salt_ssh', PACKAGED_FOR_SALT_SSH):
self.distribution.metadata.name = 'salt-ssh'
egg_info.finalize_options(self)
class Sdist(sdist):
user_options = sdist.user_options + [
('ssh-packaging', None, 'Prepare the salt-ssh packaging')
]
boolean_options = sdist.boolean_options + [
'ssh-packaging'
]
def initialize_options(self):
sdist.initialize_options(self)
self.ssh_packaging = PACKAGED_FOR_SALT_SSH
def finalize_options(self):
sdist.finalize_options(self)
self.distribution.packaged_for_salt_ssh = self.ssh_packaging
def make_release_tree(self, base_dir, files):
if self.ssh_packaging:
self.distribution.salt_ssh_packaging_file = PACKAGED_FOR_SALT_SSH_FILE
self.run_command('write-salt-ssh-packaging-file')
self.distribution.package_data.pop('salt.daemons.flo', None)
self.filelist.files.append(os.path.basename(PACKAGED_FOR_SALT_SSH_FILE))
self.distribution.metadata.name = 'salt-ssh'
self.distribution.data_files = [('share/man/man1',
['doc/man/salt-ssh.1',
'doc/man/salt-run.1',
'doc/man/salt-call.1',
'doc/man/salt-cloud.1']),
('share/man/man7', ['doc/man/salt.7'])]
sdist.make_release_tree(self, base_dir, files)
# Let's generate salt/_version.py to include in the sdist tarball
@ -161,16 +225,21 @@ class Sdist(sdist):
)
self.run_command('write-salt-version')
def make_distribution(self):
sdist.make_distribution(self)
if self.ssh_packaging:
os.unlink(PACKAGED_FOR_SALT_SSH_FILE)
class CloudSdist(Sdist):
user_options = sdist.user_options + [
user_options = Sdist.user_options + [
('download-bootstrap-script', None,
'Download the latest stable bootstrap-salt.sh script. This '
'can also be triggered by having `DOWNLOAD_BOOTSTRAP_SCRIPT=1` as an '
'environment variable.')
]
boolean_options = sdist.boolean_options + [
boolean_options = Sdist.boolean_options + [
'download-bootstrap-script'
]
@ -268,6 +337,18 @@ class CloudSdist(Sdist):
self.filelist.files.pop(
self.filelist.files.index(filename)
)
elif self.distribution.packaged_for_salt_ssh:
# Remove un-necessary script from Salt-SSH package
for filename in self.filelist.files[:]:
if not filename.startswith('scripts/'):
continue
if filename not in ('scripts/salt-ssh',
'scripts/salt-run',
'scripts/salt-call',
'scripts/salt-cloud'):
self.filelist.files.pop(
self.filelist.files.index(filename)
)
return Sdist.write_manifest(self)
@ -405,7 +486,10 @@ class Install(install):
# Non setuptools installation
self.distribution.install_requires = _parse_requirements_file(SALT_REQS)
# pylint: disable=E0602
self.salt_transport = 'zeromq'
if PACKAGED_FOR_SALT_SSH:
self.salt_transport = 'ssh'
else:
self.salt_transport = 'zeromq'
self.salt_root_dir = ROOT_DIR
self.salt_config_dir = CONFIG_DIR
self.salt_cache_dir = CACHE_DIR
@ -420,6 +504,9 @@ class Install(install):
def finalize_options(self):
install.finalize_options(self)
if PACKAGED_FOR_SALT_SSH and self.salt_transport != 'ssh':
raise DistutilsArgError('The only available transport for salt-ssh is \'ssh\'')
for optname in ('root_dir', 'config_dir', 'cache_dir', 'sock_dir',
'srv_root_dir', 'base_file_roots_dir',
'base_pillar_roots_dir', 'base_master_roots_dir',
@ -433,19 +520,18 @@ class Install(install):
)
setattr(self.distribution, 'salt_{0}'.format(optname), optvalue)
if self.salt_transport not in ('zeromq', 'raet', 'both', 'none'):
if self.salt_transport not in ('zeromq', 'raet', 'both', 'ssh', 'none'):
raise DistutilsArgError(
'The value of --salt-transport needs be \'zeromq\', '
'\'raet\', \'both\' or \'none\' not {0!r}'.format(
'\'raet\', \'both\' \'ssh\' or \'none\' not {0!r}'.format(
self.salt_transport
)
)
elif self.salt_transport == 'none':
elif self.salt_transport in ('ssh', 'none'):
for requirement in _parse_requirements_file(SALT_ZEROMQ_REQS):
if requirement not in self.distribution.install_requires:
continue
self.distribution.install_requires.remove(requirement)
elif self.salt_transport in ('raet', 'both'):
self.distribution.install_requires.extend(
_parse_requirements_file(SALT_RAET_REQS)
@ -489,8 +575,11 @@ class InstallLib(install_lib):
os.chmod(filename, 0755)
# <---- Custom Distutils/Setuptools Commands -------------------------------------------------------------------------
if PACKAGED_FOR_SALT_SSH:
NAME = 'salt-ssh'
else:
NAME = 'salt'
NAME = 'salt'
VER = __version__ # pylint: disable=E0602
DESC = 'Portable, distributed, remote execution and configuration management system'
@ -506,7 +595,8 @@ SETUP_KWARGS = {'name': NAME,
'build': Build,
'sdist': Sdist,
'install': Install,
'write-salt-version': WriteSaltVersion
'write-salt-version': WriteSaltVersion,
'write-salt-ssh-packaging-file': WriteSaltSshPackaingFile,
},
'classifiers': ['Programming Language :: Python',
'Programming Language :: Cython',
@ -598,23 +688,34 @@ SETUP_KWARGS = {'name': NAME,
'zip_safe': False
}
if PACKAGED_FOR_SALT_SSH:
SETUP_KWARGS['data_files'][0][1].extend([
'doc/man/salt-ssh.1',
'doc/man/salt-run.1',
'doc/man/salt-cloud.1',
])
if IS_WINDOWS_PLATFORM is False:
SETUP_KWARGS['cmdclass']['sdist'] = CloudSdist
SETUP_KWARGS['cmdclass']['install_lib'] = InstallLib
# SETUP_KWARGS['packages'].extend(['salt.cloud',
# 'salt.cloud.clouds'])
SETUP_KWARGS['package_data']['salt.cloud'] = ['deploy/*.sh']
SETUP_KWARGS['data_files'][0][1].extend([
'doc/man/salt-master.1',
'doc/man/salt-key.1',
'doc/man/salt.1',
'doc/man/salt-api.1',
'doc/man/salt-syndic.1',
'doc/man/salt-run.1',
'doc/man/salt-ssh.1',
'doc/man/salt-cloud.1',
'doc/man/salt-unity.1',
])
if PACKAGED_FOR_SALT_SSH is False:
SETUP_KWARGS['data_files'][0][1].extend([
'doc/man/salt-ssh.1',
'doc/man/salt-run.1',
'doc/man/salt-cloud.1',
])
# bbfreeze explicit includes
@ -714,23 +815,41 @@ if HAS_ESKY:
SETUP_KWARGS['options'] = OPTIONS
if WITH_SETUPTOOLS:
SETUP_KWARGS['entry_points'] = {
'console_scripts': ['salt-call = salt.scripts:salt_call',
'salt-cp = salt.scripts:salt_cp',
'salt-minion = salt.scripts:salt_minion',
]
}
if IS_WINDOWS_PLATFORM is False:
SETUP_KWARGS['entry_points']['console_scripts'].extend([
'salt = salt.scripts:salt_main',
'salt-api = salt.scripts:salt_api',
'salt-cloud = salt.scripts:salt_cloud',
'salt-key = salt.scripts:salt_key',
'salt-master = salt.scripts:salt_master',
'salt-run = salt.scripts:salt_run',
SETUP_KWARGS['cmdclass']['egg_info'] = EggInfo
if PACKAGED_FOR_SALT_SSH is False:
SETUP_KWARGS['entry_points'] = {
'console_scripts': ['salt-call = salt.scripts:salt_call',
'salt-cp = salt.scripts:salt_cp',
'salt-minion = salt.scripts:salt_minion',
]
}
else:
SETUP_KWARGS['entry_points'] = {'console_scripts': [
'salt-ssh = salt.scripts:salt_ssh',
'salt-syndic = salt.scripts:salt_syndic',
])
'salt-run = salt.scripts:salt_run',
'salt-call = salt.scripts:salt_call',
'salt-cloud = salt.scripts:salt_cloud',
]}
if IS_WINDOWS_PLATFORM is False:
if PACKAGED_FOR_SALT_SSH:
SETUP_KWARGS['entry_points']['console_scripts'].extend([
'salt = salt.scripts:salt_main',
'salt-api = salt.scripts:salt_api',
'salt-key = salt.scripts:salt_key',
'salt-master = salt.scripts:salt_master',
'salt-syndic = salt.scripts:salt_syndic',
])
else:
SETUP_KWARGS['entry_points']['console_scripts'].extend([
'salt = salt.scripts:salt_main',
'salt-api = salt.scripts:salt_api',
'salt-cloud = salt.scripts:salt_cloud',
'salt-key = salt.scripts:salt_key',
'salt-master = salt.scripts:salt_master',
'salt-run = salt.scripts:salt_run',
'salt-ssh = salt.scripts:salt_ssh',
'salt-syndic = salt.scripts:salt_syndic',
])
# Required for running the tests suite
SETUP_KWARGS['dependency_links'] = [
@ -741,23 +860,41 @@ if WITH_SETUPTOOLS:
# When WITH_SETUPTOOLS is True, esky builds would fail to include the scripts,
# and, if WITH_SETUPTOOLS is True, having scripts and console_scripts defined
# does not, apparently, break the build, so, let's have both
SETUP_KWARGS['scripts'] = ['scripts/salt-call',
'scripts/salt-cp',
'scripts/salt-minion',
'scripts/salt-unity',
]
if PACKAGED_FOR_SALT_SSH is False:
SETUP_KWARGS['scripts'] = ['scripts/salt-call',
'scripts/salt-cp',
'scripts/salt-minion',
'scripts/salt-unity',
]
if IS_WINDOWS_PLATFORM is False:
SETUP_KWARGS['scripts'].extend([
'scripts/salt',
'scripts/salt-api',
'scripts/salt-cloud',
'scripts/salt-key',
'scripts/salt-master',
'scripts/salt-run',
'scripts/salt-ssh',
'scripts/salt-syndic',
])
if PACKAGED_FOR_SALT_SSH:
SETUP_KWARGS['scripts'] = [
'scripts/salt-ssh',
'scripts/salt-run',
'scripts/salt-call',
'scripts/salt-cloud'
]
else:
SETUP_KWARGS['scripts'].extend([
'scripts/salt',
'scripts/salt-api',
'scripts/salt-cloud',
'scripts/salt-key',
'scripts/salt-master',
'scripts/salt-run',
'scripts/salt-ssh',
'scripts/salt-syndic',
])
if PACKAGED_FOR_SALT_SSH:
SETUP_KWARGS.pop('extras_require')
for requirement in _parse_requirements_file(SALT_ZEROMQ_REQS):
if requirement not in SETUP_KWARGS['install_requires']:
continue
SETUP_KWARGS['install_requires'].remove(requirement)
if __name__ == '__main__':
setup(**SETUP_KWARGS)

View File

@ -6,6 +6,7 @@ ensure_in_syspath('../../')
# Import salt libs
import integration
import os
class StdTest(integration.ModuleCase):
@ -36,6 +37,25 @@ class StdTest(integration.ModuleCase):
self.assertTrue(ret['minion'])
assert num_ret > 0
# ping a minion that doesnt exist, to make sure that it doesnt hang forever
# create fake mininion
key_file = os.path.join(self.master_opts['pki_dir'], 'minions', 'footest')
# touch the file
open(key_file, 'a').close()
# ping that minion and ensure it times out
try:
cmd_iter = self.client.cmd_cli(
'footest',
'test.ping',
)
num_ret = 0
for ret in cmd_iter:
num_ret += 1
self.assertTrue(ret['minion'])
assert num_ret == 0
finally:
os.unlink(key_file)
def test_iter(self):
'''
test cmd_iter

View File

@ -77,6 +77,18 @@ class RunnerModuleTest(integration.ClientCase):
self.runner.cmd_async(low)
def test_cmd_sync_w_arg(self):
low = {
'fun': 'test.arg',
'foo': 'Foo!',
'bar': 'Bar!',
}
low.update(self.eauth_creds)
ret = self.runner.cmd_sync(low)
self.assertEqual(ret['kwargs']['foo'], 'Foo!')
self.assertEqual(ret['kwargs']['bar'], 'Bar!')
if __name__ == '__main__':
from integration import run_tests

View File

@ -17,9 +17,7 @@ import re
import sys
import json
import time
import random
import shutil
import hashlib
import argparse
import requests
import subprocess
@ -89,8 +87,7 @@ def generate_vm_name(options):
if 'BUILD_NUMBER' in os.environ:
random_part = 'BUILD{0:0>6}'.format(os.environ.get('BUILD_NUMBER'))
else:
random_part = hashlib.md5(
str(random.randint(1, 100000000))).hexdigest()[:6]
random_part = os.urandom(3).encode('hex')
return '{0}-{1}-{2}'.format(options.vm_prefix, options.vm_source, random_part)

View File

@ -16,9 +16,7 @@ import re
import sys
import json
import time
import random
import shutil
import hashlib
import optparse
import subprocess
@ -95,8 +93,7 @@ def generate_vm_name(options):
if 'BUILD_NUMBER' in os.environ:
random_part = 'BUILD{0:0>6}'.format(os.environ.get('BUILD_NUMBER'))
else:
random_part = hashlib.md5(
str(random.randint(1, 100000000))).hexdigest()[:6]
random_part = os.urandom(3).encode('hex')
return '{0}-{1}-{2}'.format(options.vm_prefix, options.platform, random_part)

View File

@ -268,6 +268,17 @@ G:
list('ABCDEFG')
)
def test_slsdir(self):
result = render_sls('''
formula/woot.sls:
cmd.run:
- name: echo {{ slspath }}
- cwd: /
''', sls='formula.woot', argline='yaml . jinja')
r = result['formula/woot.sls']['cmd.run'][0]['name']
self.assertEqual(r, 'echo formula/woot')
if __name__ == '__main__':
from integration import run_tests