salt-0.17.5+ds.orig/0000755000175000017500000000000012270576532012235 5ustar joejoesalt-0.17.5+ds.orig/scripts/0000755000175000017500000000000012270576532013724 5ustar joejoesalt-0.17.5+ds.orig/scripts/salt-syndic0000755000175000017500000000025012263042153016066 0ustar joejoe#!/usr/bin/env python ''' This script is used to kick off a salt syndic daemon ''' from salt.scripts import salt_syndic if __name__ == '__main__': salt_syndic() salt-0.17.5+ds.orig/scripts/salt0000755000175000017500000000027012263042153014601 0ustar joejoe#!/usr/bin/env python ''' Publish commands to the salt system from the command line on the master. ''' from salt.scripts import salt_main if __name__ == '__main__': salt_main() salt-0.17.5+ds.orig/scripts/salt-minion0000755000175000017500000000053612263042153016075 0ustar joejoe#!/usr/bin/env python ''' This script is used to kick off a salt minion daemon ''' from salt.scripts import salt_minion from multiprocessing import freeze_support if __name__ == '__main__': # This handles the bootstrapping code that is included with frozen # scripts. It is a no-op on unfrozen code. freeze_support() salt_minion() salt-0.17.5+ds.orig/scripts/salt-master0000755000175000017500000000021112263042153016065 0ustar joejoe#!/usr/bin/env python ''' Start the salt-master ''' from salt.scripts import salt_master if __name__ == '__main__': salt_master() salt-0.17.5+ds.orig/scripts/salt-call0000755000175000017500000000031312263042153015510 0ustar joejoe#!/usr/bin/env python ''' Directly call a salt command in the modules, does not require a running salt minion to run. ''' from salt.scripts import salt_call if __name__ == '__main__': salt_call() salt-0.17.5+ds.orig/scripts/salt-cp0000755000175000017500000000026412263042153015204 0ustar joejoe#!/usr/bin/env python ''' Publish commands to the salt system from the command line on the master. ''' from salt.scripts import salt_cp if __name__ == '__main__': salt_cp() salt-0.17.5+ds.orig/scripts/salt-run0000755000175000017500000000022012263042153015376 0ustar joejoe#!/usr/bin/env python ''' Execute a salt convenience routine ''' from salt.scripts import salt_run if __name__ == '__main__': salt_run() salt-0.17.5+ds.orig/scripts/salt-key0000755000175000017500000000023212263042153015365 0ustar joejoe#!/usr/bin/env python ''' Manage the authentication keys with salt-key ''' from salt.scripts import salt_key if __name__ == '__main__': salt_key() salt-0.17.5+ds.orig/scripts/salt-ssh0000755000175000017500000000021112263042153015367 0ustar joejoe#!/usr/bin/env python ''' Execute the salt ssh system ''' from salt.scripts import salt_ssh if __name__ == '__main__': salt_ssh() salt-0.17.5+ds.orig/requirements.txt0000644000175000017500000000011112270576113015505 0ustar joejoeJinja2 M2Crypto msgpack-python pycrypto PyYAML pyzmq >= 2.1.9 markupsafe salt-0.17.5+ds.orig/salt/0000755000175000017500000000000012270576532013200 5ustar joejoesalt-0.17.5+ds.orig/salt/scripts.py0000644000175000017500000000454412270576114015244 0ustar joejoe# -*- coding: utf-8 -*- ''' This module contains the function calls to execute command line scipts ''' # Import python libs import os import sys # Import salt libs import salt import salt.cli def salt_master(): ''' Start the salt-master. ''' master = salt.Master() master.start() def salt_minion(): ''' Kick off a salt minion daemon. ''' if '' in sys.path: sys.path.remove('') minion = salt.Minion() minion.start() def salt_syndic(): ''' Kick off a salt syndic daemon. ''' pid = os.getpid() try: syndic = salt.Syndic() syndic.start() except KeyboardInterrupt: os.kill(pid, 15) def salt_key(): ''' Manage the authentication keys with salt-key. ''' try: saltkey = salt.cli.SaltKey() saltkey.run() except KeyboardInterrupt: raise SystemExit('\nExiting gracefully on Ctrl-c') def salt_cp(): ''' Publish commands to the salt system from the command line on the master. ''' try: cp_ = salt.cli.SaltCP() cp_.run() except KeyboardInterrupt: raise SystemExit('\nExiting gracefully on Ctrl-c') def salt_call(): ''' Directly call a salt command in the modules, does not require a running salt minion to run. ''' if '' in sys.path: sys.path.remove('') try: client = salt.cli.SaltCall() client.run() except KeyboardInterrupt: raise SystemExit('\nExiting gracefully on Ctrl-c') def salt_run(): ''' Execute a salt convenience routine. ''' if '' in sys.path: sys.path.remove('') try: client = salt.cli.SaltRun() client.run() except KeyboardInterrupt: raise SystemExit('\nExiting gracefully on Ctrl-c') def salt_ssh(): ''' Execute the salt-ssh system ''' if '' in sys.path: sys.path.remove('') try: client = salt.cli.SaltSSH() client.run() except KeyboardInterrupt: raise SystemExit('\nExiting gracefully on Ctrl-c') def salt_main(): ''' Publish commands to the salt system from the command line on the master. ''' if '' in sys.path: sys.path.remove('') try: client = salt.cli.SaltCMD() client.run() except KeyboardInterrupt: raise SystemExit('\nExiting gracefully on Ctrl-c') salt-0.17.5+ds.orig/salt/search/0000755000175000017500000000000012270576532014445 5ustar joejoesalt-0.17.5+ds.orig/salt/search/whoosh_search.py0000644000175000017500000000470112270576114017651 0ustar joejoe# -*- coding: utf-8 -*- ''' Routines to manage interactions with the whoosh search system ''' # Import python libs import os # Import salt libs import salt.search # Import third party libs HAS_WHOOSH = False try: import whoosh.index import whoosh.fields import whoosh.store import whoosh.qparser HAS_WHOOSH = True except ImportError: pass def __virtual__(): ''' Only load if the whoosh libs are available ''' return 'whoosh' if HAS_WHOOSH else False def index(): ''' Build the search index ''' schema = whoosh.fields.Schema( path=whoosh.fields.TEXT, # Path for sls files content=whoosh.fields.TEXT, # All content is indexed here env=whoosh.fields.ID, # The environment associated with a file fn_type=whoosh.fields.ID, # Set to pillar or state minion=whoosh.fields.ID, # The minion id associated with the content jid=whoosh.fields.ID, # The job id load=whoosh.fields.ID, # The load data ) index_dir = os.path.join(__opts__['cachedir'], 'whoosh') if not os.path.isdir(index_dir): os.makedirs(index_dir) if whoosh.index.exists_in(index_dir): ix_ = whoosh.index.open_dir(index_dir) else: ix_ = whoosh.index.create_in(index_dir, schema) try: writer = ix_.writer() except whoosh.store.LockError: return False for data in salt.search.iter_roots(__opts__['file_roots']): for chunk in data: writer.add_document(fn_type=u'file', **chunk) for data in salt.search.iter_roots(__opts__['pillar_roots']): for chunk in data: writer.add_document(fn_type=u'pillar', **chunk) for data in salt.search.iter_ret(__opts__, __ret__): writer.add_document(jid=data['jid'], load=data['load']) for minion in data['ret']: writer.add_document( jid=data['jid'], minion=minion, content=data['ret'][minion]) writer.commit() def query(qstr, limit=10): ''' Execute a query ''' index_dir = os.path.join(__opts__['cachedir'], 'whoosh') if whoosh.index.exists_in(index_dir): ix_ = whoosh.index.open_dir(index_dir) else: return {} qp_ = whoosh.qparser.QueryParser(u'content', schema=ix_.schema) qobj = qp_.parse(unicode(qstr), limit) with ix_.searcher() as searcher: return searcher.search(qobj) salt-0.17.5+ds.orig/salt/search/__init__.py0000644000175000017500000000564112270576114016560 0ustar joejoe# -*- coding: utf-8 -*- ''' Set up the correct search system ''' # Import python libs import os # Import salt libs import salt.minion import salt.loader import salt.utils def iter_ret(opts, ret): ''' Yield returner data if the external job cache is enabled ''' if not opts['ext_job_cache']: raise StopIteration get_load = '{0}.get_load'.format(opts['ext_job_cache']) get_jid = '{0}.get_jid'.format(opts['ext_job_cache']) get_jids = '{0}.get_jids'.format(opts['ext_job_cache']) if get_load not in ret: raise StopIteration else: get_load = ret[get_load] if get_jid not in ret: raise StopIteration else: get_jid = ret[get_jid] if get_jids not in ret: raise StopIteration else: get_jids = ret[get_jids] for jid in get_jids(): jids = {} jids['load'] = get_load(jid) jids['ret'] = get_jid(jid) jids['jid'] = jid yield jids def _iter_dir(dir_, env): ret = [] for fn_ in os.listdir(dir_): path = os.path.join(dir_, fn_) if os.path.isdir(path): yield _iter_dir(path, env) elif os.path.isfile(path): with salt.utils.fopen(path) as fp_: if salt.utils.istextfile(fp_): ret.append( {'path': unicode(path), 'env': unicode(env), 'content': unicode(fp_.read())} ) else: ret.append( {'path': unicode(path), 'env': unicode(env), 'content': u'bin'} ) yield ret def iter_roots(roots): ''' Accepts the file_roots or the pillar_roots structures and yields {'path': , 'env': , 'cont': } ''' for env, dirs in roots.items(): for dir_ in dirs: if not os.path.isdir(dir_): continue for ret in _iter_dir(dir_, env): yield ret class Search(object): ''' Set up the object than manages search operations ''' def __init__(self, opts): self.opts = opts self.mminion = salt.minion.MasterMinion( self.opts, states=False, rend=False, matcher=False) self.search = salt.loader.search(self.opts, self.mminion.returners) def index(self): ''' Execute a search index run ''' ifun = '{0}.index'.format(self.opts.get('search', '')) if ifun not in self.search: return return self.search[ifun]() def query(self, term): ''' Search the index for the given term ''' qfun = '{0}.query'.format(self.opts.get('search', '')) if qfun not in self.search: return return self.search[qfun](term) salt-0.17.5+ds.orig/salt/modules/0000755000175000017500000000000012270576532014650 5ustar joejoesalt-0.17.5+ds.orig/salt/modules/zpool.py0000644000175000017500000001566712263042153016371 0ustar joejoe# -*- coding: utf-8 -*- ''' Module for running ZFS zpool command ''' # Import Python libs import os import logging # Import Salt libs import salt.utils import salt.utils.decorators as decorators log = logging.getLogger(__name__) @decorators.memoize def _check_zpool(): ''' Looks to see if zpool is present on the system ''' return salt.utils.which('zpool') @decorators.memoize def _check_mkfile(): ''' Looks to see if mkfile is present on the system ''' return salt.utils.which('mkfile') def __virtual__(): ''' Provides zpool. ''' if _check_zpool(): return 'zpool' return False def status(name=''): ''' Return the status of the named zpool CLI Example: .. code-block:: bash salt '*' zpool.status ''' zpool = _check_zpool() res = __salt__['cmd.run']('{0} status {1}'.format(zpool, name)) ret = res.splitlines() return ret def iostat(name=''): ''' Display I/O statistics for the given pools CLI Example: .. code-block:: bash salt '*' zpool.iostat ''' zpool = _check_zpool() res = __salt__['cmd.run']('{0} iostat -v {1}'.format(zpool, name)) ret = res.splitlines() return ret def zpool_list(): ''' Return a list of all pools in the system with health status and space usage CLI Example: .. code-block:: bash salt '*' zpool.zpool_list ''' zpool = _check_zpool() res = __salt__['cmd.run']('{0} list'.format(zpool)) pool_list = [l for l in res.splitlines()] return {'pools': pool_list} def exists(pool_name): ''' Check if a ZFS storage pool is active CLI Example: .. code-block:: bash salt '*' zpool.exists myzpool ''' current_pools = zpool_list() for pool in current_pools['pools']: if pool_name in pool: return True return None def destroy(pool_name): ''' Destroys a storage pool CLI Example: .. code-block:: bash salt '*' zpool.destroy myzpool ''' ret = {} if exists(pool_name): zpool = _check_zpool() cmd = '{0} destroy {1}'.format(zpool, pool_name) __salt__['cmd.run'](cmd) if not exists(pool_name): ret[pool_name] = "Deleted" return ret else: ret['Error'] = 'Storage pool {0} does not exist'.format(pool_name) def scrub(pool_name=None): ''' Begin a scrub CLI Example: .. code-block:: bash salt '*' zpool.scrub myzpool ''' ret = {} if not pool_name: ret['Error'] = 'zpool name parameter is mandatory.' return ret if exists(pool_name): zpool = _check_zpool() cmd = '{0} scrub {1}'.format(zpool, pool_name) res = __salt__['cmd.run'](cmd) ret[pool_name] = res.splitlines() return ret else: ret['Error'] = 'Storage pool {0} does not exist'.format(pool_name) def create(pool_name, *vdevs): ''' Create a new storage pool CLI Example: .. code-block:: bash salt '*' zpool.create myzpool /path/to/vdev1 [/path/to/vdev2] [...] ''' ret = {} dlist = [] # Check if the pool_name is already being used if exists(pool_name): ret['Error'] = 'Storage Pool `{0}` already exists'.format(pool_name) return ret # make sure files are present on filesystem for vdev in vdevs: if not os.path.isfile(vdev): # File is not there error and return ret[vdev] = '{0} not present on filesystem'.format(vdev) return ret else: dlist.append(vdev) devs = ' '.join(dlist) zpool = _check_zpool() cmd = '{0} create {1} {2}'.format(zpool, pool_name, devs) # Create storage pool __salt__['cmd.run'](cmd) # Check and see if the pools is available if exists(pool_name): ret[pool_name] = 'created' return ret else: ret['Error'] = 'Unable to create storage pool {0}'.format(pool_name) return ret def add(pool_name, vdev): ''' Add the specified vdev to the given pool CLI Example: .. code-block:: bash salt '*' zpool.add myzpool /path/to/vdev ''' ret = {} # check for pool if not exists(pool_name): ret['Error'] = 'Can\'t add {0} to {1} pool is not available'.format( pool_name, vdev) return ret # check device is a file if not os.path.isfile(vdev): ret['Error'] = '{0} not on filesystem'.format(vdev) return ret # try and add watch out for mismatched replication levels zpool = _check_zpool() cmd = '{0} add {1} {2}'.format(zpool, pool_name, vdev) res = __salt__['cmd.run'](cmd) if not 'errors' in res.splitlines(): ret['Added'] = '{0} to {1}'.format(vdev, pool_name) return ret ret['Error'] = 'Something went wrong add {0} to {1}'.format(vdev, pool_name) def replace(pool_name, old, new): ''' Replaces old device with new device. CLI Example: .. code-block:: bash salt '*' zpool.replace myzpool /path/to/vdev1 /path/to/vdev2 ''' ret = {} # Make sure pools there if not exists(pool_name): ret['Error'] = '{0}: pool does not exists.'.format(pool_name) return ret # make sure old, new vdevs are on filesystem if not os.path.isfile(old): ret['Error'] = '{0}: is not on the file system.'.format(old) return ret if not os.path.isfile(new): ret['Error'] = '{0}: is not on the file system.'.format(new) return ret # Replace vdevs zpool = _check_zpool() cmd = '{0} replace {1} {2} {3}'.format(zpool, pool_name, old, new) __salt__['cmd.run'](cmd) # check for new vdev in pool res = status(name=pool_name) for line in res: if new in line: ret['replaced'] = '{0} with {1}'.format(old, new) return ret ret['Error'] = 'Does not look like devices where swapped check status' return ret def create_file_vdev(size, *vdevs): ''' Creates file based ``virtual devices`` for a zpool ``*vdevs`` is a list of full paths for mkfile to create CLI Example: .. code-block:: bash salt '*' zpool.create_file_vdev 7g /path/to/vdev1 [/path/to/vdev2] [...] Depending on file size this may take a while to return ''' ret = {} if not _check_mkfile(): return False dlist = [] # Get file names to create for vdev in vdevs: # check if file is present if not add it if os.path.isfile(vdev): ret[vdev] = 'File: {0} already present'.format(vdev) else: dlist.append(vdev) devs = ' '.join(dlist) mkfile = _check_mkfile() cmd = '{0} {1} {2}'.format(mkfile, size, devs) __salt__['cmd.run'](cmd) # Makesure the files are there for vdev in vdevs: if not os.path.isfile(vdev): ret[vdev] = 'The vdev can\'t be created' ret['status'] = True ret[cmd] = cmd return ret salt-0.17.5+ds.orig/salt/modules/system.py0000644000175000017500000000270712263042153016541 0ustar joejoe# -*- coding: utf-8 -*- ''' Support for reboot, shutdown, etc ''' import salt.utils def __virtual__(): ''' Only supported on POSIX-like systems ''' if salt.utils.is_windows() or not salt.utils.which('shutdown'): return False return 'system' def halt(): ''' Halt a running system CLI Example: .. code-block:: bash salt '*' system.halt ''' cmd = 'halt' ret = __salt__['cmd.run'](cmd) return ret def init(runlevel): ''' Change the system runlevel on sysV compatible systems CLI Example: .. code-block:: bash salt '*' system.init 3 ''' cmd = 'init {0}'.format(runlevel) ret = __salt__['cmd.run'](cmd) return ret def poweroff(): ''' Poweroff a running system CLI Example: .. code-block:: bash salt '*' system.poweroff ''' cmd = 'poweroff' ret = __salt__['cmd.run'](cmd) return ret def reboot(): ''' Reboot the system using the 'reboot' command CLI Example: .. code-block:: bash salt '*' system.reboot ''' cmd = 'reboot' ret = __salt__['cmd.run'](cmd) return ret def shutdown(at_time=None): ''' Shutdown a running system CLI Example: .. code-block:: bash salt '*' system.shutdown ''' if at_time: cmd = 'shutdown -h {0}'.format(at_time) else: cmd = 'shutdown -h now' ret = __salt__['cmd.run'](cmd) return ret salt-0.17.5+ds.orig/salt/modules/logrotate.py0000644000175000017500000001205112263042153017206 0ustar joejoe# -*- coding: utf-8 -*- ''' Module for managing logrotate. ''' # Import python libs import os import logging # Import salt libs import salt.utils log = logging.getLogger(__name__) default_conf = '/etc/logrotate.conf' # Define a function alias in order not to shadow built-in's __func_alias__ = { 'set_': 'set' } def __virtual__(): ''' Only work on POSIX-like systems ''' if salt.utils.is_windows(): return False return 'logrotate' def _parse_conf(conf_file=default_conf): ''' Parse a logrotate configuration file. Includes will also be parsed, and their configuration will be stored in the return dict, as if they were part of the main config file. A dict of which configs came from which includes will be stored in the 'include files' dict inside the return dict, for later reference by the user or module. ''' ret = {} mode = 'single' multi_name = '' multi = {} with salt.utils.fopen(conf_file, 'r') as ifile: for line in ifile: line = line.strip() if not line: continue if line.startswith('#'): continue comps = line.split() if '{' in line and '}' not in line: mode = 'multi' multi_name = comps[0] continue if '}' in line: mode = 'single' ret[multi_name] = multi multi_name = '' multi = {} continue if mode == 'single': key = ret else: key = multi if comps[0] == 'include': if 'include files' not in ret: ret['include files'] = {} for include in os.listdir(comps[1]): if include not in ret['include files']: ret['include files'][include] = [] include_path = '{0}/{1}'.format(comps[1], include) include_conf = _parse_conf(include_path) for file_key in include_conf: ret[file_key] = include_conf[file_key] ret['include files'][include].append(file_key) if len(comps) > 1: key[comps[0]] = ' '.join(comps[1:]) else: key[comps[0]] = True return ret def show_conf(conf_file=default_conf): ''' Show parsed configuration CLI Example: .. code-block:: bash salt '*' logrotate.show_conf ''' return _parse_conf(conf_file) def set_(key, value, setting=None, conf_file=default_conf): ''' Set a new value for a specific configuration line CLI Example: .. code-block:: bash salt '*' logrotate.set rotate 2 Can also be used to set a single value inside a multiline configuration block. For instance, to change rotate in the following block:: /var/log/wtmp { monthly create 0664 root root rotate 1 } Use the following command: .. code-block:: bash salt '*' logrotate.set /var/log/wtmp rotate 2 This module also has the ability to scan files inside an include directory, and make changes in the appropriate file. ''' conf = _parse_conf(conf_file) for include in conf['include files']: if key in conf['include files'][include]: conf_file = os.path.join(conf['include'], include) if isinstance(conf[key], dict) and not setting: return ( 'Error: {0} includes a dict, and a specific setting inside the ' 'dict was not declared'.format(key) ) if setting: if isinstance(conf[key], str): return ('Error: A setting for a dict was declared, but the ' 'configuration line given is not a dict') # We're going to be rewriting an entire stanza stanza = conf[key] if value == 'False': del stanza[value] else: stanza[value] = setting new_line = _dict_to_stanza(key, stanza) log.debug(stanza) log.debug(new_line) log.debug(key) __salt__['file.psed'](conf_file, '{0}.*{{.*}}'.format(key), new_line) else: # This is the new config line that will be set if value == 'True': new_line = key elif value == 'False': new_line = '' else: new_line = '{0} {1}'.format(key, value) log.debug(conf_file) log.debug(key) log.debug(new_line) __salt__['file.psed'](conf_file, '^{0}.*'.format(key), new_line, flags='gM') def _dict_to_stanza(key, stanza): ''' Convert a dict to a multi-line stanza ''' ret = '' for skey in stanza: if stanza[skey] is True: stanza[skey] = '' ret += ' {0} {1}\n'.format(skey, stanza[skey]) return '{0} {{\n{1}}}'.format(key, ret) salt-0.17.5+ds.orig/salt/modules/layman.py0000644000175000017500000000660212263042153016474 0ustar joejoe# -*- coding: utf-8 -*- ''' Support for Layman ''' import salt.utils def __virtual__(): ''' Only work on Gentoo systems with layman installed ''' if __grains__['os'] == 'Gentoo' and salt.utils.which('layman'): return 'layman' return False def _get_makeconf(): ''' Find the correct make.conf. Gentoo recently moved the make.conf but still supports the old location, using the old location first ''' old_conf = '/etc/make.conf' new_conf = '/etc/portage/make.conf' if __salt__['file.file_exists'](old_conf): return old_conf elif __salt__['file.file_exists'](new_conf): return new_conf def add(overlay): ''' Add the given overlay from the caced remote list to your locally installed overlays. Specify 'ALL' to add all overlays from the remote list. Return a list of the new overlay(s) added: CLI Example: .. code-block:: bash salt '*' layman.add ''' ret = list() old_overlays = list_local() cmd = 'layman --quietness=0 --add {0}'.format(overlay) __salt__['cmd.retcode'](cmd) new_overlays = list_local() # If we did not have any overlays before and we successfully added # a new one. We need to ensure the make.conf is sourcing layman's # make.conf so emerge can see the overlays if len(old_overlays) == 0 and len(new_overlays) > 0: srcline = 'source /var/lib/layman/make.conf' makeconf = _get_makeconf() if not __salt__['file.contains'](makeconf, 'layman'): __salt__['file.append'](makeconf, srcline) ret = [overlay for overlay in new_overlays if overlay not in old_overlays] return ret def delete(overlay): ''' Remove the given overlay from the your locally installed overlays. Specify 'ALL' to remove all overlays. Return a list of the overlays(s) that were removed: CLI Example: .. code-block:: bash salt '*' layman.delete ''' ret = list() old_overlays = list_local() cmd = 'layman --quietness=0 --delete {0}'.format(overlay) __salt__['cmd.retcode'](cmd) new_overlays = list_local() # If we now have no overlays added, We need to ensure that the make.conf # does not source layman's make.conf, as it will break emerge if len(new_overlays) == 0: srcline = 'source /var/lib/layman/make.conf' makeconf = _get_makeconf() if __salt__['file.contains'](makeconf, 'layman'): __salt__['file.sed'](makeconf, srcline, '') ret = [overlay for overlay in old_overlays if overlay not in new_overlays] return ret def sync(overlay='ALL'): ''' Update the specified overlay. Use 'ALL' to synchronize all overlays. This is the default if no overlay is specified. overlay Name of the overlay to sync. (Defaults to 'ALL') CLI Example: .. code-block:: bash salt '*' layman.sync ''' cmd = 'layman --quietness=0 --sync {0}'.format(overlay) return __salt__['cmd.retcode'](cmd) == 0 def list_local(): ''' List the locally installed overlays. Return a list of installed overlays: CLI Example: .. code-block:: bash salt '*' layman.list_local ''' cmd = 'layman --quietness=1 --list-local --nocolor' out = __salt__['cmd.run'](cmd).split('\n') ret = [line.split()[1] for line in out if len(line.split()) > 2] return ret salt-0.17.5+ds.orig/salt/modules/test.py0000644000175000017500000002103312270576114016174 0ustar joejoe# -*- coding: utf-8 -*- ''' Module for running arbitrary tests ''' # Import Python libs import os import sys import time import random # Import Salt libs import salt import salt.version import salt.loader def echo(text): ''' Return a string - used for testing the connection CLI Example: .. code-block:: bash salt '*' test.echo 'foo bar baz quo qux' ''' return text def ping(): ''' Just used to make sure the minion is up and responding Return True CLI Example: .. code-block:: bash salt '*' test.ping ''' return True def sleep(length): ''' Instruct the minion to initiate a process that will sleep for a given period of time. CLI Example: .. code-block:: bash salt '*' test.sleep 20 ''' time.sleep(int(length)) return True def rand_sleep(max=60): ''' Sleep for a random number of seconds, used to test long-running commands and minions returning at differing intervals CLI Example: .. code-block:: bash salt '*' test.rand_sleep 60 ''' time.sleep(random.randint(0, max)) return True def version(): ''' Return the version of salt on the minion CLI Example: .. code-block:: bash salt '*' test.version ''' return salt.__version__ def versions_information(): ''' Returns versions of components used by salt as a dict CLI Example: .. code-block:: bash salt '*' test.versions_information ''' return dict(salt.version.versions_information()) def versions_report(): ''' Returns versions of components used by salt CLI Example: .. code-block:: bash salt '*' test.versions_report ''' return '\n'.join(salt.version.versions_report()) def conf_test(): ''' Return the value for test.foo in the minion configuration file, or return the default value CLI Example: .. code-block:: bash salt '*' test.conf_test ''' return __salt__['config.option']('test.foo') def get_opts(): ''' Return the configuration options passed to this minion CLI Example: .. code-block:: bash salt '*' test.get_opts ''' return __opts__ def cross_test(func, args=None): ''' Execute a minion function via the __salt__ object in the test module, used to verify that the minion functions can be called via the __salt__ module. CLI Example: .. code-block:: bash salt '*' test.cross_test file.gid_to_group 0 ''' if args is None: args = [] return __salt__[func](*args) def kwarg(**kwargs): ''' Print out the data passed into the function ``**kwargs``, this is used to both test the publication data and cli kwarg passing, but also to display the information available within the publication data. CLI Example: .. code-block:: bash salt '*' test.kwarg num=1 txt="two" env='{a: 1, b: "hello"}' ''' return kwargs def arg(*args, **kwargs): ''' Print out the data passed into the function ``*args`` and ```kwargs``, this is used to both test the publication data and cli argument passing, but also to display the information available within the publication data. Returns {"args": args, "kwargs": kwargs}. CLI Example: .. code-block:: bash salt '*' test.arg 1 "two" 3.1 txt="hello" wow='{a: 1, b: "hello"}' ''' return {"args": args, "kwargs": kwargs} def arg_type(*args, **kwargs): ''' Print out the types of the args and kwargs. This is used to test the types of the args and kwargs passed down to the minion CLI Example: .. code-block:: bash salt '*' test.arg_type 1 'int' ''' ret = {'args': [], 'kwargs': {}} # all the args for arg in args: ret['args'].append(str(type(arg))) # all the kwargs for key, val in kwargs.iteritems(): ret['kwargs'][key] = str(type(val)) return ret def arg_repr(*args, **kwargs): ''' Print out the data passed into the function ``*args`` and ```kwargs``, this is used to both test the publication data and cli argument passing, but also to display the information available within the publication data. Returns {"args": repr(args), "kwargs": repr(kwargs)}. CLI Example: .. code-block:: bash salt '*' test.arg_repr 1 "two" 3.1 txt="hello" wow='{a: 1, b: "hello"}' ''' return {"args": repr(args), "kwargs": repr(kwargs)} def fib(num): ''' Return a Fibonacci sequence up to the passed number, and the timeit took to compute in seconds. Used for performance tests CLI Example: .. code-block:: bash salt '*' test.fib 3 ''' num = int(num) start = time.time() fib_a, fib_b = 0, 1 ret = [0] while fib_b < num: ret.append(fib_b) fib_a, fib_b = fib_b, fib_a + fib_b return ret, time.time() - start def collatz(start): ''' Execute the collatz conjecture from the passed starting number, returns the sequence and the time it took to compute. Used for performance tests. CLI Example: .. code-block:: bash salt '*' test.collatz 3 ''' start = int(start) begin = time.time() steps = [] while start != 1: steps.append(start) if start > 1: if start % 2 == 0: start = start / 2 else: start = start * 3 + 1 return steps, time.time() - begin def outputter(data): ''' Test the outputter, pass in data to return CLI Example: .. code-block:: bash salt '*' test.outputter foobar ''' return data def retcode(code=42): ''' Test that the returncode system is functioning correctly CLI Example: .. code-block:: bash salt '*' test.retcode 42 ''' __context__['retcode'] = code return True def provider(module): ''' Pass in a function name to discover what provider is being used CLI Example: .. code-block:: bash salt '*' test.provider service ''' func = '' for key in __salt__: if not key.startswith('{0}.'.format(module)): continue func = key break if not func: return '' pfn = sys.modules[__salt__[func].__module__].__file__ pfn = os.path.basename(pfn) return pfn[:pfn.rindex('.')] def providers(): ''' Return a dict of the provider names and the files that provided them CLI Example: .. code-block:: bash salt '*' test.providers ''' ret = {} for funcname in __salt__: modname = funcname.split('.')[0] if modname not in ret: ret[provider(modname)] = modname return ret def not_loaded(): ''' List the modules that were not loaded by the salt loader system CLI Example: .. code-block:: bash salt '*' test.not_loaded ''' prov = providers() ret = set() loader = salt.loader._create_loader(__opts__, 'modules', 'module') for mod_dir in loader.module_dirs: if not os.path.isabs(mod_dir): continue if not os.path.isdir(mod_dir): continue for fn_ in os.listdir(mod_dir): if fn_.startswith('_'): continue name = fn_.split('.')[0] if name not in prov: ret.add(name) return sorted(ret) def opts_pkg(): ''' Return an opts package with the grains and opts for this minion. This is primarily used to create the options used for master side state compiling routines CLI Example: .. code-block:: bash salt '*' test.opts_pkg ''' ret = {} ret.update(__opts__) ret['grains'] = __grains__ return ret def tty(device, echo=None): ''' Echo a string to a specific tty CLI Example: .. code-block:: bash salt '*' test.tty tty0 'This is a test' salt '*' test.tty pts3 'This is a test' ''' if device.startswith('tty'): teletype = '/dev/{0}'.format(device) elif device.startswith('pts'): teletype = '/dev/{0}'.format(device.replace('pts', 'pts/')) else: return {'Error': 'The specified device is not a valid TTY'} cmd = 'echo {0} > {1}'.format(echo, teletype) ret = __salt__['cmd.run_all'](cmd) if ret['retcode'] == 0: return { 'Success': 'Message was successfully echoed to {0}'.format(teletype) } else: return { 'Error': 'Echoing to {0} returned error code {1}'.format( teletype, ret['retcode']) } salt-0.17.5+ds.orig/salt/modules/qemu_img.py0000644000175000017500000000206512263042153017015 0ustar joejoe# -*- coding: utf-8 -*- ''' Qemu-img Command Wrapper ======================== The qemu img command is wrapped for specific functions :depends: qemu-img ''' # Import python libs import os # Import salt libs import salt.utils def __virtual__(): ''' Only load if qemu-img is installed ''' if salt.utils.which('qemu-img'): return 'qemu_img' return False def make_image(location, size, fmt): ''' Create a blank virtual machine image file of the specified size in megabytes. The image can be created in any format supported by qemu CLI Example: .. code-block:: bash salt '*' qemu_img.make_image /tmp/image.qcow 2048 qcow2 salt '*' qemu_img.make_image /tmp/image.raw 10240 raw ''' if not os.path.isabs(location): return '' if not os.path.isdir(os.path.dirname(location)): return '' if not __salt__['cmd.retcode']( 'qemu-img create -f {0} {1} {2}M'.format( fmt, location, size)): return location return '' salt-0.17.5+ds.orig/salt/modules/pkgng.py0000644000175000017500000005041012270576114016324 0ustar joejoe# -*- coding: utf-8 -*- ''' Support for pkgng ''' # Import python libs import os # Import salt libs import salt.utils def __virtual__(): ''' Pkgng module load on FreeBSD only. ''' if __grains__['os'] == 'FreeBSD': return 'pkgng' else: return False def parse_config(file_name='/usr/local/etc/pkg.conf'): ''' Return dict of uncommented global variables. CLI Example: .. code-block:: bash salt '*' pkgng.parse_config ``NOTE:`` not working properly right now ''' ret = {} if not os.path.isfile(file_name): return 'Unable to find {0} on file system'.format(file_name) with salt.utils.fopen(file_name) as ifile: for line in ifile: if line.startswith('#') or line.startswith('\n'): pass else: key, value = line.split('\t') ret[key] = value ret['config_file'] = file_name return ret def version(): ''' Displays the current version of pkg CLI Example: .. code-block:: bash salt '*' pkgng.version ''' cmd = 'pkg -v' return __salt__['cmd.run'](cmd) def latest_version(pkg_name, **kwargs): ''' The available version of the package in the repository CLI Example: .. code-block:: bash salt '*' pkgng.latest_version ''' kwargs.pop('refresh', True) cmd = 'pkg info {0}'.format(pkg_name) out = __salt__['cmd.run'](cmd).split() return out[0] # available_version is being deprecated available_version = latest_version def update_package_site(new_url): ''' Updates remote package repo URL, PACKAGESITE var to be exact. Must be using http://, ftp://, or https// protos CLI Example: .. code-block:: bash salt '*' pkgng.update_package_site http://127.0.0.1/ ''' config_file = parse_config()['config_file'] __salt__['file.sed']( config_file, 'PACKAGESITE.*', 'PACKAGESITE\t : {0}'.format(new_url) ) # add change return later return True def stats(local=False, remote=False): ''' Return pkgng stats. CLI Example: .. code-block:: bash salt '*' pkgng.stats local Display stats only for the local package database. CLI Example: .. code-block:: bash salt '*' pkgng.stats local=True remote Display stats only for the remote package database(s). CLI Example: .. code-block:: bash salt '*' pkgng.stats remote=True ''' opts = '' if local: opts += 'l' if remote: opts += 'r' if opts: opts = '-' + opts cmd = 'pkg stats {0}'.format(opts) res = __salt__['cmd.run'](cmd) res = [x.strip("\t") for x in res.split("\n")] return res def backup(file_name): ''' Export installed packages into yaml+mtree file CLI Example: .. code-block:: bash salt '*' pkgng.backup /tmp/pkg ''' cmd = 'pkg backup -d {0}'.format(file_name) res = __salt__['cmd.run'](cmd) return res.split('...')[1] def restore(file_name): ''' Reads archive created by pkg backup -d and recreates the database. CLI Example: .. code-block:: bash salt '*' pkgng.restore /tmp/pkg ''' cmd = 'pkg backup -r {0}'.format(file_name) res = __salt__['cmd.run'](cmd) return res def add(pkg_path): ''' Install a package from either a local source or remote one CLI Example: .. code-block:: bash salt '*' pkgng.add /tmp/package.txz ''' if not os.path.isfile(pkg_path) or pkg_path.split(".")[1] != "txz": return '{0} could not be found or is not a *.txz \ format'.format(pkg_path) cmd = 'pkg add {0}'.format(pkg_path) res = __salt__['cmd.run'](cmd) return res def audit(): ''' Audits installed packages against known vulnerabilities CLI Example: .. code-block:: bash salt '*' pkgng.audit ''' cmd = 'pkg audit -F' return __salt__['cmd.run'](cmd) def install(pkg_name, orphan=False, force=False, glob=False, local=False, dryrun=False, quiet=False, require=False, reponame=None, regex=False, pcre=False): ''' Install package from repositories CLI Example: .. code-block:: bash salt '*' pkgng.install orphan Mark the installed package as orphan. Will be automatically removed if no other packages depend on them. For more information please refer to pkg-autoremove(8). CLI Example: .. code-block:: bash salt '*' pkgng.install orphan=True force Force the reinstallation of the package if already installed. CLI Example: .. code-block:: bash salt '*' pkgng.install force=True glob Treat the package names as shell glob patterns. CLI Example: .. code-block:: bash salt '*' pkgng.install glob=True local Skip updating the repository catalogues with pkg-update(8). Use the locally cached copies only. CLI Example: .. code-block:: bash salt '*' pkgng.install local=True dryrun Dru-run mode. The list of changes to packages is always printed, but no changes are actually made. CLI Example: .. code-block:: bash salt '*' pkgng.install dryrun=True quiet Force quiet output, except when dryrun is used, where pkg install will always show packages to be installed, upgraded or deleted. CLI Example: .. code-block:: bash salt '*' pkgng.install quiet=True require When used with force, reinstalls any packages that require the given package. CLI Example: .. code-block:: bash salt '*' pkgng.install require=True force=True reponame In multi-repo mode, override the pkg.conf ordering and only attempt to download packages from the named repository. CLI Example: .. code-block:: bash salt '*' pkgng.install reponame=repo regex Treat the package names as a regular expression CLI Example: .. code-block:: bash salt '*' pkgng.install regex=True pcre Treat the package names as extended regular expressions. CLI Example: .. code-block:: bash salt '*' pkgng.install pcre=True ''' opts = '' repo_opts = '' if orphan: opts += 'A' if force: opts += 'f' if glob: opts += 'g' if local: opts += 'l' if dryrun: opts += 'n' if not dryrun: opts += 'y' if quiet: opts += 'q' if require: opts += 'R' if reponame: repo_opts += 'r {0}'.format(reponame) if regex: opts += 'x' if pcre: opts += 'X' if opts: opts = '-' + opts if repo_opts: repo_opts = '-' + repo_opts cmd = 'pkg install {0} {1} {2}'.format(repo_opts, opts, pkg_name) return __salt__['cmd.run'](cmd) def delete(pkg_name, all_installed=False, force=False, glob=False, dryrun=False, recurse=False, regex=False, pcre=False): ''' Delete a package from the database and system CLI Example: .. code-block:: bash salt '*' pkgng.delete all_installed Deletes all installed packages from the system and empties the database. USE WITH CAUTION! CLI Example: .. code-block:: bash salt '*' pkgng.delete all all_installed=True force=True force Forces packages to be removed despite leaving unresolved dependencies. CLI Example: .. code-block:: bash salt '*' pkgng.delete force=True glob Treat the package names as shell glob patterns. CLI Example: .. code-block:: bash salt '*' pkgng.delete glob=True dryrun Dry run mode. The list of packages to delete is always printed, but no packages are actually deleted. CLI Example: .. code-block:: bash salt '*' pkgng.delete dryrun=True recurse Delete all packages that require the listed package as well. CLI Example: .. code-block:: bash salt '*' pkgng.delete recurse=True regex Treat the package names as regular expressions. CLI Example: .. code-block:: bash salt '*' pkgng.delete regex=True pcre Treat the package names as extended regular expressions. CLI Example: .. code-block:: bash salt '*' pkgng.delete pcre=True ''' opts = '' if all_installed: opts += 'a' if force: opts += 'f' if glob: opts += 'g' if dryrun: opts += 'n' if not dryrun: opts += 'y' if recurse: opts += 'R' if regex: opts += 'x' if pcre: opts += 'X' if opts: opts = '-' + opts cmd = 'pkg delete {0} {1}'.format(opts, pkg_name) return __salt__['cmd.run'](cmd) def info(pkg_name=None): ''' Returns info on packages installed on system CLI Example: .. code-block:: bash salt '*' pkgng.info salt '*' pkgng.info sudo ''' if pkg_name: cmd = 'pkg info {0}'.format(pkg_name) else: cmd = 'pkg info' res = __salt__['cmd.run'](cmd) if not pkg_name: res = res.splitlines() return res def update(force=False): ''' Refresh PACKAGESITE contents CLI Example: .. code-block:: bash salt '*' pkgng.update force Force a full download of the repository catalogue without regard to the respective ages of the local and remote copies of the catalogue. CLI Example: .. code-block:: bash salt '*' pkgng.update force=True ''' opts = '' if force: opts += 'f' if opts: opts = '-' + opts cmd = 'pkg update {0}'.format(opts) return __salt__['cmd.run'](cmd) def upgrade(force=False, local=False, dryrun=False): ''' Upgrade all packages CLI Example: .. code-block:: bash salt '*' pkgng.upgrade force Force reinstalling/upgrading the whole set of packages. CLI Example: .. code-block:: bash salt '*' pkgng.upgrade force=True local Do not update the repository catalogues with ``pkg-update(8)``. A value of ``True`` here is equivalent to using the ``-U`` flag with ``pkg upgrade``. CLI Example: .. code-block:: bash salt '*' pkgng.update local=True dryrun Dry-run mode: show what packages have updates available, but do not perform any upgrades. Repository catalogues will be updated as usual unless the local option is also given. CLI Example: .. code-block:: bash salt '*' pkgng.update dryrun=True ''' opts = '' if force: opts += 'f' if local: opts += 'L' if dryrun: opts += 'n' if not dryrun: opts += 'y' if opts: opts = '-' + opts cmd = 'pkg upgrade {0}'.format(opts) return __salt__['cmd.run'](cmd) def clean(): ''' Cleans the local cache of fetched remote packages CLI Example: .. code-block:: bash salt '*' pkgng.clean ''' cmd = 'pkg clean' return __salt__['cmd.run'](cmd) def autoremove(dryrun=False): ''' Delete packages which were automatically installed as dependencies and are not required anymore. dryrun Dry-run mode. The list of changes to packages is always printed, but no changes are actually made. CLI Example: .. code-block:: bash salt '*' pkgng.autoremove salt '*' pkgng.autoremove dryrun=True ''' opts = '' if dryrun: opts += 'n' else: opts += 'y' if opts: opts = '-' + opts cmd = 'pkg autoremove {0}'.format(opts) return __salt__['cmd.run'](cmd) def check(depends=False, recompute=False, checksum=False): ''' Sanity checks installed packages depends Check for and install missing dependencies. CLI Example: .. code-block:: bash salt '*' pkgng.check recompute=True recompute Recompute sizes and checksums of installed packages. CLI Example: .. code-block:: bash salt '*' pkgng.check depends=True checksum Find invalid checksums for installed packages. CLI Example: .. code-block:: bash salt '*' pkgng.check checksum=True ''' opts = '' if depends: opts += 'dy' if recompute: opts += 'r' if checksum: opts += 's' if opts: opts = '-' + opts cmd = 'pkg check {0}'.format(opts) return __salt__['cmd.run'](cmd) def which(file_name, origin=False, quiet=False): ''' Displays which package installed a specific file CLI Example: .. code-block:: bash salt '*' pkgng.which origin Shows the origin of the package instead of name-version. CLI Example: .. code-block:: bash salt '*' pkgng.which origin=True quiet Quiet output. CLI Example: .. code-block:: bash salt '*' pkgng.which quiet=True ''' opts = '' if quiet: opts += 'q' if origin: opts += 'o' if opts: opts = '-' + opts cmd = 'pkg which {0} {1}'.format(opts, file_name) return __salt__['cmd.run'](cmd) def search(pkg_name, exact=False, glob=False, regex=False, pcre=False, comment=False, desc=False, full=False, depends=False, size=False, quiet=False, origin=False, prefix=False): ''' Searches in remote package repositories CLI Example: .. code-block:: bash salt '*' pkgng.search pattern exact Treat pattern as exact pattern. CLI Example: .. code-block:: bash salt '*' pkgng.search pattern exact=True glob Treat pattern as a shell glob pattern. CLI Example: .. code-block:: bash salt '*' pkgng.search pattern glob=True regex Treat pattern as a regular expression. CLI Example: .. code-block:: bash salt '*' pkgng.search pattern regex=True pcre Treat pattern as an extended regular expression. CLI Example: .. code-block:: bash salt '*' pkgng.search pattern pcre=True comment Search for pattern in the package comment one-line description. CLI Example: .. code-block:: bash salt '*' pkgng.search pattern comment=True desc Search for pattern in the package description. CLI Example: .. code-block:: bash salt '*' pkgng.search pattern desc=True full Displays full information about the matching packages. CLI Example: .. code-block:: bash salt '*' pkgng.search pattern full=True depends Displays the dependencies of pattern. CLI Example: .. code-block:: bash salt '*' pkgng.search pattern depends=True size Displays the size of the package CLI Example: .. code-block:: bash salt '*' pkgng.search pattern size=True quiet Be quiet. Prints only the requested information without displaying many hints. CLI Example: .. code-block:: bash salt '*' pkgng.search pattern quiet=True origin Displays pattern origin. CLI Example: .. code-block:: bash salt '*' pkgng.search pattern origin=True prefix Displays the installation prefix for each package matching pattern. CLI Example: .. code-block:: bash salt '*' pkgng.search pattern prefix=True ''' opts = '' if exact: opts += 'e' if glob: opts += 'g' if regex: opts += 'x' if pcre: opts += 'X' if comment: opts += 'c' if desc: opts += 'D' if full: opts += 'f' if depends: opts += 'd' if size: opts += 's' if quiet: opts += 'q' if origin: opts += 'o' if prefix: opts += 'p' if opts: opts = '-' + opts cmd = 'pkg search {0} {1}'.format(opts, pkg_name) return __salt__['cmd.run'](cmd) def fetch(pkg_name, all=False, quiet=False, reponame=None, glob=True, regex=False, pcre=False, local=False, depends=False): ''' Fetches remote packages CLI Example: .. code-block:: bash salt '*' pkgng.fetch all Fetch all packages. CLI Example: .. code-block:: bash salt '*' pkgng.fetch all=True quiet Quiet mode. Show less output. CLI Example: .. code-block:: bash salt '*' pkgng.fetch quiet=True reponame Fetches packages from the given reponame if multiple repo support is enabled. See pkg.conf(5). CLI Example: .. code-block:: bash salt '*' pkgng.fetch reponame=repo glob Treat pkg_name as a shell glob pattern. CLI Example: .. code-block:: bash salt '*' pkgng.fetch glob=True regex Treat pkg_name as a regular expression. CLI Example: .. code-block:: bash salt '*' pkgng.fetch regex=True pcre Treat pkg_name is an extended regular expression. CLI Example: .. code-block:: bash salt '*' pkgng.fetch pcre=True local Skip updating the repository catalogues with pkg-update(8). Use the local cache only. CLI Example: .. code-block:: bash salt '*' pkgng.fetch local=True depends Fetch the package and its dependencies as well. CLI Example: .. code-block:: bash salt '*' pkgng.fetch depends=True ''' opts = '' repo_opts = '' if all: opts += 'a' if quiet: opts += 'q' if reponame: repo_opts += 'r {0}'.format(reponame) if glob: opts += 'g' if regex: opts += 'x' if pcre: opts += 'X' if local: opts += 'L' if depends: opts += 'd' if opts: opts = '-' + opts if repo_opts: opts = '-' + repo_opts cmd = 'pkg fetch -y {0} {1} {2}'.format(repo_opts, opts, pkg_name) return __salt__['cmd.run'](cmd) def updating(pkg_name, filedate=None, filename=None): '''' Displays UPDATING entries of software packages CLI Example: .. code-block:: bash salt '*' pkgng.updating foo filedate Only entries newer than date are shown. Use a YYYYMMDD date format. CLI Example: .. code-block:: bash salt '*' pkgng.updating foo filedate=20130101 filename Defines an alternative location of the UPDATING file. CLI Example: .. code-block:: bash salt '*' pkgng.updating foo filename=/tmp/UPDATING ''' opts = '' if filedate: opts += 'd {0}'.format(filedate) if filename: opts += 'f {0}'.format(filename) if opts: opts = '-' + opts cmd = 'pkg updating {0} {1}'.format(opts, pkg_name) return __salt__['cmd.run'](cmd) salt-0.17.5+ds.orig/salt/modules/alternatives.py0000644000175000017500000000702212263042153017711 0ustar joejoe# -*- coding: utf-8 -*- ''' Support for Alternatives system :codeauthor: Radek Rada :copyright: © 2012 by the SaltStack Team, see AUTHORS for more details. :license: Apache 2.0, see LICENSE for more details. ''' # Import python libs import os import logging __outputter__ = { 'display': 'txt', 'install': 'txt', 'remove': 'txt', } log = logging.getLogger(__name__) # Don't shadow built-in's. __func_alias__ = { 'set_': 'set' } def __virtual__(): ''' Only if alternatives dir is available ''' if os.path.isdir('/etc/alternatives'): return 'alternatives' return False def _get_cmd(): ''' Alteratives commands and differ across distributions ''' if __grains__['os_family'] == 'RedHat': return 'alternatives' return 'update-alternatives' def display(name): ''' Display alternatives settings for defined command name CLI Example: .. code-block:: bash salt '*' alternatives.display editor ''' cmd = [_get_cmd(), '--display', name] out = __salt__['cmd.run_all'](cmd, python_shell=False) if out['retcode'] > 0 and out['stderr'] != '': return out['stderr'] return out['stdout'] def show_current(name): ''' Display the current highest-priority alternative for a given alternatives link CLI Example: .. code-block:: bash salt '*' alternatives.show_current editor ''' alt_link_path = '/etc/alternatives/{0}'.format(name) try: return os.readlink(alt_link_path) except OSError: log.error( 'alternatives: path {0} does not exist'.format(alt_link_path) ) return False def check_installed(name, path): ''' Check if the current highest-priority match for a given alternatives link is set to the desired path CLI Example: .. code-block:: bash salt '*' alternatives.check_installed name path ''' return show_current(name) == path def install(name, link, path, priority): ''' Install symbolic links determining default commands CLI Example: .. code-block:: bash salt '*' alternatives.install editor /usr/bin/editor /usr/bin/emacs23 50 ''' cmd = [_get_cmd(), '--install', link, name, path, str(priority)] out = __salt__['cmd.run_all'](cmd, python_shell=False) if out['retcode'] > 0 and out['stderr'] != '': return out['stderr'] return out['stdout'] def remove(name, path): ''' Remove symbolic links determining the default commands. CLI Example: .. code-block:: bash salt '*' alternatives.remove name path ''' cmd = [_get_cmd(), '--remove', name, path] out = __salt__['cmd.run_all'](cmd, python_shell=False) if out['retcode'] > 0: return out['stderr'] return out['stdout'] def auto(name): ''' Trigger alternatives to set the path for as specified by priority. CLI Example: .. code-block:: bash salt '*' alternatives.auto name ''' cmd = [_get_cmd(), '--auto', name] out = __salt__['cmd.run_all'](cmd, python_shell=False) if out['retcode'] > 0: return out['stderr'] return out['stdout'] def set_(name, path): ''' Manually set the alternative for . CLI Example: .. code-block:: bash salt '*' alternatives.set name path ''' cmd = [_get_cmd(), '--set', name, path] out = __salt__['cmd.run_all'](cmd, python_shell=False) if out['retcode'] > 0: return out['stderr'] return out['stdout'] salt-0.17.5+ds.orig/salt/modules/pkgin.py0000644000175000017500000002770212270576114016336 0ustar joejoe# -*- coding: utf-8 -*- ''' Package support for pkgin based systems, inspired from freebsdpkg module ''' # Import python libs import os import re import logging # Import salt libs import salt.utils import salt.utils.decorators as decorators VERSION_MATCH = re.compile(r'pkgin(?:[\s]+)([\d.]+)(?:[\s]+)(?:.*)') log = logging.getLogger(__name__) @decorators.memoize def _check_pkgin(): ''' Looks to see if pkgin is present on the system, return full path ''' ppath = salt.utils.which('pkgin') if ppath is None: # pkgin was not found in $PATH, try to find it via LOCALBASE localbase = __salt__['cmd.run']('pkg_info -Q LOCALBASE pkgin') if localbase is not None: ppath = '{0}/bin/pkgin'.format(localbase) if not os.path.exists(ppath): return None return ppath @decorators.memoize def _supports_regex(): ''' Get the pkgin version ''' ppath = _check_pkgin() version_string = __salt__['cmd.run']('{0} -v'.format(ppath)) if version_string is None: # Dunno why it would, but... return False version_match = VERSION_MATCH.search(version_string) if not version_match: return False return tuple([int(i) for i in version_match.group(1).split('.')]) > (0, 5) def __virtual__(): ''' Set the virtual pkg module if the os is supported by pkgin ''' supported = ['NetBSD', 'SunOS', 'DragonFly', 'Minix', 'Darwin', 'SmartOS'] if __grains__['os'] in supported and _check_pkgin(): return 'pkg' return False def _splitpkg(name): # name is in the format foobar-1.0nb1, already space-splitted if name[0].isalnum() and name != 'No': # avoid < > = and 'No result' return name.rsplit('-', 1) def search(pkg_name): ''' Searches for an exact match using pkgin ^package$ CLI Example: .. code-block:: bash salt '*' pkg.search 'mysql-server' ''' pkglist = {} pkgin = _check_pkgin() if not pkgin: return pkglist if _supports_regex(): pkg_name = '^{0}$'.format(pkg_name) for p in __salt__['cmd.run']('{0} se {1}'.format(pkgin, pkg_name) ).splitlines(): if p: s = _splitpkg(p.split()[0]) if s: pkglist[s[0]] = s[1] return pkglist def latest_version(*names, **kwargs): ''' Return the latest version of the named package available for upgrade or installation. If the latest version of a given package is already installed, an empty string will be returned for that package. CLI Example: .. code-block:: bash salt '*' pkg.latest_version salt '*' pkg.latest_version ... ''' refresh = salt.utils.is_true(kwargs.pop('refresh', True)) pkglist = {} pkgin = _check_pkgin() if not pkgin: return pkglist # Refresh before looking for the latest version available if refresh: refresh_db() for name in names: if _supports_regex(): name = '^{0}$'.format(name) for line in __salt__['cmd.run']('{0} se {1}'.format(pkgin, name) ).splitlines(): p = line.split() # pkgname-version status if p and p[0] in ('=:', '<:', '>:'): # These are explanation comments continue elif p: s = _splitpkg(p[0]) if s: if len(p) > 1 and p[1] == '<': pkglist[s[0]] = s[1] else: pkglist[s[0]] = '' if len(names) == 1 and pkglist: return pkglist[names[0]] return pkglist # available_version is being deprecated available_version = latest_version def version(*names, **kwargs): ''' Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. CLI Example: .. code-block:: bash salt '*' pkg.version salt '*' pkg.version ... ''' return __salt__['pkg_resource.version'](*names, **kwargs) def refresh_db(): ''' Use pkg update to get latest pkg_summary CLI Example: .. code-block:: bash salt '*' pkg.refresh_db ''' pkgin = _check_pkgin() if pkgin: __salt__['cmd.run']('{0} up'.format(pkgin)) return {} def list_pkgs(versions_as_list=False, **kwargs): ''' List the packages currently installed as a dict:: {'': ''} CLI Example: .. code-block:: bash salt '*' pkg.list_pkgs ''' versions_as_list = salt.utils.is_true(versions_as_list) # 'removed' not yet implemented or not applicable if salt.utils.is_true(kwargs.get('removed')): return {} pkgin = _check_pkgin() if pkgin: pkg_command = '{0} ls'.format(pkgin) else: pkg_command = 'pkg_info' ret = {} for line in __salt__['cmd.run'](pkg_command).splitlines(): if not line: continue pkg, ver = line.split(' ')[0].rsplit('-', 1) __salt__['pkg_resource.add_pkg'](ret, pkg, ver) __salt__['pkg_resource.sort_pkglist'](ret) if not versions_as_list: __salt__['pkg_resource.stringify'](ret) return ret def install(name=None, refresh=False, fromrepo=None, pkgs=None, sources=None, **kwargs): ''' Install the passed package name The name of the package to be installed. refresh Whether or not to refresh the package database before installing. fromrepo Specify a package repository to install from. Multiple Package Installation Options: pkgs A list of packages to install from a software repository. Must be passed as a python list. CLI Example: .. code-block:: bash salt '*' pkg.install pkgs='["foo","bar"]' sources A list of 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.deb"},{"bar": "salt://bar.deb"}]' Return a dict containing the new package names and versions:: {'': {'old': '', 'new': ''}} CLI Example: .. code-block:: bash salt '*' pkg.install ''' pkg_params, pkg_type = __salt__['pkg_resource.parse_targets'](name, pkgs, sources, **kwargs) # Support old "repo" argument repo = kwargs.get('repo', '') if not fromrepo and repo: fromrepo = repo if not pkg_params: return {} env = [] args = [] pkgin = _check_pkgin() if pkgin: cmd = pkgin if fromrepo: log.info('Setting PKG_REPOS={0}'.format(fromrepo)) env.append(('PKG_REPOS', fromrepo)) else: cmd = 'pkg_add' if fromrepo: log.info('Setting PKG_PATH={0}'.format(fromrepo)) env.append(('PKG_PATH', fromrepo)) if pkg_type == 'file': cmd = 'pkg_add' elif pkg_type == 'repository': if pkgin: if refresh: args.append('-f') # update repo db args.extend(('-y', 'in')) # Assume yes when asked args.extend(pkg_params) old = list_pkgs() __salt__['cmd.run_all']('{0} {1}'.format(cmd, ' '.join(args)), env=env) new = list_pkgs() rehash() return __salt__['pkg_resource.find_changes'](old, new) def upgrade(): ''' Run pkg upgrade, if pkgin used. Otherwise do nothing Return a dict containing the new package names and versions:: {'': {'old': '', 'new': ''}} CLI Example: .. code-block:: bash salt '*' pkg.upgrade ''' pkgin = _check_pkgin() if not pkgin: # There is not easy way to upgrade packages with old package system return {} old = list_pkgs() __salt__['cmd.retcode']('{0} -y fug'.format(pkgin)) new = list_pkgs() return __salt__['pkg_resource.find_changes'](old, new) def remove(name=None, pkgs=None, **kwargs): ''' name The name of the package to be deleted. Multiple Package Options: pkgs A list of packages to delete. Must be passed as a python list. The ``name`` parameter will be ignored if this option is passed. .. versionadded:: 0.16.0 Returns a list containing the removed packages. CLI Example: .. code-block:: bash salt '*' pkg.remove salt '*' pkg.remove ,, salt '*' pkg.remove pkgs='["foo", "bar"]' ''' pkg_params, pkg_type = __salt__['pkg_resource.parse_targets'](name, pkgs) if not pkg_params: return {} old = list_pkgs() args = [] for param in pkg_params: ver = old.get(param, []) if not ver: continue if isinstance(ver, list): args.extend(['{0}-{1}'.format(param, v) for v in ver]) else: args.append('{0}-{1}'.format(param, ver)) if not args: return {} for_remove = ' '.join(args) pkgin = _check_pkgin() if pkgin: cmd = '{0} -y remove {1}'.format(pkgin, for_remove) else: cmd = 'pkg_remove {0}'.format(for_remove) __salt__['cmd.run_all'](cmd) new = list_pkgs() return __salt__['pkg_resource.find_changes'](old, new) def purge(name=None, pkgs=None, **kwargs): ''' Package purges are not supported, this function is identical to ``remove()``. name The name of the package to be deleted. Multiple Package Options: pkgs A list of packages to delete. Must be passed as a python list. The ``name`` parameter will be ignored if this option is passed. .. versionadded:: 0.16.0 Returns a dict containing the changes. CLI Example: .. code-block:: bash salt '*' pkg.purge salt '*' pkg.purge ,, salt '*' pkg.purge pkgs='["foo", "bar"]' ''' return remove(name=name, pkgs=pkgs) def rehash(): ''' Recomputes internal hash table for the PATH variable. Use whenever a new command is created during the current session. CLI Example: .. code-block:: bash salt '*' pkg.rehash ''' shell = __salt__['cmd.run']('echo $SHELL').split('/') if shell[len(shell) - 1] in ['csh', 'tcsh']: __salt__['cmd.run']('rehash') def file_list(package): ''' List the files that belong to a package. CLI Examples: .. code-block:: bash salt '*' pkg.file_list nginx ''' ret = file_dict(package) files = [] for pkg_files in ret['files'].values(): files.extend(pkg_files) ret['files'] = files return ret def file_dict(package): ''' List the files that belong to a package. CLI Examples: .. code-block:: bash salt '*' pkg.file_list nginx ''' errors = [] files = {} files[package] = None cmd = 'pkg_info -qL {0}'.format(package) ret = __salt__['cmd.run_all'](cmd) for line in ret['stderr'].splitlines(): errors.append(line) for line in ret['stdout'].splitlines(): if line.startswith('/'): if files[package] is None: files[package] = [line] else: files[package].append(line) else: continue # unexpected string print files return {'errors': errors, 'files': files} # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 salt-0.17.5+ds.orig/salt/modules/iptables.py0000644000175000017500000006355412270576114017036 0ustar joejoe# -*- coding: utf-8 -*- ''' Support for iptables ''' # Import python libs import os import sys import shlex # Import salt libs import salt.utils from salt.state import STATE_INTERNAL_KEYWORDS as _STATE_INTERNAL_KEYWORDS from salt.exceptions import SaltException def __virtual__(): ''' Only load the module if iptables is installed ''' if salt.utils.which('iptables'): return 'iptables' return False def _conf(): ''' Some distros have a specific location for config files ''' if __grains__['os_family'] == 'RedHat': return '/etc/sysconfig/iptables' elif __grains__['os_family'] == 'Arch': return '/etc/iptables/iptables.rules' elif __grains__['os_family'] == 'Debian': return '/etc/iptables/rules.v4' elif __grains__['os'] == 'Gentoo': return '/var/lib/iptables/rules-save' else: return False def version(): ''' Return version from iptables --version CLI Example: .. code-block:: bash salt '*' iptables.version ''' cmd = 'iptables --version' out = __salt__['cmd.run'](cmd).split() return out[1] def build_rule(table=None, chain=None, command=None, position='', full=None, **kwargs): ''' Build a well-formatted iptables rule based on kwargs. Long options must be used (`--jump` instead of `-j`) because they will have the `--` added to them. A `table` and `chain` are not required, unless `full` is True. If `full` is `True`, then `table`, `chain` and `command` are required. `command` may be specified as either a short option ('I') or a long option (`--insert`). This will return the iptables command, exactly as it would be used from the command line. If a position is required (as with `-I` or `-D`), it may be specified as `position`. This will only be useful if `full` is True. If `connstate` is passed in, it will automatically be changed to `state`. CLI Examples: .. code-block:: bash salt '*' iptables.build_rule match=state connstate=RELATED,ESTABLISHED \\ jump=ACCEPT salt '*' iptables.build_rule filter INPUT command=I position=3 \\ full=True match=state state=RELATED,ESTABLISHED jump=ACCEPT ''' if 'target' in kwargs: kwargs['jump'] = kwargs['target'] del kwargs['target'] for ignore in list(_STATE_INTERNAL_KEYWORDS) + ['chain', 'save', 'table']: if ignore in kwargs: del kwargs[ignore] rule = '' if 'match' in kwargs: rule = '-m {0} '.format(kwargs['match']) del kwargs['match'] if 'state' in kwargs: del kwargs['state'] if 'connstate' in kwargs: kwargs['state'] = kwargs['connstate'] del kwargs['connstate'] for item in kwargs: rule += '--{0} {1} '.format(item, kwargs[item]) if full is True: if not table: return 'Error: Table needs to be specified' if not chain: return 'Error: Chain needs to be specified' if not command: return 'Error: Command needs to be specified' if command in 'ACDIRLSFZNXPE': flag = '-' else: flag = '--' return 'iptables {0}{1} {2} {3} {4}'.format(flag, command, chain, position, rule) return rule def get_saved_rules(conf_file=None): ''' Return a data structure of the rules in the conf file CLI Example: .. code-block:: bash salt '*' iptables.get_saved_rules ''' return _parse_conf(conf_file) def get_rules(): ''' Return a data structure of the current, in-memory rules CLI Example: .. code-block:: bash salt '*' iptables.get_rules ''' return _parse_conf(in_mem=True) def get_saved_policy(table='filter', chain=None, conf_file=None): ''' Return the current policy for the specified table/chain CLI Examples: .. code-block:: bash salt '*' iptables.get_saved_policy filter INPUT salt '*' iptables.get_saved_policy filter INPUT conf_file=/etc/iptables.saved ''' if not chain: return 'Error: Chain needs to be specified' rules = _parse_conf(conf_file) return rules[table][chain]['policy'] def get_policy(table='filter', chain=None): ''' Return the current policy for the specified table/chain CLI Example: .. code-block:: bash salt '*' iptables.get_policy filter INPUT ''' if not chain: return 'Error: Chain needs to be specified' rules = _parse_conf(in_mem=True) return rules[table][chain]['policy'] def set_policy(table='filter', chain=None, policy=None): ''' Set the current policy for the specified table/chain CLI Example: .. code-block:: bash salt '*' iptables.set_policy filter INPUT ACCEPT ''' if not chain: return 'Error: Chain needs to be specified' if not policy: return 'Error: Policy needs to be specified' cmd = 'iptables -t {0} -P {1} {2}'.format(table, chain, policy) out = __salt__['cmd.run'](cmd) return out def save(filename=None): ''' Save the current in-memory rules to disk CLI Example: .. code-block:: bash salt '*' iptables.save /etc/sysconfig/iptables ''' if _conf() and not filename: filename = _conf() parent_dir = os.path.dirname(filename) if not os.path.isdir(parent_dir): os.makedirs(parent_dir) cmd = 'iptables-save > {0}'.format(filename) out = __salt__['cmd.run'](cmd) return out def check(table='filter', chain=None, rule=None): ''' Check for the existance of a rule in the table and chain This function accepts a rule in a standard iptables command format, starting with the chain. Trying to force users to adapt to a new method of creating rules would be irritating at best, and we already have a parser that can handle it. CLI Example: .. code-block:: bash salt '*' iptables.check filter INPUT rule='-m state --state RELATED,ESTABLISHED -j ACCEPT' ''' if not chain: return 'Error: Chain needs to be specified' if not rule: return 'Error: Rule needs to be specified' cmd = 'iptables -t {0} -C {1} {2}'.format(table, chain, rule) out = __salt__['cmd.run'](cmd) if not out: return True return out def append(table='filter', chain=None, rule=None): ''' Append a rule to the specified table/chain. This function accepts a rule in a standard iptables command format, starting with the chain. Trying to force users to adapt to a new method of creating rules would be irritating at best, and we already have a parser that can handle it. CLI Example: .. code-block:: bash salt '*' iptables.append filter INPUT rule='-m state --state RELATED,ESTABLISHED -j ACCEPT' ''' if not chain: return 'Error: Chain needs to be specified' if not rule: return 'Error: Rule needs to be specified' cmd = 'iptables -t {0} -A {1} {2}'.format(table, chain, rule) out = __salt__['cmd.run'](cmd) return out def insert(table='filter', chain=None, position=None, rule=None): ''' Insert a rule into the specified table/chain, at the specified position. This function accepts a rule in a standard iptables command format, starting with the chain. Trying to force users to adapt to a new method of creating rules would be irritating at best, and we already have a parser that can handle it. CLI Examples: .. code-block:: bash salt '*' iptables.insert filter INPUT position=3 rule='-m state --state RELATED,ESTABLISHED -j ACCEPT' ''' if not chain: return 'Error: Chain needs to be specified' if not position: return 'Error: Position needs to be specified or use append (-A)' if not rule: return 'Error: Rule needs to be specified' cmd = 'iptables -t {0} -I {1} {2} {3}'.format(table, chain, position, rule) out = __salt__['cmd.run'](cmd) return out def delete(table, chain=None, position=None, rule=None): ''' Delete a rule from the specified table/chain, specifying either the rule in its entirety, or the rule's position in the chain. This function accepts a rule in a standard iptables command format, starting with the chain. Trying to force users to adapt to a new method of creating rules would be irritating at best, and we already have a parser that can handle it. CLI Examples: .. code-block:: bash salt '*' iptables.delete filter INPUT position=3 salt '*' iptables.delete filter INPUT rule='-m state --state RELATED,ESTABLISHED -j ACCEPT' ''' if position and rule: return 'Error: Only specify a position or a rule, not both' if position: rule = position cmd = 'iptables -t {0} -D {1} {2}'.format(table, chain, rule) out = __salt__['cmd.run'](cmd) return out def flush(table='filter'): ''' Flush all chains in the specified table. CLI Example: .. code-block:: bash salt '*' iptables.flush filter ''' cmd = 'iptables -t {0} -F'.format(table) out = __salt__['cmd.run'](cmd) return out def _parse_conf(conf_file=None, in_mem=False): ''' If a file is not passed in, and the correct one for this OS is not detected, return False ''' if _conf() and not conf_file and not in_mem: conf_file = _conf() rules = '' if conf_file: with salt.utils.fopen(conf_file, 'r') as ifile: rules = ifile.read() elif in_mem: cmd = 'iptables-save' rules = __salt__['cmd.run'](cmd) else: raise SaltException('A file was not found to parse') ret = {} table = '' for line in rules.splitlines(): if line.startswith('*'): table = line.replace('*', '') ret[table] = {} elif line.startswith(':'): comps = line.split() chain = comps[0].replace(':', '') ret[table][chain] = {} ret[table][chain]['policy'] = comps[1] counters = comps[2].replace('[', '').replace(']', '') (pcount, bcount) = counters.split(':') ret[table][chain]['packet count'] = pcount ret[table][chain]['byte count'] = bcount ret[table][chain]['rules'] = [] ret[table][chain]['rules_comment'] = {} elif line.startswith('-A'): parser = _parser() parsed_args = [] if sys.version.startswith('2.6'): (opts, args) = parser.parse_args(shlex.split(line)) parsed_args = vars(opts) else: parsed_args = vars(parser.parse_args(shlex.split(line))) ret_args = {} chain = parsed_args['append'] for arg in parsed_args: if parsed_args[arg] and arg is not 'append': ret_args[arg] = parsed_args[arg] if parsed_args['comment'] is not None: comment = parsed_args['comment'][0].strip('"') ret[table][chain[0]]['rules_comment'][comment] = ret_args ret[table][chain[0]]['rules'].append(ret_args) return ret def _parser(): ''' This function contains _all_ the options I could find in man 8 iptables, listed in the first section that I found them in. They will not all be used by all parts of the module; use them intelligently and appropriately. ''' add_arg = None if sys.version.startswith('2.6'): import optparse parser = optparse.OptionParser() add_arg = parser.add_option else: import argparse parser = argparse.ArgumentParser() add_arg = parser.add_argument # COMMANDS add_arg('-A', '--append', dest='append', action='append') add_arg('-D', '--delete', dest='delete', action='append') add_arg('-I', '--insert', dest='insert', action='append') add_arg('-R', '--replace', dest='replace', action='append') add_arg('-L', '--list', dest='list', action='append') add_arg('-F', '--flush', dest='flush', action='append') add_arg('-Z', '--zero', dest='zero', action='append') add_arg('-N', '--new-chain', dest='new-chain', action='append') add_arg('-X', '--delete-chain', dest='delete-chain', action='append') add_arg('-P', '--policy', dest='policy', action='append') add_arg('-E', '--rename-chain', dest='rename-chain', action='append') # PARAMETERS add_arg('-p', '--protocol', dest='protocol', action='append') add_arg('-s', '--source', dest='source', action='append') add_arg('-d', '--destination', dest='destination', action='append') add_arg('-j', '--jump', dest='jump', action='append') add_arg('-g', '--goto', dest='goto', action='append') add_arg('-i', '--in-interface', dest='in-interface', action='append') add_arg('-o', '--out-interface', dest='out-interface', action='append') add_arg('-f', '--fragment', dest='fragment', action='append') add_arg('-c', '--set-counters', dest='set-counters', action='append') # MATCH EXTENSIONS add_arg('-m', '--match', dest='match', action='append') ## addrtype add_arg('--src-type', dest='src-type', action='append') add_arg('--dst-type', dest='dst-type', action='append') add_arg('--limit-iface-in', dest='limit-iface-in', action='append') add_arg('--limit-iface-out', dest='limit-iface-out', action='append') ## ah add_arg('--ahspi', dest='ahspi', action='append') ## cluster add_arg('--cluster-total-nodes', dest='cluster-total-nodes', action='append') add_arg('--cluster-local-node', dest='cluster-local-node', action='append') add_arg('--cluster-local-nodemask', dest='cluster-local-nodemask', action='append') add_arg('--cluster-hash-seed', dest='cluster-hash-seed', action='append') add_arg('--h-length', dest='h-length', action='append') add_arg('--mangle-mac-s', dest='mangle-mac-s', action='append') add_arg('--mangle-mac-d', dest='mangle-mac-d', action='append') ## comment add_arg('--comment', dest='comment', action='append') ## connbytes add_arg('--connbytes', dest='connbytes', action='append') add_arg('--connbytes-dir', dest='connbytes-dir', action='append') add_arg('--connbytes-mode', dest='connbytes-mode', action='append') ## connlimit add_arg('--connlimit-above', dest='connlimit-above', action='append') add_arg('--connlimit-mask', dest='connlimit-mask', action='append') ## connmark add_arg('--mark', dest='mark', action='append') ## conntrack add_arg('--ctstate', dest='ctstate', action='append') add_arg('--ctproto', dest='ctproto', action='append') add_arg('--ctorigsrc', dest='ctorigsrc', action='append') add_arg('--ctorigdst', dest='ctorigdst', action='append') add_arg('--ctreplsrc', dest='ctreplsrc', action='append') add_arg('--ctrepldst', dest='ctrepldst', action='append') add_arg('--ctorigsrcport', dest='ctorigsrcport', action='append') add_arg('--ctorigdstport', dest='ctorigdstport', action='append') add_arg('--ctreplsrcport', dest='ctreplsrcport', action='append') add_arg('--ctrepldstport', dest='ctrepldstport', action='append') add_arg('--ctstatus', dest='ctstatus', action='append') add_arg('--ctexpire', dest='ctexpire', action='append') ## dccp add_arg('--sport', '--source-port', dest='source_port', action='append') add_arg('--dport', '--destination-port', dest='destination_port', action='append') add_arg('--dccp-types', dest='dccp-types', action='append') add_arg('--dccp-option', dest='dccp-option', action='append') ## dscp add_arg('--dscp', dest='dscp', action='append') add_arg('--dscp-class', dest='dscp-class', action='append') ## ecn add_arg('--ecn-tcp-cwr', dest='ecn-tcp-cwr', action='append') add_arg('--ecn-tcp-ece', dest='ecn-tcp-ece', action='append') add_arg('--ecn-ip-ect', dest='ecn-ip-ect', action='append') ## esp add_arg('--espspi', dest='espspi', action='append') ## hashlimit add_arg('--hashlimit-upto', dest='hashlimit-upto', action='append') add_arg('--hashlimit-above', dest='hashlimit-above', action='append') add_arg('--hashlimit-burst', dest='hashlimit-burst', action='append') add_arg('--hashlimit-mode', dest='hashlimit-mode', action='append') add_arg('--hashlimit-srcmask', dest='hashlimit-srcmask', action='append') add_arg('--hashlimit-dstmask', dest='hashlimit-dstmask', action='append') add_arg('--hashlimit-name', dest='hashlimit-name', action='append') add_arg('--hashlimit-htable-size', dest='hashlimit-htable-size', action='append') add_arg('--hashlimit-htable-max', dest='hashlimit-htable-max', action='append') add_arg('--hashlimit-htable-expire', dest='hashlimit-htable-expire', action='append') add_arg('--hashlimit-htable-gcinterval', dest='hashlimit-htable-gcinterval', action='append') ## helper add_arg('--helper', dest='helper', action='append') ## icmp add_arg('--icmp-type', dest='icmp-type', action='append') ## iprange add_arg('--src-range', dest='src-range', action='append') add_arg('--dst-range', dest='dst-range', action='append') ## length add_arg('--length', dest='length', action='append') ## limit add_arg('--limit', dest='limit', action='append') add_arg('--limit-burst', dest='limit-burst', action='append') ## mac add_arg('--mac-source', dest='mac-source', action='append') ## multiport add_arg('--sports', '--source-ports', dest='source-ports', action='append') add_arg('--dports', '--destination-ports', dest='destination-ports', action='append') add_arg('--ports', dest='ports', action='append') ## owner add_arg('--uid-owner', dest='uid-owner', action='append') add_arg('--gid-owner', dest='gid-owner', action='append') add_arg('--socket-exists', dest='socket-exists', action='append') ## physdev add_arg('--physdev-in', dest='physdev-in', action='append') add_arg('--physdev-out', dest='physdev-out', action='append') add_arg('--physdev-is-in', dest='physdev-is-in', action='append') add_arg('--physdev-is-out', dest='physdev-is-out', action='append') add_arg('--physdev-is-bridged', dest='physdev-is-bridged', action='append') ## pkttype add_arg('--pkt-type', dest='pkt-type', action='append') ## policy add_arg('--dir', dest='dir', action='append') add_arg('--pol', dest='pol', action='append') add_arg('--strict', dest='strict', action='append') add_arg('--reqid', dest='reqid', action='append') add_arg('--spi', dest='spi', action='append') add_arg('--proto', dest='proto', action='append') add_arg('--mode', dest='mode', action='append') add_arg('--tunnel-src', dest='tunnel-src', action='append') add_arg('--tunnel-dst', dest='tunnel-dst', action='append') add_arg('--next', dest='next', action='append') ## quota add_arg('--quota', dest='quota', action='append') ## rateest add_arg('--rateest1', dest='rateest1', action='append') add_arg('--rateest2', dest='rateest2', action='append') add_arg('--rateest-delta', dest='rateest-delta', action='append') add_arg('--rateest1-bps', dest='rateest1-bps', action='append') add_arg('--rateest2-bps', dest='rateest2-bps', action='append') add_arg('--rateest1-pps', dest='rateest1-pps', action='append') add_arg('--rateest2-pps', dest='rateest2-pps', action='append') add_arg('--rateest1-lt', dest='rateest1-lt', action='append') add_arg('--rateest1-gt', dest='rateest1-gt', action='append') add_arg('--rateest1-eq', dest='rateest1-eq', action='append') add_arg('--rateest-name', dest='rateest-name', action='append') add_arg('--rateest-interval', dest='rateest-interval', action='append') add_arg('--rateest-ewma', dest='rateest-ewma', action='append') ## realm add_arg('--realm', dest='realm', action='append') ## recent add_arg('--set', dest='set', action='append') add_arg('--name', dest='name', action='append') add_arg('--rsource', dest='rsource', action='append') add_arg('--rdest', dest='rdest', action='append') add_arg('--rcheck', dest='rcheck', action='append') add_arg('--update', dest='update', action='append') add_arg('--remove', dest='remove', action='append') add_arg('--seconds', dest='seconds', action='append') add_arg('--hitcount', dest='hitcount', action='append') add_arg('--rttl', dest='rttl', action='append') ## sctp add_arg('--chunk-types', dest='chunk-types', action='append') ## set add_arg('--match-set', dest='match-set', action='append') ## socket add_arg('--transparent', dest='transparent', action='append') ## state add_arg('--state', dest='state', action='append') ## statistic add_arg('--probability', dest='probability', action='append') add_arg('--every', dest='every', action='append') add_arg('--packet', dest='packet', action='append') ## string add_arg('--algo', dest='algo', action='append') add_arg('--from', dest='from', action='append') add_arg('--to', dest='to', action='append') add_arg('--string', dest='string', action='append') add_arg('--hex-string', dest='hex-string', action='append') ## tcp add_arg('--tcp-flags', dest='tcp-flags', action='append') add_arg('--syn', dest='syn', action='append') add_arg('--tcp-option', dest='tcp-option', action='append') ## tcpmss add_arg('--mss', dest='mss', action='append') ## time add_arg('--datestart', dest='datestart', action='append') add_arg('--datestop', dest='datestop', action='append') add_arg('--monthdays', dest='monthdays', action='append') add_arg('--weekdays', dest='weekdays', action='append') add_arg('--utc', dest='utc', action='append') add_arg('--localtz', dest='localtz', action='append') ## tos add_arg('--tos', dest='tos', action='append') ## ttl add_arg('--ttl-eq', dest='ttl-eq', action='append') add_arg('--ttl-gt', dest='ttl-gt', action='append') add_arg('--ttl-lt', dest='ttl-lt', action='append') ## u32 add_arg('--u32', dest='u32', action='append') # CHECKSUM add_arg('--checksum-fill', dest='checksum-fill', action='append') # CLASSIFY add_arg('--set-class', dest='set-class', action='append') # CLUSTERIP add_arg('--new', dest='new', action='append') add_arg('--hashmode', dest='hashmode', action='append') add_arg('--clustermac', dest='clustermac', action='append') add_arg('--total-nodes', dest='total-nodes', action='append') add_arg('--local-node', dest='local-node', action='append') add_arg('--hash-init', dest='hash-init', action='append') # CONNMARK add_arg('--set-xmark', dest='set-xmark', action='append') add_arg('--save-mark', dest='save-mark', action='append') add_arg('--restore-mark', dest='restore-mark', action='append') add_arg('--and-mark', dest='and-mark', action='append') add_arg('--or-mark', dest='or-mark', action='append') add_arg('--xor-mark', dest='xor-mark', action='append') add_arg('--set-mark', dest='set-mark', action='append') # DNAT add_arg('--to-destination', dest='to-destination', action='append') add_arg('--random', dest='random', action='append') add_arg('--persistent', dest='persistent', action='append') # DSCP add_arg('--set-dscp', dest='set-dscp', action='append') add_arg('--set-dscp-class', dest='set-dscp-class', action='append') # ECN add_arg('--ecn-tcp-remove', dest='ecn-tcp-remove', action='append') # LOG add_arg('--log-level', dest='log-level', action='append') add_arg('--log-prefix', dest='log-prefix', action='append') add_arg('--log-tcp-sequence', dest='log-tcp-sequence', action='append') add_arg('--log-tcp-options', dest='log-tcp-options', action='append') add_arg('--log-ip-options', dest='log-ip-options', action='append') add_arg('--log-uid', dest='log-uid', action='append') # NFLOG add_arg('--nflog-group', dest='nflog-group', action='append') add_arg('--nflog-prefix', dest='nflog-prefix', action='append') add_arg('--nflog-range', dest='nflog-range', action='append') add_arg('--nflog-threshold', dest='nflog-threshold', action='append') # NFQUEUE add_arg('--queue-num', dest='queue-num', action='append') add_arg('--queue-balance', dest='queue-balance', action='append') # RATEEST add_arg('--rateest-ewmalog', dest='rateest-ewmalog', action='append') # REDIRECT add_arg('--to-ports', dest='to-ports', action='append') # REJECT add_arg('--reject-with', dest='reject-with', action='append') # SAME add_arg('--nodst', dest='nodst', action='append') # SECMARK add_arg('--selctx', dest='selctx', action='append') # SET add_arg('--add-set', dest='add-set', action='append') add_arg('--del-set', dest='del-set', action='append') # SNAT add_arg('--to-source', dest='to-source', action='append') # TCPMSS add_arg('--set-mss', dest='set-mss', action='append') add_arg('--clamp-mss-to-pmtu', dest='clamp-mss-to-pmtu', action='append') # TCPOPTSTRIP add_arg('--strip-options', dest='strip-options', action='append') # TOS add_arg('--set-tos', dest='set-tos', action='append') add_arg('--and-tos', dest='and-tos', action='append') add_arg('--or-tos', dest='or-tos', action='append') add_arg('--xor-tos', dest='xor-tos', action='append') # TPROXY add_arg('--on-port', dest='on-port', action='append') add_arg('--on-ip', dest='on-ip', action='append') add_arg('--tproxy-mark', dest='tproxy-mark', action='append') # TTL add_arg('--ttl-set', dest='ttl-set', action='append') add_arg('--ttl-dec', dest='ttl-dec', action='append') add_arg('--ttl-inc', dest='ttl-inc', action='append') # ULOG add_arg('--ulog-nlgroup', dest='ulog-nlgroup', action='append') add_arg('--ulog-prefix', dest='ulog-prefix', action='append') add_arg('--ulog-cprange', dest='ulog-cprange', action='append') add_arg('--ulog-qthreshold', dest='ulog-qthreshold', action='append') return parser salt-0.17.5+ds.orig/salt/modules/locate.py0000644000175000017500000000472712263042153016470 0ustar joejoe# -*- coding: utf-8 -*- ''' Module for using the locate utilities ''' # Import python libs import logging # Import salt libs import salt.utils log = logging.getLogger(__name__) def __virtual__(): ''' Only work on POSIX-like systems ''' if salt.utils.is_windows(): return False return 'locate' def version(): ''' Returns the version of locate CLI Example: .. code-block:: bash salt '*' locate.version ''' cmd = 'locate -V' out = __salt__['cmd.run'](cmd).splitlines() return out def stats(): ''' Returns statistics about the locate database CLI Example: .. code-block:: bash salt '*' locate.stats ''' ret = {} cmd = 'locate -S' out = __salt__['cmd.run'](cmd).splitlines() for line in out: comps = line.strip().split() if line.startswith('Database'): ret['database'] = comps[1].replace(':', '') continue ret[' '.join(comps[1:])] = comps[0] return ret def updatedb(): ''' Updates the locate database CLI Example: .. code-block:: bash salt '*' locate.updatedb ''' cmd = 'updatedb' out = __salt__['cmd.run'](cmd).splitlines() return out def locate(pattern, database='', limit=0, **kwargs): ''' Performs a file lookup. Valid options (and their defaults) are:: basename=False count=False existing=False follow=True ignore=False nofollow=False wholename=True regex=False database= limit= See the manpage for ``locate(1)`` for further explanation of these options. CLI Example: .. code-block:: bash salt '*' locate.locate ''' options = '' toggles = { 'basename': 'b', 'count': 'c', 'existing': 'e', 'follow': 'L', 'ignore': 'i', 'nofollow': 'P', 'wholename': 'w', } for option in kwargs: if bool(kwargs[option]) is True: options += toggles[option] if options: options = '-{0}'.format(options) if database: options += ' -d {0}'.format(database) if limit > 0: options += ' -l {0}'.format(limit) if 'regex' in kwargs and bool(kwargs['regex']) is True: options += ' --regex' cmd = 'locate {0} {1}'.format(options, pattern) out = __salt__['cmd.run'](cmd).splitlines() return out salt-0.17.5+ds.orig/salt/modules/cp.py0000644000175000017500000002623012270576114015623 0ustar joejoe# -*- coding: utf-8 -*- ''' Minion side functions for salt-cp ''' # Import python libs import os import logging # Import salt libs import salt.minion import salt.fileclient import salt.utils import salt.crypt from salt.exceptions import CommandExecutionError log = logging.getLogger(__name__) def _auth(): ''' Return the auth object ''' if not 'auth' in __context__: __context__['auth'] = salt.crypt.SAuth(__opts__) return __context__['auth'] def recv(files, dest): ''' Used with salt-cp, pass the files dict, and the destination. This function receives small fast copy files from the master via salt-cp. It does not work via the CLI. ''' ret = {} for path, data in files.items(): if os.path.basename(path) == os.path.basename(dest) \ and not os.path.isdir(dest): final = dest elif os.path.isdir(dest): final = os.path.join(dest, os.path.basename(path)) elif os.path.isdir(os.path.dirname(dest)): final = dest else: return 'Destination unavailable' try: salt.utils.fopen(final, 'w+').write(data) ret[final] = True except IOError: ret[final] = False return ret def _mk_client(): ''' Create a file client and add it to the context ''' if not 'cp.fileclient' in __context__: __context__['cp.fileclient'] = \ salt.fileclient.get_file_client(__opts__) def _render_filenames(path, dest, env, template): if not template: return (path, dest) # render the path as a template using path_template_engine as the engine if template not in salt.utils.templates.TEMPLATE_REGISTRY: raise CommandExecutionError( 'Attempted to render file paths with unavailable engine ' '{0}'.format(template) ) kwargs = {} kwargs['salt'] = __salt__ kwargs['pillar'] = __pillar__ kwargs['grains'] = __grains__ kwargs['opts'] = __opts__ kwargs['env'] = env def _render(contents): # write out path to temp file tmp_path_fn = salt.utils.mkstemp() with salt.utils.fopen(tmp_path_fn, 'w+') as fp_: fp_.write(contents) data = salt.utils.templates.TEMPLATE_REGISTRY[template]( tmp_path_fn, to_str=True, **kwargs ) salt.utils.safe_rm(tmp_path_fn) if not data['result']: # Failed to render the template raise CommandExecutionError( 'Failed to render file path with error: {0}'.format( data['data'] ) ) else: return data['data'] path = _render(path) dest = _render(dest) return (path, dest) def get_file(path, dest, env='base', makedirs=False, template=None, gzip=None): ''' Used to get a single file from the salt master CLI Example: .. code-block:: bash salt '*' cp.get_file salt://path/to/file /minion/dest Template rendering can be enabled on both the source and destination file names like so: .. code-block:: bash salt '*' cp.get_file "salt://{{grains.os}}/vimrc" /etc/vimrc template=jinja This example would instruct all Salt minions to download the vimrc from a directory with the same name as their os grain and copy it to /etc/vimrc For larger files, the cp.get_file module also supports gzip compression. Because gzip is CPU-intensive, this should only be used in scenarios where the compression ratio is very high (e.g. pretty-printed JSON or YAML files). Use the *gzip* named argument to enable it. Valid values are 1..9, where 1 is the lightest compression and 9 the heaviest. 1 uses the least CPU on the master (and minion), 9 uses the most. ''' (path, dest) = _render_filenames(path, dest, env, template) if not hash_file(path, env): return '' else: _mk_client() return __context__['cp.fileclient'].get_file( path, dest, makedirs, env, gzip) def get_template(path, dest, template='jinja', env='base', **kwargs): ''' Render a file as a template before setting it down CLI Example: .. code-block:: bash salt '*' cp.get_template salt://path/to/template /minion/dest ''' _mk_client() if not 'salt' in kwargs: kwargs['salt'] = __salt__ if not 'pillar' in kwargs: kwargs['pillar'] = __pillar__ if not 'grains' in kwargs: kwargs['grains'] = __grains__ if not 'opts' in kwargs: kwargs['opts'] = __opts__ return __context__['cp.fileclient'].get_template( path, dest, template, False, env, **kwargs) def get_dir(path, dest, env='base', template=None, gzip=None): ''' Used to recursively copy a directory from the salt master CLI Example: .. code-block:: bash salt '*' cp.get_dir salt://path/to/dir/ /minion/dest get_dir supports the same template and gzip arguments as get_file. ''' (path, dest) = _render_filenames(path, dest, env, template) _mk_client() return __context__['cp.fileclient'].get_dir(path, dest, env, gzip) def get_url(path, dest, env='base'): ''' Used to get a single file from a URL. CLI Example: .. code-block:: bash salt '*' cp.get_url salt://my/file /tmp/mine salt '*' cp.get_url http://www.slashdot.org /tmp/index.html ''' _mk_client() return __context__['cp.fileclient'].get_url(path, dest, False, env) def get_file_str(path, env='base'): ''' Return the contents of a file from a URL CLI Example: .. code-block:: bash salt '*' cp.get_file_str salt://my/file ''' fn_ = cache_file(path, env) with salt.utils.fopen(fn_, 'r') as fp_: data = fp_.read() return data def cache_file(path, env='base'): ''' Used to cache a single file in the local salt-master file cache. CLI Example: .. code-block:: bash salt '*' cp.cache_file salt://path/to/file ''' _mk_client() if path.startswith('salt://|'): # Strip pipe. Windows doesn't allow pipes in filenames path = 'salt://{0}'.format(path[8:]) result = __context__['cp.fileclient'].cache_file(path, env) if not result: log.error( 'Unable to cache file "{0}" from env "{1}".'.format( path, env ) ) return result def cache_files(paths, env='base'): ''' Used to gather many files from the master, the gathered files will be saved in the minion cachedir reflective to the paths retrieved from the master. CLI Example: .. code-block:: bash salt '*' cp.cache_files salt://pathto/file1,salt://pathto/file1 ''' _mk_client() return __context__['cp.fileclient'].cache_files(paths, env) def cache_dir(path, env='base', include_empty=False): ''' Download and cache everything under a directory from the master CLI Example: .. code-block:: bash salt '*' cp.cache_dir salt://path/to/dir ''' _mk_client() return __context__['cp.fileclient'].cache_dir(path, env, include_empty) def cache_master(env='base'): ''' Retrieve all of the files on the master and cache them locally CLI Example: .. code-block:: bash salt '*' cp.cache_master ''' _mk_client() return __context__['cp.fileclient'].cache_master(env) def cache_local_file(path): ''' Cache a local file on the minion in the localfiles cache CLI Example: .. code-block:: bash salt '*' cp.cache_local_file /etc/hosts ''' if not os.path.exists(path): return '' path_cached = is_cached(path) # If the file has already been cached, return the path if path_cached: path_hash = hash_file(path) path_cached_hash = hash_file(path_cached) if path_hash['hsum'] == path_cached_hash['hsum']: return path_cached # The file hasn't been cached or has changed; cache it _mk_client() return __context__['cp.fileclient'].cache_local_file(path) def list_states(env='base'): ''' List all of the available state modules in an environment CLI Example: .. code-block:: bash salt '*' cp.list_states ''' _mk_client() return __context__['cp.fileclient'].list_states(env) def list_master(env='base', prefix=''): ''' List all of the files stored on the master CLI Example: .. code-block:: bash salt '*' cp.list_master ''' _mk_client() return __context__['cp.fileclient'].file_list(env, prefix) def list_master_dirs(env='base', prefix=''): ''' List all of the directories stored on the master CLI Example: .. code-block:: bash salt '*' cp.list_master_dirs ''' _mk_client() return __context__['cp.fileclient'].dir_list(env, prefix) def list_minion(env='base'): ''' List all of the files cached on the minion CLI Example: .. code-block:: bash salt '*' cp.list_minion ''' _mk_client() return __context__['cp.fileclient'].file_local_list(env) def is_cached(path, env='base'): ''' Return a boolean if the given path on the master has been cached on the minion CLI Example: .. code-block:: bash salt '*' cp.is_cached salt://path/to/file ''' _mk_client() return __context__['cp.fileclient'].is_cached(path, env) def hash_file(path, env='base'): ''' Return the hash of a file, to get the hash of a file on the salt master file server prepend the path with salt:// otherwise, prepend the file with / for a local file. CLI Example: .. code-block:: bash salt '*' cp.hash_file salt://path/to/file ''' _mk_client() return __context__['cp.fileclient'].hash_file(path, env) def push(path): ''' Push a file from the minion up to the master, the file will be saved to the salt master in the master's minion files cachedir (defaults to ``/var/cache/salt/master/minions/minion-id/files``) Since this feature allows a minion to push a file up to the master server it is disabled by default for security purposes. To enable, set ``file_recv`` to ``True`` in the master configuration file, and restart the master. CLI Example: .. code-block:: bash salt '*' cp.push /etc/fstab ''' if '../' in path or not os.path.isabs(path): return False path = os.path.realpath(path) if not os.path.isfile(path): return False auth = _auth() load = {'cmd': '_file_recv', 'id': __opts__['id'], 'path': path.lstrip(os.sep), 'tok': auth.gen_token('salt')} sreq = salt.payload.SREQ(__opts__['master_uri']) with salt.utils.fopen(path, 'rb') as fp_: while True: load['loc'] = fp_.tell() load['data'] = fp_.read(__opts__['file_buffer_size']) if not load['data']: return True ret = sreq.send('aes', auth.crypticle.dumps(load)) if not ret: return ret salt-0.17.5+ds.orig/salt/modules/pkg_resource.py0000644000175000017500000003165312270576114017716 0ustar joejoe# -*- coding: utf-8 -*- ''' Resources needed by pkg providers ''' # Import python libs import fnmatch import logging import os import pprint import re import sys import yaml # Import salt libs import salt.utils log = logging.getLogger(__name__) __SUFFIX_NOT_NEEDED = ('x86_64', 'noarch') def _parse_pkg_meta(path): ''' Parse metadata from a binary package and return the package's name and version number. ''' def parse_rpm(path): try: from salt.modules.yumpkg5 import __QUERYFORMAT, _parse_pkginfo from salt.utils import namespaced_function as _namespaced_function _parse_pkginfo = _namespaced_function(_parse_pkginfo, globals()) except ImportError: log.critical('Error importing helper functions. This is almost ' 'certainly a bug.') return '', '' pkginfo = __salt__['cmd.run_all']( 'rpm -qp --queryformat {0!r} {1!r}'.format(__QUERYFORMAT, path) ).get('stdout', '').strip() pkginfo = _parse_pkginfo(pkginfo) if pkginfo is None: return '', '' else: return pkginfo.name, pkginfo.version def parse_pacman(path): name = '' version = '' result = __salt__['cmd.run_all']('pacman -Qpi "{0}"'.format(path)) if result['retcode'] == 0: for line in result['stdout'].splitlines(): if not name: match = re.match(r'^Name\s*:\s*(\S+)', line) if match: name = match.group(1) continue if not version: match = re.match(r'^Version\s*:\s*(\S+)', line) if match: version = match.group(1) continue return name, version def parse_deb(path): name = '' version = '' arch = '' # This is ugly, will have to try to find a better way of accessing the # __grains__ global. cpuarch = sys.modules[ __salt__['test.ping'].__module__ ].__grains__.get('cpuarch', '') osarch = sys.modules[ __salt__['test.ping'].__module__ ].__grains__.get('osarch', '') result = __salt__['cmd.run_all']('dpkg-deb -I "{0}"'.format(path)) if result['retcode'] == 0: for line in result['stdout'].splitlines(): if not name: try: name = re.match( r'^\s*Package\s*:\s*(\S+)', line ).group(1) except AttributeError: continue if not version: try: version = re.match( r'^\s*Version\s*:\s*(\S+)', line ).group(1) except AttributeError: continue if cpuarch == 'x86_64' and not arch: try: arch = re.match( r'^\s*Architecture\s*:\s*(\S+)', line ).group(1) except AttributeError: continue if arch and cpuarch == 'x86_64': if arch != 'all' and osarch == 'amd64' and osarch != arch: name += ':{0}'.format(arch) return name, version if __grains__['os_family'] in ('Suse', 'RedHat', 'Mandriva'): metaparser = parse_rpm elif __grains__['os_family'] in ('Arch',): metaparser = parse_pacman elif __grains__['os_family'] in ('Debian',): metaparser = parse_deb else: log.error('No metadata parser found for {0}'.format(path)) return '', '' return metaparser(path) def _repack_pkgs(pkgs): ''' Repack packages specified using "pkgs" argument to pkg states into a single dictionary ''' return dict( [ (str(x), str(y) if y is not None else y) for x, y in salt.utils.repack_dictlist(pkgs).iteritems() ] ) def pack_sources(sources): ''' Accepts list of dicts (or a string representing a list of dicts) and packs the key/value pairs into a single dict. ``'[{"foo": "salt://foo.rpm"}, {"bar": "salt://bar.rpm"}]'`` would become ``{"foo": "salt://foo.rpm", "bar": "salt://bar.rpm"}`` CLI Example: .. code-block:: bash salt '*' pkg_resource.pack_sources '[{"foo": "salt://foo.rpm"}, {"bar": "salt://bar.rpm"}]' ''' if isinstance(sources, basestring): try: sources = yaml.safe_load(sources) except yaml.parser.ParserError as err: log.error(err) return {} ret = {} for source in sources: if (not isinstance(source, dict)) or len(source) != 1: log.error('Invalid input: {0}'.format(pprint.pformat(sources))) log.error('Input must be a list of 1-element dicts') return {} else: ret.update(source) return ret def _verify_binary_pkg(srcinfo): ''' Compares package files (s) against the metadata to confirm that they match what is expected. ''' problems = [] for pkg_name, pkg_uri, pkg_path, pkg_type in srcinfo: pkgmeta_name, pkgmeta_version = _parse_pkg_meta(pkg_path) if not pkgmeta_name: if pkg_type == 'remote': problems.append('Failed to cache {0}. Are you sure this ' 'path is correct?'.format(pkg_uri)) elif pkg_type == 'local': if not os.path.isfile(pkg_path): problems.append('Package file {0} not found. Are ' 'you sure this path is ' 'correct?'.format(pkg_path)) else: problems.append('Unable to parse package metadata for ' '{0}.'.format(pkg_path)) elif pkg_name != pkgmeta_name: problems.append('Package file {0} (Name: {1}) does not ' 'match the specified package name ' '({2}).'.format(pkg_uri, pkgmeta_name, pkg_name)) return problems def parse_targets(name=None, pkgs=None, sources=None, **kwargs): ''' Parses the input to pkg.install and returns back the package(s) to be installed. Returns a list of packages, as well as a string noting whether the packages are to come from a repository or a binary package. CLI Example: .. code-block:: bash salt '*' pkg_resource.parse_targets ''' if __grains__['os'] == 'MacOS' and sources: log.warning('Parameter "sources" ignored on MacOS hosts.') if pkgs and sources: log.error('Only one of "pkgs" and "sources" can be used.') return None, None elif pkgs: pkgs = _repack_pkgs(pkgs) if not pkgs: return None, None else: return pkgs, 'repository' elif sources and __grains__['os'] != 'MacOS': sources = pack_sources(sources) if not sources: return None, None srcinfo = [] for pkg_name, pkg_src in sources.iteritems(): if __salt__['config.valid_fileproto'](pkg_src): # Cache package from remote source (salt master, HTTP, FTP) srcinfo.append((pkg_name, pkg_src, __salt__['cp.cache_file'](pkg_src, kwargs.get('__env__', 'base')), 'remote')) else: # Package file local to the minion srcinfo.append((pkg_name, pkg_src, pkg_src, 'local')) # Check metadata to make sure the name passed matches the source if __grains__['os_family'] not in ('Solaris',) \ and __grains__['os'] not in ('Gentoo', 'OpenBSD', 'FreeBSD'): problems = _verify_binary_pkg(srcinfo) # If any problems are found in the caching or metadata parsing done # in the above for loop, log each problem and return None,None, # which will keep package installation from proceeding. if problems: for problem in problems: log.error(problem) return None, None # srcinfo is a 4-tuple (pkg_name,pkg_uri,pkg_path,pkg_type), so grab # the package path (3rd element of tuple). return [x[2] for x in srcinfo], 'file' elif name: return dict([(x, None) for x in name.split(',')]), 'repository' else: log.error('No package sources passed to pkg.install.') return None, None def version(*names, **kwargs): ''' Common interface for obtaining the version of installed packages. CLI Example: .. code-block:: bash salt '*' pkg_resource.version vim salt '*' pkg_resource.version foo bar baz salt '*' pkg_resource.version 'python*' ''' ret = {} versions_as_list = \ salt.utils.is_true(kwargs.get('versions_as_list')) pkg_glob = False if len(names) != 0: pkgs = __salt__['pkg.list_pkgs'](versions_as_list=True) for name in names: if '*' in name: pkg_glob = True for match in fnmatch.filter(pkgs.keys(), name): ret[match] = pkgs.get(match, []) else: ret[name] = pkgs.get(name, []) if not versions_as_list: __salt__['pkg_resource.stringify'](ret) # Return a string if no globbing is used, and there is one item in the # return dict if len(ret) == 1 and not pkg_glob: try: return ret.values()[0] except IndexError: return '' return ret def add_pkg(pkgs, name, version): ''' Add a package to a dict of installed packages. CLI Example: .. code-block:: bash salt '*' pkg_resource.add_pkg '{}' bind 9 ''' try: pkgs.setdefault(name, []).append(version) except AttributeError as e: log.exception(e) def sort_pkglist(pkgs): ''' Accepts a dict obtained from pkg.list_pkgs() and sorts in place the list of versions for any packages that have multiple versions installed, so that two package lists can be compared to one another. CLI Example: .. code-block:: bash salt '*' pkg_resource.sort_pkglist '["3.45", "2.13"]' ''' # It doesn't matter that ['4.9','4.10'] would be sorted to ['4.10','4.9'], # so long as the sorting is consistent. try: for key in pkgs.keys(): pkgs[key].sort() except AttributeError as e: log.exception(e) def stringify(pkgs): ''' Takes a dict of package name/version information and joins each list of installed versions into a string. CLI Example: .. code-block:: bash salt '*' pkg_resource.stringify 'vim: 7.127' ''' try: for key in pkgs.keys(): pkgs[key] = ','.join(pkgs[key]) except AttributeError as e: log.exception(e) def find_changes(old=None, new=None): ''' Compare before and after results from pkg.list_pkgs() to determine what changes were made to the packages installed on the minion. CLI Example: .. code-block:: bash salt '*' pkg_resource.find_changes ''' pkgs = {} for npkg in set((new or {}).keys()).union((old or {}).keys()): if npkg not in old: # the package is freshly installed pkgs[npkg] = {'old': '', 'new': new[npkg]} elif npkg not in new: # the package is removed pkgs[npkg] = {'new': '', 'old': old[npkg]} elif new[npkg] != old[npkg]: # the package was here before and the version has changed pkgs[npkg] = {'old': old[npkg], 'new': new[npkg]} return pkgs def version_clean(version): ''' Clean the version string removing extra data. This function will simply try to call ``pkg.version_clean``. CLI Example: .. code-block:: bash salt '*' pkg_resource.version_clean ''' if version and 'pkg.version_clean' in __salt__: return __salt__['pkg.version_clean'](version) return version def check_extra_requirements(pkgname, pkgver): ''' Check if the installed package already has the given requirements. This function will simply try to call "pkg.check_extra_requirements". CLI Example: .. code-block:: bash salt '*' pkg_resource.check_extra_requirements ''' if pkgver and 'pkg.check_extra_requirements' in __salt__: return __salt__['pkg.check_extra_requirements'](pkgname, pkgver) return True salt-0.17.5+ds.orig/salt/modules/extfs.py0000644000175000017500000002140712263042153016344 0ustar joejoe# -*- coding: utf-8 -*- ''' Module for managing ext2/3/4 file systems ''' # Import python libs import logging # Import salt libs import salt.utils log = logging.getLogger(__name__) def __virtual__(): ''' Only work on POSIX-like systems ''' if salt.utils.is_windows(): return False return 'extfs' def mkfs(device, fs_type, **kwargs): ''' Create a file system on the specified device CLI Example: .. code-block:: bash salt '*' extfs.mkfs /dev/sda1 fs_type=ext4 opts='acl,noexec' Valid options are:: block_size: 1024, 2048 or 4096 check: check for bad blocks direct: use direct IO ext_opts: extended file system options (comma-separated) fragment_size: size of fragments force: setting force to True will cause mke2fs to specify the -F option twice (it is already set once); this is truly dangerous blocks_per_group: number of blocks in a block group number_of_groups: ext4 option for a virtual block group bytes_per_inode: set the bytes/inode ratio inode_size: size of the inode journal: set to True to create a journal (default on ext3/4) journal_opts: options for the fs journal (comma separated) blocks_file: read bad blocks from file label: label to apply to the file system reserved: percentage of blocks reserved for super-user last_dir: last mounted directory test: set to True to not actually create the file system (mke2fs -n) number_of_inodes: override default number of inodes creator_os: override "creator operating system" field opts: mount options (comma separated) revision: set the filesystem revision (default 1) super: write superblock and group descriptors only fs_type: set the filesystem type (REQUIRED) usage_type: how the filesystem is going to be used uuid: set the UUID for the file system See the ``mke2fs(8)`` manpage for a more complete description of these options. ''' kwarg_map = {'block_size': 'b', 'check': 'c', 'direct': 'D', 'ext_opts': 'E', 'fragment_size': 'f', 'force': 'F', 'blocks_per_group': 'g', 'number_of_groups': 'G', 'bytes_per_inode': 'i', 'inode_size': 'I', 'journal': 'j', 'journal_opts': 'J', 'blocks_file': 'l', 'label': 'L', 'reserved': 'm', 'last_dir': 'M', 'test': 'n', 'number_of_inodes': 'N', 'creator_os': 'o', 'opts': 'O', 'revision': 'r', 'super': 'S', 'usage_type': 'T', 'uuid': 'U'} opts = '' for key in kwargs: if key in kwarg_map: opt = kwarg_map[key] if kwargs[key] == 'True': opts += '-{0} '.format(opt) else: opts += '-{0} {1} '.format(opt, kwargs[key]) cmd = 'mke2fs -F -t {0} {1}{2}'.format(fs_type, opts, device) out = __salt__['cmd.run'](cmd).splitlines() ret = [] for line in out: if not line: continue elif line.startswith('mke2fs'): continue elif line.startswith('Discarding device blocks'): continue elif line.startswith('Allocating group tables'): continue elif line.startswith('Writing inode tables'): continue elif line.startswith('Creating journal'): continue elif line.startswith('Writing superblocks'): continue ret.append(line) return ret def tune(device, **kwargs): ''' Set attributes for the specified device (using tune2fs) CLI Example: .. code-block:: bash salt '*' extfs.tune /dev/sda1 force=True label=wildstallyns opts='acl,noexec' Valid options are:: max: max mount count count: mount count error: error behavior extended_opts: extended options (comma separated) force: force, even if there are errors (set to True) group: group name or gid that can use the reserved blocks interval: interval between checks journal: set to True to create a journal (default on ext3/4) journal_opts: options for the fs journal (comma separated) label: label to apply to the file system reserved: percentage of blocks reserved for super-user last_dir: last mounted directory opts: mount options (comma separated) feature: set or clear a feature (comma separated) mmp_check: mmp check interval reserved: reserved blocks count quota_opts: quota options (comma separated) time: time last checked user: user or uid who can use the reserved blocks uuid: set the UUID for the file system See the ``mke2fs(8)`` manpage for a more complete description of these options. ''' kwarg_map = {'max': 'c', 'count': 'C', 'error': 'e', 'extended_opts': 'E', 'force': 'f', 'group': 'g', 'interval': 'i', 'journal': 'j', 'journal_opts': 'J', 'label': 'L', 'last_dir': 'M', 'opts': 'o', 'feature': 'O', 'mmp_check': 'p', 'reserved': 'r', 'quota_opts': 'Q', 'time': 'T', 'user': 'u', 'uuid': 'U'} opts = '' for key in kwargs: if key in kwarg_map: opt = kwarg_map[key] if kwargs[key] == 'True': opts += '-{0} '.format(opt) else: opts += '-{0} {1} '.format(opt, kwargs[key]) cmd = 'tune2fs {0}{1}'.format(opts, device) out = __salt__['cmd.run'](cmd).splitlines() return out def attributes(device, args=None): ''' Return attributes from dumpe2fs for a specified device CLI Example: .. code-block:: bash salt '*' extfs.attributes /dev/sda1 ''' fsdump = dump(device, args) return fsdump['attributes'] def blocks(device, args=None): ''' Return block and inode info from dumpe2fs for a specified device CLI Example: .. code-block:: bash salt '*' extfs.blocks /dev/sda1 ''' fsdump = dump(device, args) return fsdump['blocks'] def dump(device, args=None): ''' Return all contents of dumpe2fs for a specified device CLI Example: .. code-block:: bash salt '*' extfs.dump /dev/sda1 ''' cmd = 'dumpe2fs {0}'.format(device) if args: cmd = cmd + ' -' + args ret = {'attributes': {}, 'blocks': {}} out = __salt__['cmd.run'](cmd).splitlines() mode = 'opts' group = None for line in out: if not line: continue if line.startswith('dumpe2fs'): continue if mode == 'opts': line = line.replace('\t', ' ') comps = line.split(': ') if line.startswith('Filesystem features'): ret['attributes'][comps[0]] = comps[1].split() elif line.startswith('Group'): mode = 'blocks' else: ret['attributes'][comps[0]] = comps[1].strip() if mode == 'blocks': if line.startswith('Group'): line = line.replace(':', '') line = line.replace('(', '') line = line.replace(')', '') line = line.replace('[', '') line = line.replace(']', '') comps = line.split() blkgrp = comps[1] group = 'Group {0}'.format(blkgrp) ret['blocks'][group] = {} ret['blocks'][group]['group'] = blkgrp ret['blocks'][group]['range'] = comps[3] # TODO: comps[4:], which may look one one of the following: # ITABLE_ZEROED # INODE_UNINIT, ITABLE_ZEROED # Does anyone know what to call these? ret['blocks'][group]['extra'] = [] elif 'Free blocks:' in line: comps = line.split(': ') free_blocks = comps[1].split(', ') ret['blocks'][group]['free blocks'] = free_blocks elif 'Free inodes:' in line: comps = line.split(': ') inodes = comps[1].split(', ') ret['blocks'][group]['free inodes'] = inodes else: line = line.strip() ret['blocks'][group]['extra'].append(line) return ret salt-0.17.5+ds.orig/salt/modules/freebsdservice.py0000644000175000017500000001653112270576114020217 0ustar joejoe# -*- coding: utf-8 -*- ''' The service module for FreeBSD ''' # Import python libs import logging import os # Import salt libs import salt.utils import salt.utils.decorators as decorators from salt.exceptions import CommandNotFoundError __func_alias__ = { 'reload_': 'reload' } log = logging.getLogger(__name__) def __virtual__(): ''' Only work on systems which default to systemd ''' # Disable on these platforms, specific service modules exist: if __grains__['os'] == 'FreeBSD': return 'service' return False @decorators.memoize def _cmd(): ''' Return full path to service command ''' service = salt.utils.which('service') if not service: raise CommandNotFoundError return service def _get_rcscript(name): ''' Return full path to service rc script ''' cmd = '{0} -r'.format(_cmd()) for line in __salt__['cmd.run_stdout'](cmd).splitlines(): if line.endswith('{0}{1}'.format(os.path.sep, name)): return line return None def _get_rcvar(name): ''' Return rcvar ''' if not available(name): log.error('Service {0} not found'.format(name)) return False cmd = '{0} {1} rcvar'.format(_cmd(), name) for line in __salt__['cmd.run_stdout'](cmd).splitlines(): if not '_enable="' in line: continue rcvar, _ = line.split('=', 1) return rcvar return None def get_enabled(): ''' Return what services are set to run on boot CLI Example: .. code-block:: bash salt '*' service.get_enabled ''' ret = [] service = _cmd() for svc in __salt__['cmd.run']('{0} -e'.format(service)).splitlines(): ret.append(os.path.basename(svc)) # This is workaround for bin/173454 bug for svc in get_all(): if svc in ret: continue if not os.path.exists('/etc/rc.conf.d/{0}'.format(svc)): continue if enabled(svc): ret.append(svc) return sorted(ret) def get_disabled(): ''' Return what services are available but not enabled to start at boot CLI Example: .. code-block:: bash salt '*' service.get_disabled ''' en_ = get_enabled() all_ = get_all() return sorted(set(all_) - set(en_)) def _switch(name, # pylint: disable=C0103 on, # pylint: disable=C0103 **kwargs): ''' Switch on/off service start at boot. ''' if not available(name): return False rcvar = _get_rcvar(name) if not rcvar: log.error('rcvar for service {0} not found'.format(name)) return False config = kwargs.get('config', __salt__['config.option']('service.config', default='/etc/rc.conf' ) ) if not config: rcdir = '/etc/rc.conf.d' if not os.path.exists(rcdir) or not os.path.isdir(rcdir): log.error('{0} not exists'.format(rcdir)) return False config = os.path.join(rcdir, rcvar.replace('_enable', '')) nlines = [] edited = False if on: val = 'YES' else: val = 'NO' if os.path.exists(config): with salt.utils.fopen(config, 'r') as ifile: for line in ifile: if not line.startswith('{0}='.format(rcvar)): nlines.append(line) continue rest = line[len(line.split()[0]):] # keep comments etc nlines.append('{0}="{1}"{2}'.format(rcvar, val, rest)) edited = True if not edited: nlines.append('{0}="{1}"\n'.format(rcvar, val)) with salt.utils.fopen(config, 'w') as ofile: ofile.writelines(nlines) return True def enable(name, **kwargs): ''' Enable the named service to start at boot name service name config : /etc/rc.conf Config file for managing service. If config value is empty string, then /etc/rc.conf.d/ used. See man rc.conf(5) for details. Also service.config variable can be used to change default. CLI Example: .. code-block:: bash salt '*' service.enable ''' return _switch(name, True, **kwargs) def disable(name, **kwargs): ''' Disable the named service to start at boot Arguments the same as for enable() CLI Example: .. code-block:: bash salt '*' service.disable ''' return _switch(name, False, **kwargs) def enabled(name): ''' Return True if the named service is enabled, false otherwise name Service name CLI Example: .. code-block:: bash salt '*' service.enabled ''' if not available(name): log.error('Service {0} not found'.format(name)) return False cmd = '{0} {1} rcvar'.format(_cmd(), name) for line in __salt__['cmd.run_stdout'](cmd).splitlines(): if not '_enable="' in line: continue _, state, _ = line.split('"', 2) return state.lower() in ('yes', 'true', 'on', '1') # probably will never reached return False def disabled(name): ''' Return True if the named service is enabled, false otherwise CLI Example: .. code-block:: bash salt '*' service.disabled ''' return not enabled(name) def available(name): ''' Check that the given service is available. CLI Example: .. code-block:: bash salt '*' service.available sshd ''' return name in get_all() def get_all(): ''' Return a list of all available services CLI Example: .. code-block:: bash salt '*' service.get_all ''' ret = [] service = _cmd() for srv in __salt__['cmd.run']('{0} -l'.format(service)).splitlines(): if not srv.isupper(): ret.append(srv) return sorted(ret) def start(name): ''' Start the specified service CLI Example: .. code-block:: bash salt '*' service.start ''' cmd = '{0} {1} onestart'.format(_cmd(), name) return not __salt__['cmd.retcode'](cmd) def stop(name): ''' Stop the specified service CLI Example: .. code-block:: bash salt '*' service.stop ''' cmd = '{0} {1} onestop'.format(_cmd(), name) return not __salt__['cmd.retcode'](cmd) def restart(name): ''' Restart the named service CLI Example: .. code-block:: bash salt '*' service.restart ''' cmd = '{0} {1} onerestart'.format(_cmd(), name) return not __salt__['cmd.retcode'](cmd) def reload_(name): ''' Restart the named service CLI Example: .. code-block:: bash salt '*' service.reload ''' cmd = '{0} {1} onereload'.format(_cmd(), name) return not __salt__['cmd.retcode'](cmd) def status(name, sig=None): ''' Return the status for a service (True or False). name Name of service CLI Example: .. code-block:: bash salt '*' service.status ''' if sig: return bool(__salt__['status.pid'](sig)) cmd = '{0} {1} onestatus'.format(_cmd(), name) return not __salt__['cmd.retcode'](cmd) salt-0.17.5+ds.orig/salt/modules/ret.py0000644000175000017500000000247712263042153016013 0ustar joejoe# -*- coding: utf-8 -*- ''' Module to integrate with the returner system and retrieve data sent to a salt returner ''' # Import salt libs import salt.loader def get_jid(returner, jid): ''' Return the information for a specified job id CLI Example: .. code-block:: bash salt '*' ret.get_jid redis 20421104181954700505 ''' returners = salt.loader.returners(__opts__, __salt__) return returners['{0}.get_jid'.format(returner)](jid) def get_fun(returner, fun): ''' Return info about last time fun was called on each minion CLI Example: .. code-block:: bash salt '*' ret.get_fun mysql network.interfaces ''' returners = salt.loader.returners(__opts__, __salt__) return returners['{0}.get_fun'.format(returner)](fun) def get_jids(returner): ''' Return a list of all job ids CLI Example: .. code-block:: bash salt '*' ret.get_jids mysql ''' returners = salt.loader.returners(__opts__, __salt__) return returners['{0}.get_jids'.format(returner)]() def get_minions(returner): ''' Return a list of all minions CLI Example: .. code-block:: bash salt '*' ret.get_minions mysql ''' returners = salt.loader.returners(__opts__, __salt__) return returners['{0}.get_minions'.format(returner)]() salt-0.17.5+ds.orig/salt/modules/img.py0000644000175000017500000000461212263042153015766 0ustar joejoe# -*- coding: utf-8 -*- ''' Virtual machine image management tools ''' # Import python libs import logging # Set up logging log = logging.getLogger(__name__) def mount_image(location): ''' Mount the named image and return the mount point CLI Example: .. code-block:: bash salt '*' img.mount_image /tmp/foo ''' if 'guestfs.mount' in __salt__: return __salt__['guestfs.mount'](location) elif 'qemu_nbd.init' in __salt__: mnt = __salt__['qemu_nbd.init'](location) if not mnt: return '' first = mnt.keys()[0] __context__['img.mnt_{0}'.format(first)] = mnt return first return '' #compatibility for api change mnt_image = mount_image def umount_image(mnt): ''' Unmount an image mountpoint CLI Example: .. code-block:: bash salt '*' img.umount_image /mnt/foo ''' if 'qemu_nbd.clear' in __salt__: if 'img.mnt_{0}'.format(mnt) in __context__: __salt__['qemu_nbd.clear'](__context__['img.mnt_{0}'.format(mnt)]) return __salt__['mount.umount'](mnt) #def get_image(name): # ''' # Download a vm image from a remote source and add it to the image cache # system # ''' # cache_dir = os.path.join(__salt__['config.option']('img.cache'), 'src') # parse = urlparse.urlparse(name) # if __salt__['config.valid_file_proto'](parse.scheme): # # Valid scheme to download # dest = os.path.join(cache_dir, parse.netloc) # sfn = __salt__['file.get_managed'](dest, None, name, ) def bootstrap(location, size, fmt): ''' HIGHLY EXPERIMENTAL Bootstrap a virtual machine image location: The location to create the image size: The size of the image to create in megabytes fmt: The image format, raw or qcow2 CLI Example: .. code-block:: bash salt '*' qemu_nbd.bootstrap /srv/salt-images/host.qcow 4096 qcow2 ''' location = __salt__['img.make_image'](location, size, fmt) if not location: return '' nbd = __salt__['qemu_nbd.connect'](location) __salt__['partition.mklabel'](nbd, 'msdos') __salt__['partition.mkpart'](nbd, 'primary', 'ext4', 1, -1) __salt__['partition.probe'](nbd) __salt__['partition.mkfs']('{0}p1'.format(nbd), 'ext4') mnt = __salt__['qemu_nbd.mount'](nbd) #return __salt__['pkg.bootstrap'](nbd, mnt.keys()[0]) salt-0.17.5+ds.orig/salt/modules/timezone.py0000644000175000017500000002000112263042153017032 0ustar joejoe# -*- coding: utf-8 -*- ''' Module for managing timezone on POSIX-like systems. ''' # Import python libs import os import hashlib import logging # Import salt libs import salt.utils log = logging.getLogger(__name__) def __virtual__(): ''' Only work on POSIX-like systems ''' if salt.utils.is_windows(): return False return 'timezone' def get_zone(): ''' Get current timezone (i.e. America/Denver) CLI Example: .. code-block:: bash salt '*' timezone.get_zone ''' cmd = '' if 'Arch' in __grains__['os_family']: cmd = ('timedatectl | grep Timezone |' 'sed -e"s/: /=/" -e"s/^[ \t]*//" | cut -d" " -f1') elif 'RedHat' in __grains__['os_family']: cmd = 'grep ZONE /etc/sysconfig/clock | grep -vE "^#"' elif 'Suse' in __grains__['os_family']: cmd = 'grep ZONE /etc/sysconfig/clock | grep -vE "^#"' elif 'Debian' in __grains__['os_family']: with salt.utils.fopen('/etc/timezone', 'r') as ofh: return ofh.read() elif 'Gentoo' in __grains__['os_family']: with salt.utils.fopen('/etc/timezone', 'r') as ofh: return ofh.read() elif 'FreeBSD' in __grains__['os_family']: return ('FreeBSD does not store a human-readable timezone. Please' 'consider using timezone.get_zonecode or timezone.zonecompare') elif 'Solaris' in __grains__['os_family']: cmd = 'grep "TZ=" /etc/TIMEZONE' out = __salt__['cmd.run'](cmd).split('=') ret = out[1].replace('"', '') return ret def get_zonecode(): ''' Get current timezone (i.e. PST, MDT, etc) CLI Example: .. code-block:: bash salt '*' timezone.get_zonecode ''' cmd = 'date +%Z' out = __salt__['cmd.run'](cmd) return out def get_offset(): ''' Get current numeric timezone offset from UCT (i.e. -0700) CLI Example: .. code-block:: bash salt '*' timezone.get_offset ''' cmd = 'date +%z' out = __salt__['cmd.run'](cmd) return out def set_zone(timezone): ''' Unlinks, then symlinks /etc/localtime to the set timezone. The timezone is crucial to several system processes, each of which SHOULD be restarted (for instance, whatever you system uses as its cron and syslog daemons). This will not be magically done for you! CLI Example: .. code-block:: bash salt '*' timezone.set_zone 'America/Denver' ''' if 'Solaris' in __grains__['os_family']: zonepath = '/usr/share/lib/zoneinfo/{0}'.format(timezone) else: zonepath = '/usr/share/zoneinfo/{0}'.format(timezone) if not os.path.exists(zonepath): return 'Zone does not exist: {0}'.format(zonepath) if os.path.exists('/etc/localtime'): os.unlink('/etc/localtime') if 'Solaris' in __grains__['os_family']: __salt__['file.sed']( '/etc/default/init', '^TZ=.*', 'TZ={0}'.format(timezone)) else: os.symlink(zonepath, '/etc/localtime') if 'Arch' in __grains__['os_family']: __salt__['file.sed']( '/etc/rc.conf', '^TIMEZONE=.*', 'TIMEZONE="{0}"'.format(timezone)) elif 'RedHat' in __grains__['os_family']: __salt__['file.sed']( '/etc/sysconfig/clock', '^ZONE=.*', 'ZONE="{0}"'.format(timezone)) elif 'Suse' in __grains__['os_family']: __salt__['file.sed']( '/etc/sysconfig/clock', '^ZONE=.*', 'ZONE="{0}"'.format(timezone)) elif 'Debian' in __grains__['os_family']: with salt.utils.fopen('/etc/timezone', 'w') as ofh: ofh.write(timezone) elif 'Gentoo' in __grains__['os_family']: with salt.utils.fopen('/etc/timezone', 'w') as ofh: ofh.write(timezone) return True def zone_compare(timezone): ''' Checks the md5sum between the given timezone, and the one set in /etc/localtime. Returns True if they match, and False if not. Mostly useful for running state checks. CLI Example: .. code-block:: bash salt '*' timezone.zone_compare 'America/Denver' ''' if 'Solaris' in __grains__['os_family']: return 'Not implemented for Solaris family' tzfile = '/etc/localtime' zonepath = '/usr/share/zoneinfo/{0}'.format(timezone) if not os.path.exists(tzfile): return 'Error: {0} does not exist.'.format(tzfile) with salt.utils.fopen(zonepath, 'r') as fp_: usrzone = hashlib.md5(fp_.read()).hexdigest() with salt.utils.fopen(tzfile, 'r') as fp_: etczone = hashlib.md5(fp_.read()).hexdigest() if usrzone == etczone: return True return False def get_hwclock(): ''' Get current hardware clock setting (UTC or localtime) CLI Example: .. code-block:: bash salt '*' timezone.get_hwclock ''' cmd = '' if 'Arch' in __grains__['os_family']: cmd = 'grep HARDWARECLOCK /etc/rc.conf | grep -vE "^#"' out = __salt__['cmd.run'](cmd).split('=') return out[1].replace('"', '') elif 'RedHat' in __grains__['os_family']: cmd = 'tail -n 1 /etc/adjtime' return __salt__['cmd.run'](cmd) elif 'Suse' in __grains__['os_family']: cmd = 'tail -n 1 /etc/adjtime' return __salt__['cmd.run'](cmd) elif 'Debian' in __grains__['os_family']: #Original way to look up hwclock on Debian-based systems cmd = 'grep "UTC=" /etc/default/rcS | grep -vE "^#"' out = __salt__['cmd.run'](cmd).split('=') if len(out) > 1: if out[1] == 'yes': return 'UTC' else: return 'localtime' else: #Since Wheezy cmd = 'tail -n 1 /etc/adjtime' return __salt__['cmd.run'](cmd) elif 'Gentoo' in __grains__['os_family']: cmd = 'grep "^clock=" /etc/conf.d/hwclock | grep -vE "^#"' out = __salt__['cmd.run'](cmd).split('=') return out[1].replace('"', '') elif 'Solaris' in __grains__['os_family']: if os.path.isfile('/etc/rtc_config'): with salt.utils.fopen('/etc/rtc_config', 'r') as fp_: for line in fp_: if line.startswith('zone_info=GMT'): return 'UTC' return 'localtime' else: return 'UTC' def set_hwclock(clock): ''' Sets the hardware clock to be either UTC or localtime CLI Example: .. code-block:: bash salt '*' timezone.set_hwclock UTC ''' timezone = get_zone() if 'Solaris' in __grains__['os_family']: if 'sparc' in __grains__['cpuarch']: return 'UTC is the only choice for SPARC architecture' if clock == 'localtime': cmd = 'rtc -z {0}'.format(timezone) __salt__['cmd.run'](cmd) return True elif clock == 'UTC': cmd = 'rtc -z GMT' __salt__['cmd.run'](cmd) return True else: zonepath = '/usr/share/zoneinfo/{0}'.format(timezone) if not os.path.exists(zonepath): return 'Zone does not exist: {0}'.format(zonepath) if 'Solaris' not in __grains__['os_family']: os.unlink('/etc/localtime') os.symlink(zonepath, '/etc/localtime') if 'Arch' in __grains__['os_family']: __salt__['file.sed']( '/etc/rc.conf', '^HARDWARECLOCK=.*', 'HARDWARECLOCK="{0}"'.format( clock)) elif 'RedHat' in __grains__['os_family']: __salt__['file.sed']( '/etc/sysconfig/clock', '^ZONE=.*', 'ZONE="{0}"'.format(timezone)) elif 'Suse' in __grains__['os_family']: __salt__['file.sed']( '/etc/sysconfig/clock', '^ZONE=.*', 'ZONE="{0}"'.format(timezone)) elif 'Debian' in __grains__['os_family']: if clock == 'UTC': __salt__['file.sed']('/etc/default/rcS', '^UTC=.*', 'UTC=yes') elif clock == 'localtime': __salt__['file.sed']('/etc/default/rcS', '^UTC=.*', 'UTC=no') elif 'Gentoo' in __grains__['os_family']: __salt__['file.sed']( '/etc/conf.d/hwclock', '^clock=.*', 'clock="{0}"'.format(clock)) return True salt-0.17.5+ds.orig/salt/modules/linux_acl.py0000644000175000017500000001413012270576114017173 0ustar joejoe# -*- coding: utf-8 -*- ''' Support for Linux File Access Control Lists ''' # Import salt libs import salt.utils def __virtual__(): ''' Only load the module if getfacl is installed ''' if salt.utils.which('getfacl'): return 'acl' return False def version(): ''' Return facl version from getfacl --version CLI Example: .. code-block:: bash salt '*' acl.version ''' cmd = 'getfacl --version' out = __salt__['cmd.run'](cmd).splitlines() ret = out[0].split() return ret[1].strip() def getfacl(*args): ''' Return (extremely verbose) map of FACLs on specified file(s) CLI Examples: .. code-block:: bash salt '*' acl.getfacl /tmp/house/kitchen salt '*' acl.getfacl /tmp/house/kitchen /tmp/house/livingroom ''' ret = {} cmd = 'getfacl -p' for dentry in args: cmd += ' {0}'.format(dentry) out = __salt__['cmd.run'](cmd).splitlines() dentry = '' for line in out: if not line: continue elif line.startswith('getfacl'): continue elif line.startswith('#'): comps = line.replace('# ', '').split(': ') if comps[0] == 'file': dentry = comps[1] ret[dentry] = {'comments': {}, 'users': [], 'groups': []} ret[dentry]['comments'][comps[0]] = comps[1] if comps[0] == 'flags': flags = list(comps[1]) if flags[0] == 's': ret[dentry]['suid'] = True if flags[1] == 's': ret[dentry]['sgid'] = True if flags[2] == 't': ret[dentry]['sticky'] = True else: vals = _parse_acl(acl=line, user=ret[dentry]['comments']['owner'], group=ret[dentry]['comments']['group']) acl_type = vals['type'] del vals['type'] for entity in ('user', 'group'): plural = entity + 's' if entity in vals.keys(): usergroup = vals[entity] del vals[entity] if acl_type == 'acl': ret[dentry][plural].append({usergroup: vals}) elif acl_type == 'default': if 'defaults' not in ret[dentry].keys(): ret[dentry]['defaults'] = {} if plural not in ret[dentry]['defaults'].keys(): ret[dentry]['defaults'][plural] = [] ret[dentry]['defaults'][plural].append({usergroup: vals}) for entity in ('other', 'mask'): if entity in vals.keys(): del vals[entity] ret[dentry][entity] = vals return ret def _parse_acl(acl, user, group): ''' Parse a single ACL rule ''' comps = acl.split(':') vals = {} # What type of rule is this? vals['type'] = 'acl' if comps[0] == 'default': vals['type'] = 'default' comps.pop(0) # If a user is not specified, use the owner of the file if comps[0] == 'user' and not comps[1]: comps[1] = user elif comps[0] == 'group' and not comps[1]: comps[1] = group vals[comps[0]] = comps[1] # Set the permissions fields octal = 0 vals['permissions'] = {} if 'r' in comps[2]: octal += 4 vals['permissions']['read'] = True else: vals['permissions']['read'] = False if 'w' in comps[2]: octal += 2 vals['permissions']['write'] = True else: vals['permissions']['write'] = False if 'x' in comps[2]: octal += 1 vals['permissions']['execute'] = True else: vals['permissions']['execute'] = False vals['octal'] = octal return vals def wipefacls(*args): ''' Remove all FACLs from the specified file(s) CLI Examples: .. code-block:: bash salt '*' acl.wipefacls /tmp/house/kitchen salt '*' acl.wipefacls /tmp/house/kitchen /tmp/house/livingroom ''' cmd = 'setfacl -b' for dentry in args: cmd += ' {0}'.format(dentry) __salt__['cmd.run'](cmd) return True def modfacl(acl_type, acl_name, perms, *args): ''' Add or modify a FACL for the specified file(s) CLI Examples: .. code-block:: bash salt '*' acl.addfacl user myuser rwx /tmp/house/kitchen salt '*' acl.addfacl default:group mygroup rx /tmp/house/kitchen salt '*' acl.addfacl d:u myuser 7 /tmp/house/kitchen salt '*' acl.addfacl g mygroup 0 /tmp/house/kitchen /tmp/house/livingroom ''' cmd = 'setfacl -m' prefix = '' if acl_type.startswith('d'): prefix = 'd:' acl_type = acl_type.replace('default:', '') acl_type = acl_type.replace('d:', '') if acl_type == 'user' or acl_type == 'u': prefix += 'u' elif acl_type == 'group' or acl_type == 'g': prefix += 'g' cmd = '{0} {1}:{2}:{3}'.format(cmd, prefix, acl_name, perms) for dentry in args: cmd += ' {0}'.format(dentry) __salt__['cmd.run'](cmd) return True def delfacl(acl_type, acl_name, *args): ''' Remove specific FACL from the specified file(s) CLI Examples: .. code-block:: bash salt '*' acl.delfacl user myuser /tmp/house/kitchen salt '*' acl.delfacl default:group mygroup /tmp/house/kitchen salt '*' acl.delfacl d:u myuser /tmp/house/kitchen salt '*' acl.delfacl g myuser /tmp/house/kitchen /tmp/house/livingroom ''' cmd = 'setfacl -x' prefix = '' if acl_type.startswith('d'): prefix = 'd:' acl_type = acl_type.replace('default:', '') acl_type = acl_type.replace('d:', '') if acl_type == 'user' or acl_type == 'u': prefix += 'u' elif acl_type == 'group' or acl_type == 'g': prefix += 'g' cmd = '{0} {1}:{2}'.format(cmd, prefix, acl_name) for dentry in args: cmd += ' {0}'.format(dentry) __salt__['cmd.run'](cmd) return True salt-0.17.5+ds.orig/salt/modules/zypper.py0000644000175000017500000003166512270576114016562 0ustar joejoe# -*- coding: utf-8 -*- ''' Package support for openSUSE via the zypper package manager ''' # Import python libs import copy import logging import re # Import salt libs import salt.utils log = logging.getLogger(__name__) def __virtual__(): ''' Set the virtual pkg module if the os is openSUSE ''' if __grains__.get('os_family', '') != 'Suse': return False # Not all versions of Suse use zypper, check that it is available if not salt.utils.which('zypper'): return False return 'pkg' def list_upgrades(refresh=True): ''' List all available package upgrades on this system CLI Example: .. code-block:: bash salt '*' pkg.list_upgrades ''' if salt.utils.is_true(refresh): refresh_db() ret = {} out = __salt__['cmd.run_stdout']('zypper list-updates').splitlines() for line in out: if not line: continue if '|' not in line: continue try: status, repo, name, cur, avail, arch = \ [x.strip() for x in line.split('|')] except (ValueError, IndexError): continue if status == 'v': ret[name] = avail return ret # Provide a list_updates function for those used to using zypper list-updates list_updates = list_upgrades def latest_version(*names, **kwargs): ''' Return the latest version of the named package available for upgrade or installation. If more than one package name is specified, a dict of name/version pairs is returned. If the latest version of a given package is already installed, an empty string will be returned for that package. CLI Example: .. code-block:: bash salt '*' pkg.latest_version salt '*' pkg.latest_version ... ''' refresh = salt.utils.is_true(kwargs.pop('refresh', True)) if len(names) == 0: return '' ret = {} for name in names: ret[name] = '' # Refresh before looking for the latest version available if refresh: refresh_db() cmd = 'zypper info -t package {0}'.format(' '.join(names)) output = __salt__['cmd.run_all'](cmd).get('stdout', '') output = re.split('Information for package \\S+:\n', output) for package in output: pkginfo = {} for line in package.splitlines(): try: key, val = line.split(':', 1) key = key.lower() val = val.strip() except ValueError: continue else: pkginfo[key] = val # Ignore if the needed keys weren't found in this iteration if not set(('name', 'version', 'status')) <= set(pkginfo.keys()): continue status = pkginfo['status'].lower() if 'not installed' in status or 'out-of-date' in status: ret[pkginfo['name']] = pkginfo['version'] # Return a string if only one package name passed if len(names) == 1: return ret[names[0]] return ret # available_version is being deprecated available_version = latest_version def upgrade_available(name): ''' Check whether or not an upgrade is available for a given package CLI Example: .. code-block:: bash salt '*' pkg.upgrade_available ''' return latest_version(name) != '' def version(*names, **kwargs): ''' Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. CLI Example: .. code-block:: bash salt '*' pkg.version salt '*' pkg.version ... ''' return __salt__['pkg_resource.version'](*names, **kwargs) def list_pkgs(versions_as_list=False, **kwargs): ''' List the packages currently installed as a dict:: {'': ''} CLI Example: .. code-block:: bash salt '*' pkg.list_pkgs ''' versions_as_list = salt.utils.is_true(versions_as_list) # 'removed' not yet implemented or not applicable if salt.utils.is_true(kwargs.get('removed')): return {} if 'pkg.list_pkgs' in __context__: if versions_as_list: return __context__['pkg.list_pkgs'] else: ret = copy.deepcopy(__context__['pkg.list_pkgs']) __salt__['pkg_resource.stringify'](ret) return ret cmd = 'rpm -qa --queryformat "%{NAME}_|-%{VERSION}_|-%{RELEASE}\\n"' ret = {} for line in __salt__['cmd.run'](cmd).splitlines(): name, pkgver, rel = line.split('_|-') if rel: pkgver += '-{0}'.format(rel) __salt__['pkg_resource.add_pkg'](ret, name, pkgver) __salt__['pkg_resource.sort_pkglist'](ret) __context__['pkg.list_pkgs'] = copy.deepcopy(ret) if not versions_as_list: __salt__['pkg_resource.stringify'](ret) return ret def refresh_db(): ''' Just run a ``zypper refresh``, return a dict:: {'': Bool} CLI Example: .. code-block:: bash salt '*' pkg.refresh_db ''' cmd = 'zypper refresh' ret = {} out = __salt__['cmd.run'](cmd).splitlines() for line in out: if not line: continue if line.strip().startswith('Repository'): key = line.split("'")[1].strip() if 'is up to date' in line: ret[key] = False elif line.strip().startswith('Building'): key = line.split("'")[1].strip() if 'done' in line: ret[key] = True return ret def install(name=None, refresh=False, pkgs=None, sources=None, **kwargs): ''' Install the passed package(s), add refresh=True to run '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 refresh Whether or not to refresh the package database before installing. 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. 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"}]' Returns a dict containing the new package names and versions:: {'': {'old': '', 'new': ''}} ''' if salt.utils.is_true(refresh): refresh_db() pkg_params, pkg_type = __salt__['pkg_resource.parse_targets'](name, pkgs, sources, **kwargs) if pkg_params is None or len(pkg_params) == 0: return {} version_num = kwargs.get('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 = [] problems = [] for param, version_num in pkg_params.iteritems(): if version_num is None: targets.append(param) else: match = re.match('^([<>])?(=)?([^<>=]+)$', version_num) if match: gt_lt, eq, verstr = match.groups() prefix = gt_lt or '' prefix += eq or '' # If no prefix characters were supplied, use '=' prefix = prefix or '=' targets.append('{0}{1}{2}'.format(param, prefix, verstr)) log.debug(targets) else: msg = 'Invalid version string "{0}" for package ' \ '"{1}"'.format(version_num, name) problems.append(msg) if problems: for problem in problems: log.error(problem) return {} else: targets = pkg_params old = list_pkgs() # Quotes needed around package targets because of the possibility of output # redirection characters "<" or ">" in zypper command. cmd = 'zypper -n install -l "{0}"'.format('" "'.join(targets)) stdout = __salt__['cmd.run_all'](cmd).get('stdout', '') downgrades = [] for line in stdout.splitlines(): match = re.match("^The selected package '([^']+)'.+has lower version", line) if match: downgrades.append(match.group(1)) if downgrades: cmd = 'zypper -n install -l --force {0}'.format(' '.join(downgrades)) __salt__['cmd.run_all'](cmd) __context__.pop('pkg.list_pkgs', None) new = list_pkgs() return __salt__['pkg_resource.find_changes'](old, new) def upgrade(refresh=True): ''' Run a full system upgrade, a zypper upgrade Return a dict containing the new package names and versions:: {'': {'old': '', 'new': ''}} CLI Example: .. code-block:: bash salt '*' pkg.upgrade ''' if salt.utils.is_true(refresh): refresh_db() old = list_pkgs() cmd = 'zypper -n up -l' __salt__['cmd.run_all'](cmd) __context__.pop('pkg.list_pkgs', None) new = list_pkgs() return __salt__['pkg_resource.find_changes'](old, new) def _uninstall(action='remove', name=None, pkgs=None): ''' remove and purge do identical things but with different zypper commands, this function performs the common logic. ''' purge_arg = '-u' if action == 'purge' else '' pkg_params = __salt__['pkg_resource.parse_targets'](name, pkgs)[0] old = list_pkgs() targets = [x for x in pkg_params if x in old] if not targets: return {} cmd = 'zypper -n remove {0} {1}'.format(purge_arg, ' '.join(targets)) __salt__['cmd.run_all'](cmd) __context__.pop('pkg.list_pkgs', None) new = list_pkgs() return __salt__['pkg_resource.find_changes'](old, new) def remove(name=None, pkgs=None, **kwargs): ''' Remove packages with ``zypper -n remove`` name The name of the package to be deleted. Multiple Package Options: pkgs A list of packages to delete. Must be passed as a python list. The ``name`` parameter will be ignored if this option is passed. .. versionadded:: 0.16.0 Returns a dict containing the changes. CLI Example: .. code-block:: bash salt '*' pkg.remove salt '*' pkg.remove ,, salt '*' pkg.remove pkgs='["foo", "bar"]' ''' return _uninstall(action='remove', name=name, pkgs=pkgs) def purge(name=None, pkgs=None, **kwargs): ''' Recursively remove a package and all dependencies which were installed with it, this will call a ``zypper -n remove -u`` name The name of the package to be deleted. Multiple Package Options: pkgs A list of packages to delete. Must be passed as a python list. The ``name`` parameter will be ignored if this option is passed. .. versionadded:: 0.16.0 Returns a dict containing the changes. CLI Example: .. code-block:: bash salt '*' pkg.purge salt '*' pkg.purge ,, salt '*' pkg.purge pkgs='["foo", "bar"]' ''' return _uninstall(action='purge', name=name, pkgs=pkgs) salt-0.17.5+ds.orig/salt/modules/cassandra.py0000644000175000017500000000744712263042153017162 0ustar joejoe# -*- coding: utf-8 -*- ''' Cassandra NoSQL Database Module :depends: - pycassa Cassandra Python adapter :configuration: The location of the 'nodetool' command, host, and thrift port needs to be specified via pillar:: cassandra.nodetool: /usr/local/bin/nodetool cassandra.host: localhost cassandra.thrift_port: 9160 ''' # Import python libs import logging log = logging.getLogger(__name__) # Import salt libs import salt.utils HAS_PYCASSA = False try: from pycassa.system_manager import SystemManager HAS_PYCASSA = True except ImportError: pass def __virtual__(): ''' Only load if pycassa is available and the system is configured ''' if not HAS_PYCASSA: return False if HAS_PYCASSA and salt.utils.which('nodetool'): return 'cassandra' return False def _nodetool(cmd): ''' Internal cassandra nodetool wrapper. Some functions are not available via pycassa so we must rely on nodetool. ''' nodetool = __salt__['config.option']('cassandra.nodetool') host = __salt__['config.option']('cassandra.host') return __salt__['cmd.run_stdout']('{0} -h {1} {2}'.format(nodetool, host, cmd)) def _sys_mgr(): ''' Return a pycassa system manager connection object ''' thrift_port = str(__salt__['config.option']('cassandra.THRIFT_PORT')) host = __salt__['config.option']('cassandra.host') return SystemManager('{0}:{1}'.format(host, thrift_port)) def compactionstats(): ''' Return compactionstats info CLI Example: .. code-block:: bash salt '*' cassandra.compactionstats ''' return _nodetool('compactionstats') def version(): ''' Return the cassandra version CLI Example: .. code-block:: bash salt '*' cassandra.version ''' return _nodetool('version') def netstats(): ''' Return netstats info CLI Example: .. code-block:: bash salt '*' cassandra.netstats ''' return _nodetool('netstats') def tpstats(): ''' Return tpstats info CLI Example: .. code-block:: bash salt '*' cassandra.tpstats ''' return _nodetool('tpstats') def info(): ''' Return cassandra node info CLI Example: .. code-block:: bash salt '*' cassandra.info ''' return _nodetool('info') def ring(): ''' Return cassandra ring info CLI Example: .. code-block:: bash salt '*' cassandra.ring ''' return _nodetool('ring') def keyspaces(): ''' Return existing keyspaces CLI Example: .. code-block:: bash salt '*' cassandra.keyspaces ''' sys = _sys_mgr() return sys.list_keyspaces() def column_families(keyspace=None): ''' Return existing column families for all keyspaces or just the provided one. CLI Example: .. code-block:: bash salt '*' cassandra.column_families salt '*' cassandra.column_families ''' sys = _sys_mgr() ksps = sys.list_keyspaces() if keyspace: if keyspace in ksps: return sys.get_keyspace_column_families(keyspace).keys() else: return None else: ret = {} for kspace in ksps: ret[kspace] = sys.get_keyspace_column_families(kspace).keys() return ret def column_family_definition(keyspace=None, column_family=None): ''' Return a dictionary of column family definitions for the given keyspace/column_family CLI Example: .. code-block:: bash salt '*' cassandra.column_family_definition ''' sys = _sys_mgr() try: return vars(sys.get_keyspace_column_families(keyspace)[column_family]) except Exception: log.debug('Invalid Keyspace/CF combination') return None salt-0.17.5+ds.orig/salt/modules/win_path.py0000644000175000017500000000663512264022040017024 0ustar joejoe# -*- coding: utf-8 -*- ''' Manage the Windows System PATH Note that not all Windows applications will rehash the PATH environment variable, Only the ones that listen to the WM_SETTINGCHANGE message http://support.microsoft.com/kb/104011 ''' # Python Libs import logging import re # Third party libs try: import win32gui import win32con HAS_WIN32 = True except ImportError: HAS_WIN32 = False # Import salt libs import salt.utils # Settings log = logging.getLogger(__name__) def __virtual__(): ''' Load only on Windows ''' if salt.utils.is_windows() and HAS_WIN32: return 'win_path' return False def _normalize_dir(string): ''' Normalize the directory to make comparison possible ''' return re.sub(r'\\$', '', string.lower()) def rehash(): ''' Send a WM_SETTINGCHANGE Broadcast to Windows to rehash the Environment variables ''' return win32gui.SendMessageTimeout(win32con.HWND_BROADCAST, win32con.WM_SETTINGCHANGE, 0, 'Environment', 0, 10000)[0] == 1 def get_path(): ''' Returns the system path ''' ret = __salt__['reg.read_key']('HKEY_LOCAL_MACHINE', 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment', 'PATH').split(';') # Trim ending backslash return map(_normalize_dir, ret) def exists(path): ''' Check if the directory is configured in the SYSTEM path Case-insensitive and ignores trailing backslash CLI Example: .. code-block:: bash salt '*' win_path.exists 'c:\\python27' salt '*' win_path.exists 'c:\\python27\\' salt '*' win_path.exists 'C:\\pyThon27' ''' path = _normalize_dir(path) sysPath = get_path() return path in sysPath def add(path, index=0): ''' Add the directory to the SYSTEM path in the index location CLI Example: .. code-block:: bash # Will add to the beginning of the path salt '*' win_path.add 'c:\\python27' 0 # Will add to the end of the path salt '*' win_path.add 'c:\\python27' index='-1' ''' currIndex = -1 sysPath = get_path() path = _normalize_dir(path) index = int(index) # validate index boundaries if index < 0: index = len(sysPath) + index + 1 if index > len(sysPath): index = len(sysPath) # Check if we are in the system path at the right location try: currIndex = sysPath.index(path) if currIndex != index: sysPath.pop(currIndex) else: return True except ValueError: pass # Add it to the Path sysPath.insert(index, path) regedit = __salt__['reg.set_key']( 'HKEY_LOCAL_MACHINE', 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment', 'PATH', ';'.join(sysPath), 'REG_EXPAND_SZ' ) # Broadcast WM_SETTINGCHANGE to Windows if regedit: return rehash() else: return False def remove(path): ''' Remove the directory from the SYSTEM path ''' path = _normalize_dir(path) sysPath = get_path() try: sysPath.remove(path) except ValueError: return True regedit = __salt__['reg.set_key']( 'HKEY_LOCAL_MACHINE', 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment', 'PATH', ';'.join(sysPath), 'REG_EXPAND_SZ' ) if regedit: return rehash() else: return False salt-0.17.5+ds.orig/salt/modules/saltcloudmod.py0000644000175000017500000000143012270576114017706 0ustar joejoe# -*- coding: utf-8 -*- ''' Control a salt cloud system ''' # Import python libs import json # Import salt libs import salt.utils HAS_CLOUD = False try: import saltcloud HAS_CLOUD = True except ImportError: pass def __virtual__(): ''' Only load if salt cloud is installed ''' if HAS_CLOUD: return 'saltcloud' return False def create(name, profile): ''' Create the named vm CLI Example: .. code-block:: bash salt saltcloud.create webserver rackspace_centos_512 ''' cmd = 'salt-cloud --out json -p {0} {1}'.format(profile, name) out = __salt__['cmd.run_stdout'](cmd) try: ret = json.loads(out, object_hook=salt.utils.decode_dict) except ValueError: ret = {} return ret salt-0.17.5+ds.orig/salt/modules/virt.py0000644000175000017500000011364212270576114016211 0ustar joejoe# -*- coding: utf-8 -*- ''' Work with virtual machines managed by libvirt :depends: libvirt Python module ''' # Special Thanks to Michael Dehann, many of the concepts, and a few structures # of his in the virt func module have been used # Import python libs import os import re import shutil import subprocess # Import third party libs import yaml try: import libvirt from xml.dom import minidom HAS_ALL_IMPORTS = True except ImportError: HAS_ALL_IMPORTS = False # Import salt libs import salt.utils from salt._compat import StringIO as _StringIO from salt.exceptions import CommandExecutionError VIRT_STATE_NAME_MAP = {0: 'running', 1: 'running', 2: 'running', 3: 'paused', 4: 'shutdown', 5: 'shutdown', 6: 'crashed'} def __virtual__(): if not HAS_ALL_IMPORTS: return False return 'virt' def __get_conn(): ''' Detects what type of dom this node is and attempts to connect to the correct hypervisor via libvirt. ''' # This has only been tested on kvm and xen, it needs to be expanded to # support all vm layers supported by libvirt def __esxi_uri(): ''' Connect to an ESXi host with a configuration like so: .. code-block:: yaml libvirt: hypervisor: esxi connection: esx01 The connection setting can either be an explicit libvirt URI, or a libvirt URI alias as in this example. No, it cannot be just a hostname. Example libvirt `/etc/libvirt/libvirt.conf`: .. code-block:: uri_aliases = [ "esx01=esx://10.1.1.101/?no_verify=1&auto_answer=1", "esx02=esx://10.1.1.102/?no_verify=1&auto_answer=1", ] Reference: - http://libvirt.org/drvesx.html#uriformat - http://libvirt.org/uri.html#URI_config ''' connection = __salt__['config.get']('libvirt:connection', 'esx') if connection.startswith('esx://'): return connection return '%s' % connection def __esxi_auth(): ''' We rely on that the credentials is provided to libvirt through it's built in mechanisms. Example libvirt `/etc/libvirt/auth.conf`: .. code-block:: [credentials-myvirt] username=user password=secret [auth-esx-10.1.1.101] credentials=myvirt [auth-esx-10.1.1.102] credentials=myvirt Reference: - http://libvirt.org/auth.html#Auth_client_config ''' return [[libvirt.VIR_CRED_EXTERNAL], lambda: 0, None] conn_func = { 'esxi': [libvirt.openAuth, [__esxi_uri(), __esxi_auth(), 0]], 'qemu': [libvirt.open, ['qemu:///system']], } hypervisor = __salt__['config.get']('libvirt:hypervisor', 'qemu') try: conn = conn_func[hypervisor][0](*conn_func[hypervisor][1]) except Exception: raise CommandExecutionError( 'Sorry, {0} failed to open a connection to the hypervisor ' 'software at {1}'.format( __grains__['fqdn'], conn_func[hypervisor][1][0] ) ) return conn def _get_dom(vm_): ''' Return a domain object for the named vm ''' conn = __get_conn() if vm_ not in list_vms(): raise CommandExecutionError('The specified vm is not present') return conn.lookupByName(vm_) def _libvirt_creds(): ''' Returns the user and group that the disk images should be owned by ''' g_cmd = 'grep ^\\s*group /etc/libvirt/qemu.conf' u_cmd = 'grep ^\\s*user /etc/libvirt/qemu.conf' try: group = subprocess.Popen(g_cmd, shell=True, stdout=subprocess.PIPE).communicate()[0].split('"')[1] except IndexError: group = 'root' try: user = subprocess.Popen(u_cmd, shell=True, stdout=subprocess.PIPE).communicate()[0].split('"')[1] except IndexError: user = 'root' return {'user': user, 'group': group} def _get_migrate_command(): ''' Returns the command shared by the different migration types ''' if __salt__['config.option']('virt.tunnel'): return ('virsh migrate --p2p --tunnelled --live --persistent ' '--undefinesource ') return 'virsh migrate --live --persistent --undefinesource ' def _get_target(target, ssh): proto = 'qemu' if ssh: proto += '+ssh' return ' {0}://{1}/{2}'.format(proto, target, 'system') def _prepare_serial_port_xml(serial_type='pty', telnet_port='', console=True, **kwargs_sink): ''' Prepares the serial and console sections of the VM xml serial_type: presently 'pty' or 'tcp'(telnet) telnet_port: When selecting tcp, which port to listen on console: Is this serial device the console or for some other purpose Returns string representing the serial and console devices suitable for insertion into the VM XML definition ''' import jinja2 templates = { 'pty': ''' {% if console %} {% endif %} ''', 'tcp': ''' {% if console %} {% endif %} ''' } dict_loader = jinja2.DictLoader(templates) env = jinja2.Environment(loader=dict_loader) template = env.get_template(serial_type) return template.render(serial_type=serial_type, telnet_port=telnet_port, console=console) def _gen_xml(name, cpu, mem, vda, nicp, hypervisor, **kwargs): ''' Generate the XML string to define a libvirt vm ''' mem = mem * 1024 # MB data = ''' %%NAME%% %%CPU%% %%MEM%% hvm %%BOOT%% %%NICS%% %%SERIAL%% ''' data = data.replace('%%HYPERVISOR%%', hypervisor) data = data.replace('%%NAME%%', name) data = data.replace('%%CPU%%', str(cpu)) data = data.replace('%%MEM%%', str(mem)) data = data.replace('%%VDA%%', vda) data = data.replace('%%DISKTYPE%%', _image_type(vda)) if 'serial_type' in kwargs: serial_section = _prepare_serial_port_xml(**kwargs) else: serial_section = '' data = data.replace('%%SERIAL%%', serial_section) boot_str = '' if 'boot_dev' in kwargs: for dev in kwargs['boot_dev']: boot_part = "" boot_part = boot_part.replace('%%DEV%%', dev) boot_str += boot_part else: boot_str = '''''' data = data.replace('%%BOOT%%', boot_str) nic_str = '' for dev, args in nicp.items(): nic_t = ''' \n''' if 'bridge' in args: nic_t = nic_t.replace('%%SOURCE%%', 'bridge=\'{0}\''.format(args['bridge'])) nic_t = nic_t.replace('%%TYPE%%', 'bridge') elif 'network' in args: nic_t = nic_t.replace('%%SOURCE%%', 'network=\'{0}\''.format(args['network'])) nic_t = nic_t.replace('%%TYPE%%', 'network') if 'model' in args: nic_t = nic_t.replace('%%MODEL%%', args['model']) dmac = '{0}_mac'.format(dev) if dmac in kwargs: nic_t = nic_t.replace('%%MAC%%', kwargs[dmac]) else: nic_t = nic_t.replace('%%MAC%%', salt.utils.gen_mac()) nic_str += nic_t data = data.replace('%%NICS%%', nic_str) return data def _image_type(vda): ''' Detect what driver needs to be used for the given image ''' out = __salt__['cmd.run']('qemu-img info {0}'.format(vda)) if 'file format: qcow2' in out: return 'qcow2' else: return 'raw' def _nic_profile(nic): ''' Gather the nic profile from the config or apply the default This is the ``default`` profile, which can be overridden in the configuration: .. code-block:: yaml virt: nic: default: eth0: bridge: br0 model: virtio ''' default = {'eth0': {'bridge': 'br0', 'model': 'virtio'}} return __salt__['config.option']('virt.nic', {}).get(nic, default) def init(name, cpu, mem, image, nic='default', hypervisor='kvm', start=True, **kwargs): ''' Initialize a new vm CLI Example: .. code-block:: bash salt 'hypervisor' virt.init vm_name 4 512 salt://path/to/image.raw ''' img_dir = os.path.join(__salt__['config.option']('virt.images'), name) img_dest = os.path.join( __salt__['config.option']('virt.images'), name, 'vda') sfn = __salt__['cp.cache_file'](image) if not os.path.isdir(img_dir): os.makedirs(img_dir) nicp = _nic_profile(nic) salt.utils.copyfile(sfn, img_dest) xml = _gen_xml(name, cpu, mem, img_dest, nicp, hypervisor, **kwargs) define_xml_str(xml) if kwargs.get('seed'): install = kwargs.get('install', True) __salt__['seed.apply'](img_dest, id_=name, config=kwargs.get('config'), install=install) elif kwargs.get('seed_cmd'): __salt__[kwargs['seed_cmd']](img_dest, name, kwargs.get('config')) if start: create(name) def list_vms(): ''' Return a list of virtual machine names on the minion CLI Example: .. code-block:: bash salt '*' virt.list_vms ''' vms = [] vms.extend(list_active_vms()) vms.extend(list_inactive_vms()) return vms def list_active_vms(): ''' Return a list of names for active virtual machine on the minion CLI Example: .. code-block:: bash salt '*' virt.list_active_vms ''' conn = __get_conn() vms = [] for id_ in conn.listDomainsID(): vms.append(conn.lookupByID(id_).name()) return vms def list_inactive_vms(): ''' Return a list of names for inactive virtual machine on the minion CLI Example: .. code-block:: bash salt '*' virt.list_inactive_vms ''' conn = __get_conn() vms = [] for id_ in conn.listDefinedDomains(): vms.append(id_) return vms def vm_info(vm_=None): ''' Return detailed information about the vms on this hyper in a list of dicts: .. code-block:: python [ 'your-vm': { 'cpu': , 'maxMem': , 'mem': , 'state': '', 'cputime' }, ... ] If you pass a VM name in as an argument then it will return info for just the named VM, otherwise it will return all VMs. CLI Example: .. code-block:: bash salt '*' virt.vm_info ''' def _info(vm_): dom = _get_dom(vm_) raw = dom.info() return {'cpu': raw[3], 'cputime': int(raw[4]), 'disks': get_disks(vm_), 'graphics': get_graphics(vm_), 'nics': get_nics(vm_), 'maxMem': int(raw[1]), 'mem': int(raw[2]), 'state': VIRT_STATE_NAME_MAP.get(raw[0], 'unknown')} info = {} if vm_: info[vm_] = _info(vm_) else: for vm_ in list_vms(): info[vm_] = _info(vm_) return info def vm_state(vm_=None): ''' Return list of all the vms and their state. If you pass a VM name in as an argument then it will return info for just the named VM, otherwise it will return all VMs. CLI Example: .. code-block:: bash salt '*' virt.vm_state ''' def _info(vm_): state = '' dom = _get_dom(vm_) raw = dom.info() state = VIRT_STATE_NAME_MAP.get(raw[0], 'unknown') return state info = {} if vm_: info[vm_] = _info(vm_) else: for vm_ in list_vms(): info[vm_] = _info(vm_) return info def node_info(): ''' Return a dict with information about this node CLI Example: .. code-block:: bash salt '*' virt.node_info ''' conn = __get_conn() raw = conn.getInfo() info = {'cpucores': raw[6], 'cpumhz': raw[3], 'cpumodel': str(raw[0]), 'cpus': raw[2], 'cputhreads': raw[7], 'numanodes': raw[4], 'phymemory': raw[1], 'sockets': raw[5]} return info def get_nics(vm_): ''' Return info about the network interfaces of a named vm CLI Example: .. code-block:: bash salt '*' virt.get_nics ''' nics = {} doc = minidom.parse(_StringIO(get_xml(vm_))) for node in doc.getElementsByTagName('devices'): i_nodes = node.getElementsByTagName('interface') for i_node in i_nodes: nic = {} nic['type'] = i_node.getAttribute('type') for v_node in i_node.getElementsByTagName('*'): if v_node.tagName == 'mac': nic['mac'] = v_node.getAttribute('address') if v_node.tagName == 'model': nic['model'] = v_node.getAttribute('type') if v_node.tagName == 'target': nic['target'] = v_node.getAttribute('dev') # driver, source, and match can all have optional attributes if re.match('(driver|source|address)', v_node.tagName): temp = {} for key in v_node.attributes.keys(): temp[key] = v_node.getAttribute(key) nic[str(v_node.tagName)] = temp # virtualport needs to be handled separately, to pick up the # type attribute of the virtualport itself if v_node.tagName == 'virtualport': temp = {} temp['type'] = v_node.getAttribute('type') for key in v_node.attributes.keys(): temp[key] = v_node.getAttribute(key) nic['virtualport'] = temp if 'mac' not in nic: continue nics[nic['mac']] = nic return nics def get_macs(vm_): ''' Return a list off MAC addresses from the named vm CLI Example: .. code-block:: bash salt '*' virt.get_macs ''' macs = [] doc = minidom.parse(_StringIO(get_xml(vm_))) for node in doc.getElementsByTagName('devices'): i_nodes = node.getElementsByTagName('interface') for i_node in i_nodes: for v_node in i_node.getElementsByTagName('mac'): macs.append(v_node.getAttribute('address')) return macs def get_graphics(vm_): ''' Returns the information on vnc for a given vm CLI Example: .. code-block:: bash salt '*' virt.get_graphics ''' out = {'autoport': 'None', 'keymap': 'None', 'listen': 'None', 'port': 'None', 'type': 'vnc'} xml = get_xml(vm_) ssock = _StringIO(xml) doc = minidom.parse(ssock) for node in doc.getElementsByTagName('domain'): g_nodes = node.getElementsByTagName('graphics') for g_node in g_nodes: for key in g_node.attributes.keys(): out[key] = g_node.getAttribute(key) return out def get_disks(vm_): ''' Return the disks of a named vm CLI Example: .. code-block:: bash salt '*' virt.get_disks ''' disks = {} doc = minidom.parse(_StringIO(get_xml(vm_))) for elem in doc.getElementsByTagName('disk'): sources = elem.getElementsByTagName('source') targets = elem.getElementsByTagName('target') if len(sources) > 0: source = sources[0] else: continue if len(targets) > 0: target = targets[0] else: continue if target.hasAttribute('dev'): qemu_target = '' if source.hasAttribute('file'): qemu_target = source.getAttribute('file') elif source.hasAttribute('dev'): qemu_target = source.getAttribute('dev') elif source.hasAttribute('protocol') and \ source.hasAttribute('name'): # For rbd network qemu_target = '%s:%s' % ( source.getAttribute('protocol'), source.getAttribute('name')) if qemu_target: disks[target.getAttribute('dev')] = { 'file': qemu_target} for dev in disks: try: hypervisor = __salt__['config.get']('libvirt:hypervisor', 'kvm') if hypervisor not in ['qemu', 'kvm']: break output = [] qemu_output = subprocess.Popen(['qemu-img', 'info', disks[dev]['file']], shell=False, stdout=subprocess.PIPE).communicate()[0] snapshots = False columns = None lines = qemu_output.strip().split('\n') for line in lines: if line.startswith('Snapshot list:'): snapshots = True continue # If this is a copy-on-write image, then the backing file # represents the base image # # backing file: base.qcow2 (actual path: /var/shared/base.qcow2) elif line.startswith('backing file'): matches = re.match(r'.*\(actual path: (.*?)\)', line) if matches: output.append('backing file: {0}'.format(matches.group(1))) continue elif snapshots: if line.startswith('ID'): # Do not parse table headers line = line.replace('VM SIZE', 'VMSIZE') line = line.replace('VM CLOCK', 'TIME VMCLOCK') columns = re.split(r'\s+', line) columns = [c.lower() for c in columns] output.append('snapshots:') continue fields = re.split(r'\s+', line) for i, field in enumerate(fields): sep = ' ' if i == 0: sep = '-' output.append( '{0} {1}: "{2}"'.format( sep, columns[i], field ) ) continue output.append(line) output = '\n'.join(output) disks[dev].update(yaml.safe_load(output)) except TypeError: disks[dev].update(yaml.safe_load('image: Does not exist')) return disks def setmem(vm_, memory, config=False): ''' Changes the amount of memory allocated to VM. The VM must be shutdown for this to work. memory is to be specified in MB If config is True then we ask libvirt to modify the config as well CLI Example: .. code-block:: bash salt '*' virt.setmem myvm 768 ''' if vm_state(vm_) != 'shutdown': return False dom = _get_dom(vm_) # libvirt has a funny bitwise system for the flags in that the flag # to affect the "current" setting is 0, which means that to set the # current setting we have to call it a second time with just 0 set flags = libvirt.VIR_DOMAIN_MEM_MAXIMUM if config: flags = flags | libvirt.VIR_DOMAIN_AFFECT_CONFIG ret1 = dom.setMemoryFlags(memory * 1024, flags) ret2 = dom.setMemoryFlags(memory * 1024, libvirt.VIR_DOMAIN_AFFECT_CURRENT) # return True if both calls succeeded return ret1 == ret2 == 0 def setvcpus(vm_, vcpus, config=False): ''' Changes the amount of vcpus allocated to VM. The VM must be shutdown for this to work. vcpus is an int representing the number to be assigned If config is True then we ask libvirt to modify the config as well CLI Example: .. code-block:: bash salt '*' virt.setvcpus myvm 2 ''' if vm_state(vm_) != 'shutdown': return False dom = _get_dom(vm_) # see notes in setmem flags = libvirt.VIR_DOMAIN_VCPU_MAXIMUM if config: flags = flags | libvirt.VIR_DOMAIN_AFFECT_CONFIG ret1 = dom.setVcpusFlags(vcpus, flags) ret2 = dom.setVcpusFlags(vcpus, libvirt.VIR_DOMAIN_AFFECT_CURRENT) return ret1 == ret2 == 0 def freemem(): ''' Return an int representing the amount of memory that has not been given to virtual machines on this node CLI Example: .. code-block:: bash salt '*' virt.freemem ''' conn = __get_conn() mem = conn.getInfo()[1] # Take off just enough to sustain the hypervisor mem -= 256 for vm_ in list_vms(): dom = _get_dom(vm_) if dom.ID() > 0: mem -= dom.info()[2] / 1024 return mem def freecpu(): ''' Return an int representing the number of unallocated cpus on this hypervisor CLI Example: .. code-block:: bash salt '*' virt.freecpu ''' conn = __get_conn() cpus = conn.getInfo()[2] for vm_ in list_vms(): dom = _get_dom(vm_) if dom.ID() > 0: cpus -= dom.info()[3] return cpus def full_info(): ''' Return the node_info, vm_info and freemem CLI Example: .. code-block:: bash salt '*' virt.full_info ''' return {'freecpu': freecpu(), 'freemem': freemem(), 'node_info': node_info(), 'vm_info': vm_info()} def get_xml(vm_): ''' Returns the XML for a given vm CLI Example: .. code-block:: bash salt '*' virt.get_xml ''' dom = _get_dom(vm_) return dom.XMLDesc(0) def shutdown(vm_): ''' Send a soft shutdown signal to the named vm CLI Example: .. code-block:: bash salt '*' virt.shutdown ''' dom = _get_dom(vm_) return dom.shutdown() == 0 def pause(vm_): ''' Pause the named vm CLI Example: .. code-block:: bash salt '*' virt.pause ''' dom = _get_dom(vm_) return dom.suspend() == 0 def resume(vm_): ''' Resume the named vm CLI Example: .. code-block:: bash salt '*' virt.resume ''' dom = _get_dom(vm_) return dom.resume() == 0 def create(vm_): ''' Start a defined domain CLI Example: .. code-block:: bash salt '*' virt.create ''' dom = _get_dom(vm_) return dom.create() == 0 def start(vm_): ''' Alias for the obscurely named 'create' function CLI Example: .. code-block:: bash salt '*' virt.start ''' return create(vm_) def stop(vm_): ''' Alias for the obscurely named 'destroy' function CLI Example: .. code-block:: bash salt '*' virt.stop ''' return destroy(vm_) def reboot(vm_): ''' Reboot a domain via ACPI request CLI Example: .. code-block:: bash salt '*' virt.reboot ''' dom = _get_dom(vm_) # reboot has a few modes of operation, passing 0 in means the # hypervisor will pick the best method for rebooting return dom.reboot(0) == 0 def reset(vm_): ''' Reset a VM by emulating the reset button on a physical machine CLI Example: .. code-block:: bash salt '*' virt.reset ''' dom = _get_dom(vm_) # reset takes a flag, like reboot, but it is not yet used # so we just pass in 0 # see: http://libvirt.org/html/libvirt-libvirt.html#virDomainReset return dom.reset(0) == 0 def ctrl_alt_del(vm_): ''' Sends CTRL+ALT+DEL to a VM CLI Example: .. code-block:: bash salt '*' virt.ctrl_alt_del ''' dom = _get_dom(vm_) return dom.sendKey(0, 0, [29, 56, 111], 3, 0) == 0 def create_xml_str(xml): ''' Start a domain based on the XML passed to the function CLI Example: .. code-block:: bash salt '*' virt.create_xml_str ''' conn = __get_conn() return conn.createXML(xml, 0) is not None def create_xml_path(path): ''' Start a domain based on the XML-file path passed to the function CLI Example: .. code-block:: bash salt '*' virt.create_xml_path ''' if not os.path.isfile(path): return False return create_xml_str(salt.utils.fopen(path, 'r').read()) def define_xml_str(xml): ''' Define a domain based on the XML passed to the function CLI Example: .. code-block:: bash salt '*' virt.define_xml_str ''' conn = __get_conn() return conn.defineXML(xml) is not None def define_xml_path(path): ''' Define a domain based on the XML-file path passed to the function CLI Example: .. code-block:: bash salt '*' virt.define_xml_path ''' if not os.path.isfile(path): return False return define_xml_str(salt.utils.fopen(path, 'r').read()) def define_vol_xml_str(xml): ''' Define a volume based on the XML passed to the function CLI Example: .. code-block:: bash salt '*' virt.define_vol_xml_str ''' poolname = __salt__['config.get']('libvirt:storagepool', 'default') conn = __get_conn() pool = conn.storagePoolLookupByName(str(poolname)) return pool.createXML(xml, 0) is not None def define_vol_xml_path(path): ''' Define a volume based on the XML-file path passed to the function CLI Example: .. code-block:: bash salt '*' virt.define_vol_xml_path ''' if not os.path.isfile(path): return False return define_vol_xml_str(salt.utils.fopen(path, 'r').read()) def migrate_non_shared(vm_, target, ssh=False): ''' Attempt to execute non-shared storage "all" migration CLI Example: .. code-block:: bash salt '*' virt.migrate_non_shared ''' cmd = _get_migrate_command() + ' --copy-storage-all ' + vm_\ + _get_target(target, ssh) return subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).communicate()[0] def migrate_non_shared_inc(vm_, target, ssh=False): ''' Attempt to execute non-shared storage "all" migration CLI Example: .. code-block:: bash salt '*' virt.migrate_non_shared_inc ''' cmd = _get_migrate_command() + ' --copy-storage-inc ' + vm_\ + _get_target(target, ssh) return subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).communicate()[0] def migrate(vm_, target, ssh=False): ''' Shared storage migration CLI Example: .. code-block:: bash salt '*' virt.migrate ''' cmd = _get_migrate_command() + ' ' + vm_\ + _get_target(target, ssh) return subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).communicate()[0] def seed_non_shared_migrate(disks, force=False): ''' Non shared migration requires that the disks be present on the migration destination, pass the disks information via this function, to the migration destination before executing the migration. CLI Example: .. code-block:: bash salt '*' virt.seed_non_shared_migrate ''' for _, data in disks.items(): fn_ = data['file'] form = data['file format'] size = data['virtual size'].split()[1][1:] if os.path.isfile(fn_) and not force: # the target exists, check to see if it is compatible pre = yaml.safe_load(subprocess.Popen('qemu-img info arch', shell=True, stdout=subprocess.PIPE).communicate()[0]) if pre['file format'] != data['file format']\ and pre['virtual size'] != data['virtual size']: return False if not os.path.isdir(os.path.dirname(fn_)): os.makedirs(os.path.dirname(fn_)) if os.path.isfile(fn_): os.remove(fn_) cmd = 'qemu-img create -f ' + form + ' ' + fn_ + ' ' + size subprocess.call(cmd, shell=True) creds = _libvirt_creds() cmd = 'chown ' + creds['user'] + ':' + creds['group'] + ' ' + fn_ subprocess.call(cmd, shell=True) return True def set_autostart(vm_, state='on'): ''' Set the autostart flag on a VM so that the VM will start with the host system on reboot. CLI Example: .. code-block:: bash salt "*" virt.set_autostart ''' dom = _get_dom(vm_) if state == 'on': return dom.setAutostart(1) == 0 elif state == 'off': return dom.setAutostart(0) == 0 else: # return False if state is set to something other then on or off return False def destroy(vm_): ''' Hard power down the virtual machine, this is equivalent to pulling the power CLI Example: .. code-block:: bash salt '*' virt.destroy ''' dom = _get_dom(vm_) return dom.destroy() == 0 def undefine(vm_): ''' Remove a defined vm, this does not purge the virtual machine image, and this only works if the vm is powered down CLI Example: .. code-block:: bash salt '*' virt.undefine ''' dom = _get_dom(vm_) return dom.undefine() == 0 def purge(vm_, dirs=False): ''' Recursively destroy and delete a virtual machine, pass True for dir's to also delete the directories containing the virtual machine disk images - USE WITH EXTREME CAUTION! CLI Example: .. code-block:: bash salt '*' virt.purge ''' disks = get_disks(vm_) try: if not destroy(vm_): return False except libvirt.libvirtError: # This is thrown if the machine is already shut down pass directories = set() for disk in disks: os.remove(disks[disk]['file']) directories.add(os.path.dirname(disks[disk]['file'])) if dirs: for dir_ in directories: shutil.rmtree(dir_) undefine(vm_) return True def virt_type(): ''' Returns the virtual machine type as a string CLI Example: .. code-block:: bash salt '*' virt.virt_type ''' return __grains__['virtual'] def is_kvm_hyper(): ''' Returns a bool whether or not this node is a KVM hypervisor CLI Example: .. code-block:: bash salt '*' virt.is_kvm_hyper ''' if __grains__['virtual'] != 'physical': return False try: if 'kvm_' not in salt.utils.fopen('/proc/modules').read(): return False except IOError: # No /proc/modules? Are we on Windows? Or Solaris? return False return 'libvirtd' in __salt__['cmd.run'](__grains__['ps']) def is_xen_hyper(): ''' Returns a bool whether or not this node is a XEN hypervisor CLI Example: .. code-block:: bash salt '*' virt.is_xen_hyper ''' try: if __grains__['virtual_subtype'] != 'Xen Dom0': return False except KeyError: # virtual_subtype isn't set everywhere. return False try: if 'xen_' not in salt.utils.fopen('/proc/modules').read(): return False except IOError: # No /proc/modules? Are we on Windows? Or Solaris? return False return 'libvirtd' in __salt__['cmd.run'](__grains__['ps']) def is_hyper(): ''' Returns a bool whether or not this node is a hypervisor of any kind CLI Example: .. code-block:: bash salt '*' virt.is_hyper ''' return is_xen_hyper() or is_kvm_hyper() def vm_cputime(vm_=None): ''' Return cputime used by the vms on this hyper in a list of dicts: .. code-block:: python [ 'your-vm': { 'cputime' 'cputime_percent' }, ... ] If you pass a VM name in as an argument then it will return info for just the named VM, otherwise it will return all VMs. CLI Example: .. code-block:: bash salt '*' virt.vm_cputime ''' host_cpus = __get_conn().getInfo()[2] def _info(vm_): dom = _get_dom(vm_) raw = dom.info() vcpus = int(raw[3]) cputime = int(raw[4]) cputime_percent = 0 if cputime: # Divide by vcpus to always return a number between 0 and 100 cputime_percent = (1.0e-7 * cputime / host_cpus) / vcpus return { 'cputime': int(raw[4]), 'cputime_percent': int('%.0f' % cputime_percent) } info = {} if vm_: info[vm_] = _info(vm_) else: for vm_ in list_vms(): info[vm_] = _info(vm_) return info def vm_netstats(vm_=None): ''' Return combined network counters used by the vms on this hyper in a list of dicts: .. code-block:: python [ 'your-vm': { 'rx_bytes' : 0, 'rx_packets' : 0, 'rx_errs' : 0, 'rx_drop' : 0, 'tx_bytes' : 0, 'tx_packets' : 0, 'tx_errs' : 0, 'tx_drop' : 0 }, ... ] If you pass a VM name in as an argument then it will return info for just the named VM, otherwise it will return all VMs. CLI Example: .. code-block:: bash salt '*' virt.vm_netstats ''' def _info(vm_): dom = _get_dom(vm_) nics = get_nics(vm_) ret = { 'rx_bytes': 0, 'rx_packets': 0, 'rx_errs': 0, 'rx_drop': 0, 'tx_bytes': 0, 'tx_packets': 0, 'tx_errs': 0, 'tx_drop': 0 } for attrs in nics.values(): if 'target' in attrs: dev = attrs['target'] stats = dom.interfaceStats(dev) ret['rx_bytes'] += stats[0] ret['rx_packets'] += stats[1] ret['rx_errs'] += stats[2] ret['rx_drop'] += stats[3] ret['tx_bytes'] += stats[4] ret['tx_packets'] += stats[5] ret['tx_errs'] += stats[6] ret['tx_drop'] += stats[7] return ret info = {} if vm_: info[vm_] = _info(vm_) else: for vm_ in list_vms(): info[vm_] = _info(vm_) return info def vm_diskstats(vm_=None): ''' Return disk usage counters used by the vms on this hyper in a list of dicts: .. code-block:: python [ 'your-vm': { 'rd_req' : 0, 'rd_bytes' : 0, 'wr_req' : 0, 'wr_bytes' : 0, 'errs' : 0 }, ... ] If you pass a VM name in as an argument then it will return info for just the named VM, otherwise it will return all VMs. CLI Example: .. code-block:: bash salt '*' virt.vm_blockstats ''' def get_disk_devs(vm_): doc = minidom.parse(_StringIO(get_xml(vm_))) disks = [] for elem in doc.getElementsByTagName('disk'): targets = elem.getElementsByTagName('target') target = targets[0] disks.append(target.getAttribute('dev')) return disks def _info(vm_): dom = _get_dom(vm_) # Do not use get_disks, since it uses qemu-img and is very slow # and unsuitable for any sort of real time statistics disks = get_disk_devs(vm_) ret = { 'rd_req': 0, 'rd_bytes': 0, 'wr_req': 0, 'wr_bytes': 0, 'errs': 0 } for disk in disks: stats = dom.blockStats(disk) ret['rd_req'] += stats[0] ret['rd_bytes'] += stats[1] ret['wr_req'] += stats[2] ret['wr_bytes'] += stats[3] ret['errs'] += stats[4] return ret info = {} if vm_: info[vm_] = _info(vm_) else: # Can not run function blockStats on inactive VMs for vm_ in list_active_vms(): info[vm_] = _info(vm_) return info salt-0.17.5+ds.orig/salt/modules/sqlite3.py0000644000175000017500000000476712263042153016611 0ustar joejoe# -*- coding: utf-8 -*- ''' Support for SQLite3 ''' from __future__ import absolute_import # Import python libs try: import sqlite3 HAS_SQLITE3 = True except ImportError: HAS_SQLITE3 = False # pylint: disable=C0103 def __virtual__(): if not HAS_SQLITE3: return False return 'sqlite3' def _connect(db=None): if db is None: return False con = sqlite3.connect(db) cur = con.cursor() return cur def version(): ''' Return version of pysqlite CLI Example: .. code-block:: bash salt '*' sqlite3.version ''' return sqlite3.version def sqlite_version(): ''' Return version of sqlite CLI Example: .. code-block:: bash salt '*' sqlite3.sqlite_version ''' return sqlite3.sqlite_version def modify(db=None, sql=None): ''' Issue an SQL query to sqlite3 (with no return data), usually used to modify the database in some way (insert, delete, create, etc) CLI Example: .. code-block:: bash salt '*' sqlite3.modify /root/test.db 'CREATE TABLE test(id INT, testdata TEXT);' ''' cur = _connect(db) if not cur: return False cur.execute(sql) return True def fetch(db=None, sql=None): ''' Retrieve data from an sqlite3 db (returns all rows, be careful!) CLI Example: .. code-block:: bash salt '*' sqlite3.fetch /root/test.db 'SELECT * FROM test;' ''' cur = _connect(db) if not cur: return False cur.execute(sql) rows = cur.fetchall() return rows def tables(db=None): ''' Show all tables in the database CLI Example: .. code-block:: bash salt '*' sqlite3.tables /root/test.db ''' cur = _connect(db) if not cur: return False cur.execute( "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;" ) rows = cur.fetchall() return rows def indices(db=None): ''' Show all indices in the database CLI Example: .. code-block:: bash salt '*' sqlite3.indices /root/test.db ''' cur = _connect(db) if not cur: return False cur.execute( "SELECT name FROM sqlite_master WHERE type='index' ORDER BY name;" ) rows = cur.fetchall() return rows def indexes(db=None): ''' Show all indices in the database, for people with poor spelling skills CLI Example: .. code-block:: bash salt '*' sqlite3.indexes /root/test.db ''' return indices(db) salt-0.17.5+ds.orig/salt/modules/supervisord.py0000644000175000017500000001524612270576114017613 0ustar joejoe# -*- coding: utf-8 -*- ''' Provide the service module for system supervisord or supervisord in a virtualenv ''' # Import python libs import os # Import salt libs from salt.exceptions import CommandNotFoundError def _get_supervisorctl_bin(bin_env): ''' Return supervisorctl command to call, either from a virtualenv, an argument passed in, or from the global modules options ''' cmd = 'supervisorctl' if not bin_env: which_result = __salt__['cmd.which_bin']([cmd]) if which_result is None: raise CommandNotFoundError('Could not find a `{0}` binary'.format(cmd)) return which_result # try to get binary from env if os.path.isdir(bin_env): cmd_bin = os.path.join(bin_env, 'bin', cmd) if os.path.isfile(cmd_bin): return cmd_bin raise CommandNotFoundError('Could not find a `{0}` binary'.format(cmd)) return bin_env def _ctl_cmd(cmd, name, conf_file, bin_env): ret = [_get_supervisorctl_bin(bin_env)] if conf_file is not None: ret += ['-c', conf_file] ret.append(cmd) if name: ret.append(name) return ' ' .join(ret) def _get_return(ret): if ret['retcode'] == 0: return ret['stdout'] else: return '' def start(name='all', user=None, conf_file=None, bin_env=None): ''' Start the named service. user user to run supervisorctl as conf_file path to supervisorctl config file bin_env path to supervisorctl bin or path to virtualenv with supervisor installed CLI Example: .. code-block:: bash salt '*' supervisord.start ''' ret = __salt__['cmd.run_all']( _ctl_cmd('start', name, conf_file, bin_env), runas=user ) return _get_return(ret) def restart(name='all', user=None, conf_file=None, bin_env=None): ''' Restart the named service. user user to run supervisorctl as conf_file path to supervisorctl config file bin_env path to supervisorctl bin or path to virtualenv with supervisor installed CLI Example: .. code-block:: bash salt '*' supervisord.restart ''' ret = __salt__['cmd.run_all']( _ctl_cmd('restart', name, conf_file, bin_env), runas=user ) return _get_return(ret) def stop(name='all', user=None, conf_file=None, bin_env=None): ''' Stop the named service. user user to run supervisorctl as conf_file path to supervisorctl config file bin_env path to supervisorctl bin or path to virtualenv with supervisor installed CLI Example: .. code-block:: bash salt '*' supervisord.stop ''' ret = __salt__['cmd.run_all']( _ctl_cmd('stop', name, conf_file, bin_env), runas=user ) return _get_return(ret) def add(name, user=None, conf_file=None, bin_env=None): ''' Activates any updates in config for process/group. user user to run supervisorctl as conf_file path to supervisorctl config file bin_env path to supervisorctl bin or path to virtualenv with supervisor installed CLI Example: .. code-block:: bash salt '*' supervisord.add ''' ret = __salt__['cmd.run_all']( _ctl_cmd('add', name, conf_file, bin_env), runas=user ) return _get_return(ret) def remove(name, user=None, conf_file=None, bin_env=None): ''' Removes process/group from active config user user to run supervisorctl as conf_file path to supervisorctl config file bin_env path to supervisorctl bin or path to virtualenv with supervisor installed CLI Example: .. code-block:: bash salt '*' supervisord.remove ''' ret = __salt__['cmd.run_all']( _ctl_cmd('remove', name, conf_file, bin_env), runas=user ) return _get_return(ret) def reread(user=None, conf_file=None, bin_env=None): ''' Reload the daemon's configuration files user user to run supervisorctl as conf_file path to supervisorctl config file bin_env path to supervisorctl bin or path to virtualenv with supervisor installed CLI Example: .. code-block:: bash salt '*' supervisord.reread ''' ret = __salt__['cmd.run_all']( _ctl_cmd('reread', None, conf_file, bin_env), runas=user ) return _get_return(ret) def update(user=None, conf_file=None, bin_env=None): ''' Reload config and add/remove as necessary user user to run supervisorctl as conf_file path to supervisorctl config file bin_env path to supervisorctl bin or path to virtualenv with supervisor installed CLI Example: .. code-block:: bash salt '*' supervisord.update ''' ret = __salt__['cmd.run_all']( _ctl_cmd('update', None, conf_file, bin_env), runas=user ) return _get_return(ret) def status(name=None, user=None, conf_file=None, bin_env=None): ''' List programs and its state user user to run supervisorctl as conf_file path to supervisorctl config file bin_env path to supervisorctl bin or path to virtualenv with supervisor installed CLI Example: .. code-block:: bash salt '*' supervisord.status ''' all_process = {} for line in status_raw(name, user, conf_file, bin_env).splitlines(): if len(line.split()) > 2: process, state, reason = line.split(None, 2) else: process, state, reason = line.split() + [''] all_process[process] = {'state': state, 'reason': reason} return all_process def status_raw(name=None, user=None, conf_file=None, bin_env=None): ''' Display the raw output of status user user to run supervisorctl as conf_file path to supervisorctl config file bin_env path to supervisorctl bin or path to virtualenv with supervisor installed CLI Example: .. code-block:: bash salt '*' supervisord.status_raw ''' ret = __salt__['cmd.run_all']( _ctl_cmd('status', name, conf_file, bin_env), runas=user ) return _get_return(ret) def custom(command, user=None, conf_file=None, bin_env=None): ''' Run any custom supervisord command user user to run supervisorctl as conf_file path to supervisorctl config file bin_env path to supervisorctl bin or path to virtualenv with supervisor installed CLI Example: .. code-block:: bash salt '*' supervisord.custom "mstop '*gunicorn*'" ''' ret = __salt__['cmd.run_all'](_ctl_cmd(command, None, conf_file, bin_env), runas=user) return _get_return(ret) salt-0.17.5+ds.orig/salt/modules/yumpkg5.py0000644000175000017500000004701212270576120016620 0ustar joejoe# -*- coding: utf-8 -*- ''' Support for YUM ''' # Import python libs import collections import copy import logging import re # Import salt libs import salt.utils from salt.exceptions import SaltInvocationError from salt.utils import namespaced_function as _namespaced_function from salt.modules.yumpkg import (mod_repo, _parse_repo_file, list_repos, get_repo, expand_repo_def, del_repo) # Import libs required by functions imported from yumpkg # DO NOT REMOVE THESE, ON PAIN OF DEATH import os log = logging.getLogger(__name__) # This is imported in salt.modules.pkg_resource._parse_pkg_meta. Don't change # it without considering its impact there. __QUERYFORMAT = '%{NAME}_|-%{VERSION}_|-%{RELEASE}_|-%{ARCH}' # From rpmUtils.arch.getArchList() (not necessarily available on RHEL/CentOS 5) __ALL_ARCHES = ('ia32e', 'x86_64', 'athlon', 'i686', 'i586', 'i486', 'i386', 'noarch') __SUFFIX_NOT_NEEDED = ('x86_64', 'noarch') # Define the module's virtual name __virtualname__ = 'pkg' def __virtual__(): ''' Confine this module to yum based systems ''' # Work only on RHEL/Fedora based distros with python 2.5 and below try: os_grain = __grains__['os'] os_family = __grains__['os_family'] os_major_version = int(__grains__['osrelease'].split('.')[0]) except Exception: return False valid = False # Fedora <= 10 need to use this module if os_grain == 'Fedora' and os_major_version < 11: valid = True # XCP == 1.x uses a CentOS 5 base elif os_grain == 'XCP': if os_major_version == 1: valid = True # XenServer 6 and earlier uses a CentOS 5 base elif os_grain == 'XenServer': if os_major_version <= 6: valid = True elif os_grain == 'Amazon': valid = True else: # RHEL <= 5 and all variants need to use this module if os_family == 'RedHat' and os_major_version <= 5: valid = True if valid: global mod_repo, _parse_repo_file, list_repos, get_repo global expand_repo_def, del_repo mod_repo = _namespaced_function(mod_repo, globals()) _parse_repo_file = _namespaced_function(_parse_repo_file, globals()) list_repos = _namespaced_function(list_repos, globals()) get_repo = _namespaced_function(get_repo, globals()) expand_repo_def = _namespaced_function(expand_repo_def, globals()) del_repo = _namespaced_function(del_repo, globals()) return 'pkg' return False # This is imported in salt.modules.pkg_resource._parse_pkg_meta. Don't change # it without considering its impact there. def _parse_pkginfo(line): ''' A small helper to parse package information; returns a namedtuple ''' # Need to reimport `collections` as this function is re-namespaced into # other modules import collections pkginfo = collections.namedtuple('PkgInfo', ('name', 'shortname', 'version', 'arch')) try: name, pkgver, rel, arch = line.split('_|-') # Handle unpack errors (should never happen with the queryformat we are # using, but can't hurt to be careful). except ValueError: return None shortname = name # Support 32-bit packages on x86_64 systems if __grains__.get('cpuarch') in __SUFFIX_NOT_NEEDED \ and arch not in __SUFFIX_NOT_NEEDED: name += '.{0}'.format(arch) if rel: pkgver += '-{0}'.format(rel) return pkginfo(name, shortname, pkgver, arch) def _repoquery(repoquery_args): ''' Runs a repoquery command and returns a list of namedtuples ''' ret = [] cmd = 'repoquery {0}'.format(repoquery_args) output = __salt__['cmd.run_all'](cmd).get('stdout', '').splitlines() for line in output: pkginfo = _parse_pkginfo(line) if pkginfo is not None: ret.append(pkginfo) return ret def _get_repo_options(**kwargs): ''' Returns a string of '--enablerepo' and '--disablerepo' options to be used in the yum command, based on the kwargs. ''' # Get repo options from the kwargs fromrepo = kwargs.get('fromrepo', '') repo = kwargs.get('repo', '') disablerepo = kwargs.get('disablerepo', '') enablerepo = kwargs.get('enablerepo', '') # Support old 'repo' argument if repo and not fromrepo: fromrepo = repo repo_arg = '' if fromrepo: log.info('Restricting to repo {0!r}'.format(fromrepo)) repo_arg = ('--disablerepo={0!r} --enablerepo={1!r}' .format('*', fromrepo)) else: repo_arg = '' if disablerepo: log.info('Disabling repo {0!r}'.format(disablerepo)) repo_arg += '--disablerepo={0!r} '.format(disablerepo) if enablerepo: log.info('Enabling repo {0!r}'.format(enablerepo)) repo_arg += '--enablerepo={0!r} '.format(enablerepo) return repo_arg def _pkg_arch(name): ''' Returns a 2-tuple of the name and arch parts of the passed string. Note that packages that are for the system architecture should not have the architecture specified in the passed string. ''' # TODO: Fix __grains__ availability in provider overrides if not any(name.endswith('.{0}'.format(x)) for x in __ALL_ARCHES): return name, __grains__['cpuarch'] try: pkgname, pkgarch = name.rsplit('.', 1) except ValueError: return name, __grains__['cpuarch'] else: return pkgname, pkgarch def latest_version(*names, **kwargs): ''' Return the latest version of the named package available for upgrade or installation. If more than one package name is specified, a dict of name/version pairs is returned. If the latest version of a given package is already installed, an empty string will be returned for that package. A specific repo can be requested using the ``fromrepo`` keyword argument. CLI Example: .. code-block:: bash salt '*' pkg.latest_version salt '*' pkg.latest_version fromrepo=epel-testing salt '*' pkg.latest_version ... ''' refresh = salt.utils.is_true(kwargs.pop('refresh', True)) # FIXME: do stricter argument checking that somehow takes # _get_repo_options() into account if len(names) == 0: return '' ret = {} namearch_map = {} # Initialize the return dict with empty strings, and populate the namearch # dict for name in names: ret[name] = '' pkgname, pkgarch = _pkg_arch(name) namearch_map.setdefault(name, {})['name'] = pkgname namearch_map[name]['arch'] = pkgarch # Refresh before looking for the latest version available if refresh: refresh_db() # Get updates for specified package(s) repo_arg = _get_repo_options(**kwargs) updates = _repoquery( '{0} --pkgnarrow=available --queryformat {1!r} ' '{2}'.format( repo_arg, __QUERYFORMAT, ' '.join([namearch_map[x]['name'] for x in names]) ) ) for name in names: for pkg in (x for x in updates if x.shortname == namearch_map[name]['name']): if (all(x in __SUFFIX_NOT_NEEDED for x in (namearch_map[name]['arch'], pkg.arch)) or namearch_map[name]['arch'] == pkg.arch): ret[name] = pkg.version # Return a string if only one package name passed if len(names) == 1: return ret[names[0]] return ret # available_version is being deprecated available_version = latest_version def upgrade_available(name): ''' Check whether or not an upgrade is available for a given package CLI Example: .. code-block:: bash salt '*' pkg.upgrade_available ''' return latest_version(name) != '' def version(*names, **kwargs): ''' Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. CLI Example: .. code-block:: bash salt '*' pkg.version salt '*' pkg.version ... ''' return __salt__['pkg_resource.version'](*names, **kwargs) def list_pkgs(versions_as_list=False, **kwargs): ''' List the packages currently installed in a dict:: {'': ''} CLI Example: .. code-block:: bash salt '*' pkg.list_pkgs ''' versions_as_list = salt.utils.is_true(versions_as_list) # 'removed' not yet implemented or not applicable if salt.utils.is_true(kwargs.get('removed')): return {} if 'pkg.list_pkgs' in __context__: if versions_as_list: return __context__['pkg.list_pkgs'] else: ret = copy.deepcopy(__context__['pkg.list_pkgs']) __salt__['pkg_resource.stringify'](ret) return ret ret = {} cmd = 'rpm -qa --queryformat "{0}\n"'.format(__QUERYFORMAT) for line in __salt__['cmd.run'](cmd).splitlines(): pkginfo = _parse_pkginfo(line) if pkginfo is None: continue __salt__['pkg_resource.add_pkg'](ret, pkginfo.name, pkginfo.version) __salt__['pkg_resource.sort_pkglist'](ret) __context__['pkg.list_pkgs'] = copy.deepcopy(ret) if not versions_as_list: __salt__['pkg_resource.stringify'](ret) return ret def list_upgrades(refresh=True, **kwargs): ''' Check whether or not an upgrade is available for all packages CLI Example: .. code-block:: bash salt '*' pkg.list_upgrades ''' if salt.utils.is_true(refresh): refresh_db() repo_arg = _get_repo_options(**kwargs) updates = _repoquery('{0} --all --pkgnarrow=updates --queryformat ' '"{1}"'.format(repo_arg, __QUERYFORMAT)) return dict([(x.name, x.version) for x in updates]) def check_db(*names, **kwargs): ''' .. versionadded:: 0.17.0 Returns a dict containing the following information for each specified package: 1. A key ``found``, which will be a boolean value denoting if a match was found in the package database. 2. If ``found`` is ``False``, then a second key called ``suggestions`` will be present, which will contain a list of possible matches. The ``fromrepo``, ``enablerepo``, and ``disablerepo`` arguments are supported, as used in pkg states. CLI Examples: .. code-block:: bash salt '*' pkg.check_db salt '*' pkg.check_db fromrepo=epel-testing ''' repo_arg = _get_repo_options(**kwargs) deplist_base = 'yum {0} deplist --quiet'.format(repo_arg) + ' {0!r}' repoquery_base = ('{0} -a --quiet --whatprovides --queryformat ' '{1!r}'.format(repo_arg, __QUERYFORMAT)) ret = {} for name in names: ret.setdefault(name, {})['found'] = bool( __salt__['cmd.run'](deplist_base.format(name)) ) if ret[name]['found'] is False: repoquery_cmd = repoquery_base + ' {0!r}'.format(name) provides = set([x.name for x in _repoquery(repoquery_cmd)]) if provides: for pkg in provides: ret[name]['suggestions'] = list(provides) else: ret[name]['suggestions'] = [] return ret def refresh_db(): ''' Since yum refreshes the database automatically, this runs a yum clean, so that the next yum operation will have a clean database Returns: - ``True``: Database updated successfully - ``False``: Problem updating database - ``None``: Database already up-to-date CLI Example: .. code-block:: bash salt '*' pkg.refresh_db ''' retcodes = { 100: True, 0: None, 1: False, } cmd = 'yum -q check-update' ret = __salt__['cmd.retcode'](cmd) return retcodes.get(ret, False) def install(name=None, refresh=False, fromrepo=None, skip_verify=False, pkgs=None, sources=None, **kwargs): ''' Install the passed package(s), add refresh=True to clean the yum database 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. 32-bit packages can be installed on 64-bit systems by appending the architecture designation (``.i686``, ``.i586``, etc.) to the end of the package name. CLI Example: .. code-block:: bash salt '*' pkg.install refresh Whether or not to update the yum database before executing. skip_verify Skip the GPG verification check (e.g., ``--nogpgcheck``) version Install a specific version of the package, e.g. 1.2.3-4.el5. Ignored if "pkgs" or "sources" is passed. Repository Options: fromrepo Specify a package repository (or repositories) from which to install. (e.g., ``yum --disablerepo='*' --enablerepo='somerepo'``) enablerepo (ignored if ``fromrepo`` is specified) Specify a disabled package repository (or repositories) to enable. (e.g., ``yum --enablerepo='somerepo'``) disablerepo (ignored if ``fromrepo`` is specified) Specify an enabled package repository (or repositories) to disable. (e.g., ``yum --disablerepo='somerepo'``) 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. CLI Examples: .. code-block:: bash salt '*' pkg.install pkgs='["foo", "bar"]' salt '*' pkg.install pkgs='["foo", {"bar": "1.2.3-4.el5"}]' 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"}]' Returns a dict containing the new package names and versions:: {'': {'old': '', 'new': ''}} ''' if salt.utils.is_true(refresh): refresh_db() pkg_params, pkg_type = __salt__['pkg_resource.parse_targets'](name, pkgs, sources, **kwargs) if pkg_params is None or len(pkg_params) == 0: return {} version_num = kwargs.get('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') repo_arg = _get_repo_options(fromrepo=fromrepo, **kwargs) old = list_pkgs() downgrade = [] if pkg_type == 'repository': targets = [] for pkgname, version_num in pkg_params.iteritems(): if version_num is None: targets.append(pkgname) else: cver = old.get(pkgname, '') if __grains__.get('cpuarch', '') == 'x86_64': try: arch = re.search(r'(\.i\d86)$', pkgname).group(1) except AttributeError: arch = '' else: # Remove arch from pkgname pkgname = pkgname[:-len(arch)] else: arch = '' pkgstr = '"{0}-{1}{2}"'.format(pkgname, version_num, arch) if not cver or salt.utils.compare_versions(ver1=version_num, oper='>=', ver2=cver): targets.append(pkgstr) else: downgrade.append(pkgstr) else: targets = pkg_params if targets: cmd = 'yum -y {repo} {gpgcheck} install {pkg}'.format( repo=repo_arg, gpgcheck='--nogpgcheck' if skip_verify else '', pkg=' '.join(targets), ) __salt__['cmd.run_all'](cmd) if downgrade: cmd = 'yum -y {repo} {gpgcheck} downgrade {pkg}'.format( repo=repo_arg, gpgcheck='--nogpgcheck' if skip_verify else '', pkg=' '.join(downgrade), ) __salt__['cmd.run_all'](cmd) __context__.pop('pkg.list_pkgs', None) new = list_pkgs() return __salt__['pkg_resource.find_changes'](old, new) def upgrade(refresh=True): ''' Run a full system upgrade, a yum upgrade Return a dict containing the new package names and versions:: {'': {'old': '', 'new': ''}} CLI Example: .. code-block:: bash salt '*' pkg.upgrade ''' if salt.utils.is_true(refresh): refresh_db() old = list_pkgs() cmd = 'yum -q -y upgrade' __salt__['cmd.run_all'](cmd) __context__.pop('pkg.list_pkgs', None) new = list_pkgs() return __salt__['pkg_resource.find_changes'](old, new) def remove(name=None, pkgs=None, **kwargs): ''' Remove packages with ``yum -q -y remove``. name The name of the package to be deleted. Multiple Package Options: pkgs A list of packages to delete. Must be passed as a python list. The ``name`` parameter will be ignored if this option is passed. .. versionadded:: 0.16.0 Returns a dict containing the changes. CLI Example: .. code-block:: bash salt '*' pkg.remove salt '*' pkg.remove ,, salt '*' pkg.remove pkgs='["foo", "bar"]' ''' pkg_params = __salt__['pkg_resource.parse_targets'](name, pkgs)[0] old = list_pkgs() targets = [x for x in pkg_params if x in old] if not targets: return {} cmd = 'yum -q -y remove "{0}"'.format('" "'.join(targets)) __salt__['cmd.run_all'](cmd) __context__.pop('pkg.list_pkgs', None) new = list_pkgs() return __salt__['pkg_resource.find_changes'](old, new) def purge(name=None, pkgs=None, **kwargs): ''' Package purges are not supported by yum, this function is identical to ``remove()``. name The name of the package to be deleted. Multiple Package Options: pkgs A list of packages to delete. Must be passed as a python list. The ``name`` parameter will be ignored if this option is passed. .. versionadded:: 0.16.0 Returns a dict containing the changes. CLI Example: .. code-block:: bash salt '*' pkg.purge salt '*' pkg.purge ,, salt '*' pkg.purge pkgs='["foo", "bar"]' ''' return remove(name=name, pkgs=pkgs) salt-0.17.5+ds.orig/salt/modules/win_firewall.py0000644000175000017500000000215012270576114017676 0ustar joejoe# -*- coding: utf-8 -*- ''' Module for configuring Windows Firewall ''' # Import python libs import re # Import salt libs import salt.utils def __virtual__(): ''' Only works on Windows systems ''' if salt.utils.is_windows(): return 'firewall' return False def get_config(): ''' Get the status of all the firewall profiles CLI Example: .. code-block:: bash salt '*' firewall.get_config ''' profiles = {} curr = None cmd = 'netsh advfirewall show allprofiles' for line in __salt__['cmd.run'](cmd).splitlines(): if not curr: tmp = re.search('(.*) Profile Settings:', line) if tmp: curr = tmp.group(1) elif line.startswith('State'): profiles[curr] = line.split()[1] == 'ON' curr = None return profiles def disable(): ''' Disable all the firewall profiles CLI Example: .. code-block:: bash salt '*' firewall.disable ''' return __salt__['cmd.run']( 'netsh advfirewall set allprofiles state off' ) == 'Ok.' salt-0.17.5+ds.orig/salt/modules/virtualenv_mod.py0000644000175000017500000002637612270576114020272 0ustar joejoe# -*- coding: utf-8 -*- ''' Create virtualenv environments ''' # Import python libs import glob import shutil import logging import os import os.path # Import salt libs import salt.utils import salt.exceptions log = logging.getLogger(__name__) __opts__ = { 'venv_bin': 'virtualenv' } __pillar__ = {} def __virtual__(): return 'virtualenv' def create(path, venv_bin=None, no_site_packages=None, system_site_packages=False, distribute=False, clear=False, python=None, extra_search_dir=None, never_download=None, prompt=None, pip=False, symlinks=None, upgrade=None, runas=None): ''' Create a virtualenv path The path to create the virtualenv venv_bin : None (default 'virtualenv') The name (and optionally path) of the virtualenv command. This can also be set globally in the minion config file as ``virtualenv.venv_bin``. no_site_packages : None Passthrough argument given to virtualenv if True. Deprecated since ``salt>=0.17.0``. Use ``system_site_packages=False`` instead. system_site_packages : False Passthrough argument given to virtualenv or pyvenv distribute : False Passthrough argument given to virtualenv pip : False Install pip after creating a virtual environment, implies distribute=True clear : False Passthrough argument given to virtualenv or pyvenv python : None (default) Passthrough argument given to virtualenv extra_search_dir : None (default) Passthrough argument given to virtualenv never_download : None (default) Passthrough argument given to virtualenv if True prompt : None (default) Passthrough argument given to virtualenv if not None symlinks : None Passthrough argument given to pyvenv if True upgrade : None Passthrough argument given to pyvenv if True runas : None Set ownership for the virtualenv CLI Example: .. code-block:: bash salt '*' virtualenv.create /path/to/new/virtualenv ''' if venv_bin is None: venv_bin = __opts__.get('venv_bin') or __pillar__.get('venv_bin') # raise CommandNotFoundError if venv_bin is missing salt.utils.check_or_die(venv_bin) if no_site_packages is not None: # Show a deprecation warning salt.utils.warn_until( (0, 19), '\'no_site_packages\' has been deprecated. Please start using ' '\'system_site_packages=False\' which means exactly the same ' 'as \'no_site_packages=True\'' ) if no_site_packages is True and system_site_packages is True: raise salt.exceptions.CommandExecutionError( '\'no_site_packages\' and \'system_site_packages\' are mutually ' 'exclusive options. Please use only one, and prefer ' '\'system_site_packages\' since \'no_site_packages\' has been ' 'deprecated.' ) cmd = [venv_bin] if 'pyvenv' not in venv_bin: # ----- Stop the user if pyvenv only options are used ---------------> # If any of the following values are not None, it means that the user # is actually passing a True or False value. Stop Him! if upgrade is not None: raise salt.exceptions.CommandExecutionError( 'The `upgrade`(`--upgrade`) option is not supported ' 'by {0!r}'.format(venv_bin) ) elif symlinks is not None: raise salt.exceptions.CommandExecutionError( 'The `symlinks`(`--symlinks`) option is not supported ' 'by {0!r}'.format(venv_bin) ) # <---- Stop the user if pyvenv only options are used ---------------- # Virtualenv package try: import virtualenv version = getattr(virtualenv, '__version__', virtualenv.virtualenv_version) virtualenv_version_info = tuple( [int(i) for i in version.split('rc')[0].split('.')] ) except ImportError: # Unable to import?? Let's parse the version from the console version_cmd = '{0} --version'.format(venv_bin) ret = __salt__['cmd.run_all'](version_cmd, runas=runas) if ret['retcode'] > 0 or not ret['stdout'].strip(): raise salt.exceptions.CommandExecutionError( 'Unable to get the virtualenv version output using {0!r}. ' 'Returned data: {1!r}'.format(version_cmd, ret) ) virtualenv_version_info = tuple( [int(i) for i in ret['stdout'].strip().split('rc')[0].split('.')] ) if no_site_packages is True: cmd.append('--no-site-packages') if distribute: if virtualenv_version_info >= (1, 10): log.info( 'The virtualenv \'--distribute\' option has been ' 'deprecated in virtualenv(>=1.10), as such, the ' '\'distribute\' option to `virtualenv.create()` has ' 'also been deprecated and it\'s not necessary anymore.' ) else: cmd.append('--distribute') if python is not None and python.strip() != '': cmd.append('--python={0}'.format(python)) if extra_search_dir is not None: if isinstance(extra_search_dir, basestring) and \ extra_search_dir.strip() != '': extra_search_dir = [ e.strip() for e in extra_search_dir.split(',') ] for entry in extra_search_dir: cmd.append('--extra-search-dir={0}'.format(entry)) if never_download is True: if virtualenv_version_info >= (1, 10): log.info( 'The virtualenv \'--never-download\' option has been ' 'deprecated in virtualenv(>=1.10), as such, the ' '\'never_download\' option to `virtualenv.create()` has ' 'also been deprecated and it\'s not necessary anymore.' ) else: cmd.append('--never-download') if prompt is not None and prompt.strip() != '': cmd.append('--prompt={0!r}'.format(prompt)) else: # venv module from the Python >= 3.3 standard library # ----- Stop the user if virtualenv only options are being used -----> # If any of the following values are not None, it means that the user # is actually passing a True or False value. Stop Him! if no_site_packages is not None: raise salt.exceptions.CommandExecutionError( 'The `no_site_packages`(`--no-site-packages`) option is not ' 'supported by {0!r}'.format(venv_bin) ) elif python is not None and python.strip() != '': raise salt.exceptions.CommandExecutionError( 'The `python`(`--python`) option is not supported ' 'by {0!r}'.format(venv_bin) ) elif extra_search_dir is not None and extra_search_dir.strip() != '': raise salt.exceptions.CommandExecutionError( 'The `extra_search_dir`(`--extra-search-dir`) option is not ' 'supported by {0!r}'.format(venv_bin) ) elif never_download is not None: raise salt.exceptions.CommandExecutionError( 'The `never_download`(`--never-download`) option is not ' 'supported by {0!r}'.format(venv_bin) ) elif prompt is not None and prompt.strip() != '': raise salt.exceptions.CommandExecutionError( 'The `prompt`(`--prompt`) option is not supported ' 'by {0!r}'.format(venv_bin) ) # <---- Stop the user if virtualenv only options are being used ------ if upgrade is True: cmd.append('--upgrade') if symlinks is True: cmd.append('--symlinks') # Common options to virtualenv and pyvenv if clear is True: cmd.append('--clear') if system_site_packages is True: cmd.append('--system-site-packages') # Finally the virtualenv path cmd.append(path) # Let's create the virtualenv ret = __salt__['cmd.run_all'](' '.join(cmd), runas=runas) if ret['retcode'] > 0: # Something went wrong. Let's bail out now! return ret # Check if distribute and pip are already installed if salt.utils.is_windows(): venv_python = os.path.join(path, 'Scripts', 'python.exe') venv_pip = os.path.join(path, 'Scripts', 'pip.exe') venv_setuptools = os.path.join(path, 'Scripts', 'easy_install.exe') else: venv_python = os.path.join(path, 'bin', 'python') venv_pip = os.path.join(path, 'bin', 'pip') venv_setuptools = os.path.join(path, 'bin', 'easy_install') # Install setuptools if (pip or distribute) and not os.path.exists(venv_setuptools): _install_script( 'https://bitbucket.org/pypa/setuptools/raw/default/ez_setup.py', path, venv_python, runas ) # clear up the distribute archive which gets downloaded for fpath in glob.glob(os.path.join(path, 'distribute-*.tar.gz*')): os.unlink(fpath) if ret['retcode'] > 0: # Something went wrong. Let's bail out now! return ret # Install pip if pip and not os.path.exists(venv_pip): _ret = _install_script( 'https://raw.github.com/pypa/pip/master/contrib/get-pip.py', path, venv_python, runas ) # Let's update the return dictionary with the details from the pip # installation ret.update( retcode=_ret['retcode'], stdout='{0}\n{1}'.format(ret['stdout'], _ret['stdout']).strip(), stderr='{0}\n{1}'.format(ret['stderr'], _ret['stderr']).strip(), ) return ret def get_site_packages(venv): ''' Returns the path to the site-packages directory inside a virtualenv CLI Example: .. code-block:: bash salt '*' virtualenv.get_site_packages /path/to/my/venv ''' bin_path = os.path.join(venv, 'bin/python') if not os.path.exists(bin_path): raise salt.exceptions.CommandExecutionError( "Path does not appear to be a virtualenv: '{0}'".format(bin_path)) return __salt__['cmd.exec_code'](bin_path, 'from distutils import sysconfig; print sysconfig.get_python_lib()') def _install_script(source, cwd, python, runas): env = 'base' if not salt.utils.is_windows(): tmppath = salt.utils.mkstemp(dir=cwd) else: tmppath = __salt__['cp.cache_file'](source, env) if not salt.utils.is_windows(): fn_ = __salt__['cp.cache_file'](source, env) shutil.copyfile(fn_, tmppath) os.chmod(tmppath, 320) os.chown(tmppath, __salt__['file.user_to_uid'](runas), -1) try: return __salt__['cmd.run_all']( '{0} {1}'.format(python, tmppath), runas=runas, cwd=cwd, env={'VIRTUAL_ENV': cwd} ) finally: os.remove(tmppath) salt-0.17.5+ds.orig/salt/modules/powerpath.py0000644000175000017500000000436312263042153017226 0ustar joejoe# -*- coding: utf-8 -*- ''' powerpath support. Assumes RedHat ''' # Import python libs import os import re POLICY_MAP_DICT = { 'Adaptive': 'ad', 'CLAROpt': 'co', 'LeastBlocks': 'lb', 'LeastIos': 'li', 'REquest': 're', 'RoundRobin': 'rr', 'StreamIo': 'si', 'SymmOpt': 'so', } POLICY_RE = re.compile('.*policy=([^;]+)') def has_powerpath(): if os.path.exists('/sbin/emcpreg'): return True return False def __virtual__(): ''' Provide this only on Linux systems until proven to work elsewhere. ''' try: kernel_grain = __grains__['kernel'] except Exception: return False if not has_powerpath(): return False if kernel_grain == 'Linux': return 'powerpath' return False def list_licenses(): ''' returns a list of applied powerpath license keys ''' KEY_PATTERN = re.compile('Key (.*)') keys = [] out = __salt__['cmd.run']('/sbin/emcpreg -list') for line in out.splitlines(): match = KEY_PATTERN.match(line) if not match: continue keys.append({'key': match.group(1)}) return keys def add_license(key): ''' Add a license ''' result = { 'result': False, 'retcode': -1, 'output': '' } if not has_powerpath(): result['output'] = 'PowerPath is not installed' return result cmd = '/sbin/emcpreg -add {0}'.format(key) ret = __salt__['cmd.run_all'](cmd) result['retcode'] = ret['retcode'] if ret['retcode'] != 0: result['output'] = ret['stderr'] else: result['output'] = ret['stdout'] result['result'] = True return result def remove_license(key): ''' Remove a license ''' result = { 'result': False, 'retcode': -1, 'output': '' } if not has_powerpath(): result['output'] = 'PowerPath is not installed' return result cmd = '/sbin/emcpreg -remove {0}'.format(key) ret = __salt__['cmd.run_all'](cmd) result['retcode'] = ret['retcode'] if ret['retcode'] != 0: result['output'] = ret['stderr'] else: result['output'] = ret['stdout'] result['result'] = True return result salt-0.17.5+ds.orig/salt/modules/archive.py0000644000175000017500000001646312270576114016651 0ustar joejoe# -*- coding: utf-8 -*- ''' A module to wrap archive calls ''' # Import salt libs import salt._compat from salt.utils import which as _which, which_bin as _which_bin from salt.exceptions import SaltInvocationError import salt.utils.decorators as decorators # TODO: Check that the passed arguments are correct # Don't shadow built-in's. __func_alias__ = { 'zip_': 'zip' } def __virtual__(): commands = ('tar', 'gzip', 'gunzip', 'zip', 'unzip', 'rar', 'unrar') # If none of the above commands are in $PATH this module is a no-go if not any(_which(cmd) for cmd in commands): return False return 'archive' @decorators.which('tar') def tar(options, tarfile, sources=None, dest=None, cwd=None, template=None): ''' .. note:: This function has changed for version 0.17.0. In prior versions, the ``cwd`` and ``template`` arguments must be specified, with the source directories/files coming as a space-separated list at the end of the command. Beginning with 0.17.0, ``sources`` must be a comma-separated list, and the ``cwd`` and ``template`` arguments are optional. Uses the tar command to pack, unpack, etc tar files options: Options to pass to the ``tar`` binary. tarfile: The tar filename to pack/unpack. sources: Comma delimited list of files to **pack** into the tarfile. dest: The destination directory to **unpack** the tarfile to. cwd: The directory in which the tar command should be executed. template: Template engine name to render the command arguments before execution. CLI Example: .. code-block:: bash salt '*' archive.tar cjvf /tmp/tarfile.tar.bz2 /tmp/file_1,/tmp/file_2 The template arg can be set to ``jinja`` or another supported template engine to render the command arguments before execution. For example: .. code-block:: bash salt '*' archive.tar template=jinja cjvf /tmp/salt.tar.bz2 {{grains.saltpath}} To unpack a tarfile, for example: ..code-block:: bash salt '*' archive.tar foo.tar xf dest=/target/directory ''' if sources is not None and dest is not None: raise SaltInvocationError( 'The \'sources\' and \'dest\' arguments are mutually exclusive' ) if isinstance(sources, salt._compat.string_types): sources = [s.strip() for s in sources.split(',')] cmd = 'tar -{0} {1}'.format(options, tarfile) if sources: cmd += ' {0}'.format(' '.join(sources)) elif dest: cmd += ' -C {0}'.format(dest) return __salt__['cmd.run'](cmd, cwd=cwd, template=template).splitlines() @decorators.which('gzip') def gzip(sourcefile, template=None): ''' Uses the gzip command to create gzip files CLI Example to create ``/tmp/sourcefile.txt.gz``: .. code-block:: bash salt '*' archive.gzip /tmp/sourcefile.txt The template arg can be set to 'jinja' or another supported template engine to render the command arguments before execution. CLI Example: .. code-block:: bash salt '*' archive.gzip template=jinja /tmp/{{grains.id}}.txt ''' cmd = 'gzip {0}'.format(sourcefile) return __salt__['cmd.run'](cmd, template=template).splitlines() @decorators.which('gunzip') def gunzip(gzipfile, template=None): ''' Uses the gunzip command to unpack gzip files CLI Example to create ``/tmp/sourcefile.txt``: .. code-block:: bash salt '*' archive.gunzip /tmp/sourcefile.txt.gz The template arg can be set to 'jinja' or another supported template engine to render the command arguments before execution. CLI Example: .. code-block:: bash salt '*' archive.gunzip template=jinja /tmp/{{grains.id}}.txt.gz ''' cmd = 'gunzip {0}'.format(gzipfile) return __salt__['cmd.run'](cmd, template=template).splitlines() @decorators.which('zip') def zip_(zipfile, sources, template=None): ''' Uses the zip command to create zip files CLI Example: .. code-block:: bash salt '*' archive.zip /tmp/zipfile.zip /tmp/sourcefile1,/tmp/sourcefile2 The template arg can be set to 'jinja' or another supported template engine to render the command arguments before execution. For example: .. code-block:: bash salt '*' archive.zip template=jinja /tmp/zipfile.zip /tmp/sourcefile1,/tmp/{{grains.id}}.txt ''' if isinstance(sources, salt._compat.string_types): sources = [s.strip() for s in sources.split(',')] cmd = 'zip {0} {1}'.format(zipfile, ' '.join(sources)) return __salt__['cmd.run'](cmd, template=template).splitlines() @decorators.which('unzip') def unzip(zipfile, dest, excludes=None, template=None): ''' Uses the unzip command to unpack zip files CLI Example: .. code-block:: bash salt '*' archive.unzip /tmp/zipfile.zip /home/strongbad/ excludes=file_1,file_2 The template arg can be set to 'jinja' or another supported template engine to render the command arguments before execution. For example: .. code-block:: bash salt '*' archive.unzip template=jinja /tmp/zipfile.zip /tmp/{{grains.id}}/ excludes=file_1,file_2 ''' if isinstance(excludes, salt._compat.string_types): excludes = [entry.strip() for entry in excludes.split(',')] cmd = 'unzip {0} -d {1}'.format(zipfile, dest) if excludes is not None: cmd += ' -x {0}'.format(' '.join(excludes)) return __salt__['cmd.run'](cmd, template=template).splitlines() @decorators.which('rar') def rar(rarfile, sources, template=None): ''' Uses the rar command to create rar files Uses rar for Linux from http://www.rarlab.com/ CLI Example: .. code-block:: bash salt '*' archive.rar /tmp/rarfile.rar /tmp/sourcefile1,/tmp/sourcefile2 The template arg can be set to 'jinja' or another supported template engine to render the command arguments before execution. For example: .. code-block:: bash salt '*' archive.rar template=jinja /tmp/rarfile.rar /tmp/sourcefile1,/tmp/{{grains.id}}.txt ''' if isinstance(sources, salt._compat.string_types): sources = [s.strip() for s in sources.split(',')] cmd = 'rar a -idp {0} {1}'.format(rarfile, ' '.join(sources)) return __salt__['cmd.run'](cmd, template=template).splitlines() @decorators.which_bin(('unrar', 'rar')) def unrar(rarfile, dest, excludes=None, template=None): ''' Uses the unrar command to unpack rar files Uses rar for Linux from http://www.rarlab.com/ CLI Example: .. code-block:: bash salt '*' archive.unrar /tmp/rarfile.rar /home/strongbad/ excludes=file_1,file_2 The template arg can be set to 'jinja' or another supported template engine to render the command arguments before execution. For example: .. code-block:: bash salt '*' archive.unrar template=jinja /tmp/rarfile.rar /tmp/{{grains.id}}/ excludes=file_1,file_2 ''' if isinstance(excludes, salt._compat.string_types): excludes = [entry.strip() for entry in excludes.split(',')] cmd = [_which_bin(('unrar', 'rar')), 'x', '-idp', rarfile] if excludes is not None: for exclude in excludes: cmd.extend(['-x', exclude]) cmd.append(dest) return __salt__['cmd.run'](' '.join(cmd), template=template).splitlines() salt-0.17.5+ds.orig/salt/modules/poudriere.py0000644000175000017500000001516512263042153017215 0ustar joejoe# -*- coding: utf-8 -*- ''' Support for poudriere ''' # Import python libs import os import logging # Import salt libs import salt.utils log = logging.getLogger(__name__) def __virtual__(): ''' Module load on freebsd only and if poudriere installed ''' if __grains__['os'] == 'FreeBSD' and salt.utils.which('poudriere'): return 'poudriere' else: return False def _config_file(): ''' Return the config file location to use ''' return __salt__['config.option']('poudriere.config') def _config_dir(): ''' Return the configuration directory to use ''' return __salt__['config.option']('poudriere.config_dir') def _check_config_exists(config_file=None): ''' Verify the config file is present ''' if config_file is None: config_file = _config_file() if not os.path.isfile(config_file): return False return True def is_jail(name): ''' Return True if jail exists False if not CLI Example: .. code-block:: bash salt '*' poudriere.is_jail ''' jails = list_jails() for jail in jails: if jail.split()[0] == name: return True return False def make_pkgng_aware(jname): ''' Make jail ``jname`` pkgng aware CLI Example: .. code-block:: bash salt '*' poudriere.make_pkgng_aware ''' ret = {'changes': {}} cdir = _config_dir() # ensure cdir is there if not os.path.isdir(cdir): os.makedirs(cdir) if os.path.isdir(cdir): ret['changes'] = 'Created poudriere make file dir {0}'.format(cdir) else: return 'Could not create or find required directory {0}'.format( cdir) # Added args to file cmd = 'echo "WITH_PKGNG=yes" > {0}-make.conf'.format( os.path.join(cdir, jname)) __salt__['cmd.run'](cmd) if os.path.isfile(os.path.join(cdir, jname) + '-make.conf'): ret['changes'] = 'Created {0}'.format( os.path.join(cdir, '{0}-make.conf'.format(jname)) ) return ret else: return 'Looks like file {0} could not be created'.format( os.path.join(cdir, jname + '-make.conf') ) def parse_config(config_file=None): ''' Returns a dict of poudriere main configuration definitions CLI Example: .. code-block:: bash salt '*' poudriere.parse_config ''' if config_file is None: config_file = _config_file() ret = {} if _check_config_exists(config_file): with salt.utils.fopen(config_file) as ifile: for line in ifile: key, val = line.split('=') ret[key] = val return ret return 'Could not find {0} on file system'.format(config_file) def version(): ''' Return poudriere version CLI Example: .. code-block:: bash salt '*' poudriere.version ''' cmd = "poudriere version" return __salt__['cmd.run'](cmd) def list_jails(): ''' Return a list of current jails managed by poudriere CLI Example: .. code-block:: bash salt '*' poudriere.list_jails ''' _check_config_exists() cmd = 'poudriere jails -l' res = __salt__['cmd.run'](cmd) return res.splitlines() def list_ports(): ''' Return a list of current port trees managed by poudriere CLI Example: .. code-block:: bash salt '*' poudriere.list_ports ''' _check_config_exists() cmd = 'poudriere ports -l' res = __salt__['cmd.run'](cmd).splitlines() return res def create_jail(name, arch, version="9.0-RELEASE"): ''' Creates a new poudriere jail if one does not exist *NOTE* creating a new jail will take some time the master is not hanging CLI Example: .. code-block:: bash salt '*' poudriere.create_jail 90amd64 amd64 ''' # Config file must be on system to create a poudriere jail _check_config_exists() # Check if the jail is there if is_jail(name): return '{0} already exists'.format(name) cmd = 'poudriere jails -c -j {0} -v {1} -a {2}'.format(name, version, arch) __salt__['cmd.run'](cmd) # Make jail pkgng aware make_pkgng_aware(name) # Make sure the jail was created if is_jail(name): return 'Created jail {0}'.format(name) return 'Issue creating jail {0}'.format(name) def delete_jail(name): ''' Deletes poudriere jail with `name` CLI Example: .. code-block:: bash salt '*' poudriere.delete_jail 90amd64 ''' if is_jail(name): cmd = 'poudriere jail -d -j {0}'.format(name) __salt__['cmd.run'](cmd) # Make sure jail is gone if is_jail(name): return 'Looks like there was an issue deleteing jail \ {0}'.format(name) else: # Could not find jail. return 'Looks like jail {0} has not been created'.format(name) # clean up pkgng make info in config dir make_file = os.path.join(_config_dir(), '{0}-make.conf'.format(name)) if os.path.isfile(make_file): try: os.remove(make_file) except (IOError, OSError): return ('Deleted jail "{0}" but was unable to remove jail make ' 'file').format(name) cmd = 'rm -f {0}'.format(make_file) __salt__['cmd.run'](cmd) return 'Deleted jail {0}'.format(name) def create_ports_tree(): ''' Not working need to run portfetch non interactive ''' _check_config_exists() cmd = 'poudriere ports -c' ret = __salt__['cmd.run'](cmd) return ret def bulk_build(jail, pkg_file, keep=False): ''' Run bulk build on poudriere server. Return number of pkg builds, failures, and errors, on error dump to CLI CLI Example: .. code-block:: bash salt -N buildbox_group poudriere.bulk_build 90amd64 /root/pkg_list ''' # make sure `pkg file` and jail is on file system if not os.path.isfile(pkg_file): return 'Could not find file {0} on filesystem'.format(pkg_file) if not is_jail(jail): return 'Could not find jail {0}'.format(jail) # Generate command if keep: cmd = 'poudriere bulk -k -f {0} -j {1}'.format(pkg_file, jail) else: cmd = 'poudriere bulk -f {0} -j {1}'.format(pkg_file, jail) # Bulk build this can take some time, depending on pkg_file ... hours res = __salt__['cmd.run'](cmd) lines = res.splitlines() for line in lines: if "packages built" in line: return line return ('There may have been an issue building packages dumping output: ' '{0}').format(res) salt-0.17.5+ds.orig/salt/modules/pw_group.py0000644000175000017500000000464612270576114017072 0ustar joejoe# -*- coding: utf-8 -*- ''' Manage groups on FreeBSD ''' # Import python libs import logging # Import salt libs import salt.utils log = logging.getLogger(__name__) try: import grp except ImportError: pass def __virtual__(): ''' Set the user module if the kernel is Linux ''' return 'group' if __grains__['kernel'] == 'FreeBSD' else False def add(name, gid=None, **kwargs): ''' Add the specified group CLI Example: .. code-block:: bash salt '*' group.add foo 3456 ''' kwargs = salt.utils.clean_kwargs(**kwargs) if salt.utils.is_true(kwargs.pop('system', False)): log.warning('pw_group module does not support the \'system\' argument') if kwargs: log.warning('Invalid kwargs passed to group.add') cmd = 'pw groupadd ' if gid: cmd += '-g {0} '.format(gid) cmd = '{0} -n {1}'.format(cmd, name) ret = __salt__['cmd.run_all'](cmd) return not ret['retcode'] def delete(name): ''' Remove the named group CLI Example: .. code-block:: bash salt '*' group.delete foo ''' ret = __salt__['cmd.run_all']('pw groupdel {0}'.format(name)) return not ret['retcode'] def info(name): ''' Return information about a group CLI Example: .. code-block:: bash salt '*' group.info foo ''' try: grinfo = grp.getgrnam(name) except KeyError: return {} else: return {'name': grinfo.gr_name, 'passwd': grinfo.gr_passwd, 'gid': grinfo.gr_gid, 'members': grinfo.gr_mem} def getent(refresh=False): ''' Return info on all groups CLI Example: .. code-block:: bash salt '*' group.getent ''' if 'group.getent' in __context__ and not refresh: return __context__['group.getent'] ret = [] for grinfo in grp.getgrall(): ret.append(info(grinfo.gr_name)) __context__['group.getent'] = ret return ret def chgid(name, gid): ''' Change the gid for a named group CLI Example: .. code-block:: bash salt '*' group.chgid foo 4376 ''' pre_gid = __salt__['file.group_to_gid'](name) if gid == pre_gid: return True cmd = 'pw groupmod {0} -g {1}'.format(name, gid) __salt__['cmd.run'](cmd) post_gid = __salt__['file.group_to_gid'](name) if post_gid != pre_gid: return post_gid == gid return False salt-0.17.5+ds.orig/salt/modules/win_timezone.py0000644000175000017500000005767512270576114017751 0ustar joejoe# -*- coding: utf-8 -*- ''' Module for managing timezone on Windows systems. ''' # Import python libs import salt.utils import logging import re log = logging.getLogger(__name__) # Maybe put in a different file ... ? %-0 # http://unicode.org/repos/cldr/trunk/common/supplemental/windowsZones.xml LINTOWIN = { 'Africa/Abidjan': 'Greenwich Standard Time', 'Africa/Accra': 'Greenwich Standard Time', 'Africa/Addis_Ababa': 'E. Africa Standard Time', 'Africa/Algiers': 'W. Central Africa Standard Time', 'Africa/Asmera': 'E. Africa Standard Time', 'Africa/Bamako': 'Greenwich Standard Time', 'Africa/Bangui': 'W. Central Africa Standard Time', 'Africa/Banjul': 'Greenwich Standard Time', 'Africa/Bissau': 'Greenwich Standard Time', 'Africa/Blantyre': 'South Africa Standard Time', 'Africa/Brazzaville': 'W. Central Africa Standard Time', 'Africa/Bujumbura': 'South Africa Standard Time', 'Africa/Cairo': 'Egypt Standard Time', 'Africa/Casablanca': 'Morocco Standard Time', 'Africa/Conakry': 'Greenwich Standard Time', 'Africa/Dakar': 'Greenwich Standard Time', 'Africa/Dar_es_Salaam': 'E. Africa Standard Time', 'Africa/Djibouti': 'E. Africa Standard Time', 'Africa/Douala': 'W. Central Africa Standard Time', 'Africa/El_Aaiun': 'Greenwich Standard Time', 'Africa/Freetown': 'Greenwich Standard Time', 'Africa/Gaborone': 'South Africa Standard Time', 'Africa/Harare': 'South Africa Standard Time', 'Africa/Johannesburg': 'South Africa Standard Time', 'Africa/Juba': 'E. Africa Standard Time', 'Africa/Kampala': 'E. Africa Standard Time', 'Africa/Khartoum': 'E. Africa Standard Time', 'Africa/Kigali': 'South Africa Standard Time', 'Africa/Kinshasa': 'W. Central Africa Standard Time', 'Africa/Lagos': 'W. Central Africa Standard Time', 'Africa/Libreville': 'W. Central Africa Standard Time', 'Africa/Lome': 'Greenwich Standard Time', 'Africa/Luanda': 'W. Central Africa Standard Time', 'Africa/Lubumbashi': 'South Africa Standard Time', 'Africa/Lusaka': 'South Africa Standard Time', 'Africa/Malabo': 'W. Central Africa Standard Time', 'Africa/Maputo': 'South Africa Standard Time', 'Africa/Maseru': 'South Africa Standard Time', 'Africa/Mbabane': 'South Africa Standard Time', 'Africa/Mogadishu': 'E. Africa Standard Time', 'Africa/Monrovia': 'Greenwich Standard Time', 'Africa/Nairobi': 'E. Africa Standard Time', 'Africa/Ndjamena': 'W. Central Africa Standard Time', 'Africa/Niamey': 'W. Central Africa Standard Time', 'Africa/Nouakchott': 'Greenwich Standard Time', 'Africa/Ouagadougou': 'Greenwich Standard Time', 'Africa/Porto-Novo': 'W. Central Africa Standard Time', 'Africa/Sao_Tome': 'Greenwich Standard Time', 'Africa/Tripoli': 'W. Europe Standard Time', 'Africa/Tunis': 'W. Central Africa Standard Time', 'Africa/Windhoek': 'Namibia Standard Time', 'America/Anchorage': 'Alaskan Standard Time', 'America/Juneau': 'Alaskan Standard Time', 'America/Nome': 'Alaskan Standard Time', 'America/Sitka': 'Alaskan Standard Time', 'America/Yakutat': 'Alaskan Standard Time', 'America/Anguilla': 'SA Western Standard Time', 'America/Antigua': 'SA Western Standard Time', 'America/Aruba': 'SA Western Standard Time', 'America/Asuncion': 'Paraguay Standard Time', 'America/Bahia': 'Bahia Standard Time', 'America/Barbados': 'SA Western Standard Time', 'America/Belize': 'Central America Standard Time', 'America/Blanc-Sablon': 'SA Western Standard Time', 'America/Bogota': 'SA Pacific Standard Time', 'America/Buenos_Aires': 'Argentina Standard Time', 'America/Argentina/La_Rioja': 'Argentina Standard Time', 'America/Argentina/Rio_Gallegos': 'Argentina Standard Time', 'America/Argentina/Salta': 'Argentina Standard Time', 'America/Argentina/San_Juan': 'Argentina Standard Time', 'America/Argentina/San_Luis': 'Argentina Standard Time', 'America/Argentina/Tucuman': 'Argentina Standard Time', 'America/Argentina/Ushuaia': 'Argentina Standard Time', 'America/Catamarca': 'Argentina Standard Time', 'America/Cordoba': 'Argentina Standard Time', 'America/Jujuy': 'Argentina Standard Time', 'America/Mendoza': 'Argentina Standard Time', 'America/Caracas': 'Venezuela Standard Time', 'America/Cayenne': 'SA Eastern Standard Time', 'America/Cayman': 'SA Pacific Standard Time', 'America/Chicago': 'Central Standard Time', 'America/Indiana/Knox': 'Central Standard Time', 'America/Indiana/Tell_City': 'Central Standard Time', 'America/Menominee': 'Central Standard Time', 'America/North_Dakota/Beulah': 'Central Standard Time', 'America/North_Dakota/Center': 'Central Standard Time', 'America/North_Dakota/New_Salem': 'Central Standard Time', 'America/Chihuahua': 'Mountain Standard Time (Mexico)', 'America/Mazatlan': 'Mountain Standard Time (Mexico)', 'America/Coral_Harbour': 'SA Pacific Standard Time', 'America/Costa_Rica': 'Central America Standard Time', 'America/Cuiaba': 'Central Brazilian Standard Time', 'America/Campo_Grande': 'Central Brazilian Standard Time', 'America/Curacao': 'SA Western Standard Time', 'America/Danmarkshavn': 'UTC', 'America/Dawson_Creek': 'US Mountain Standard Time', 'America/Creston': 'US Mountain Standard Time', 'America/Denver': 'Mountain Standard Time', 'America/Boise': 'Mountain Standard Time', 'America/Shiprock': 'Mountain Standard Time', 'America/Dominica': 'SA Western Standard Time', 'America/Edmonton': 'Mountain Standard Time', 'America/Cambridge_Bay': 'Mountain Standard Time', 'America/Inuvik': 'Mountain Standard Time', 'America/Yellowknife': 'Mountain Standard Time', 'America/El_Salvador': 'Central America Standard Time', 'America/Fortaleza': 'SA Eastern Standard Time', 'America/Belem': 'SA Eastern Standard Time', 'America/Maceio': 'SA Eastern Standard Time', 'America/Recife': 'SA Eastern Standard Time', 'America/Santarem': 'SA Eastern Standard Time', 'America/Godthab': 'Greenland Standard Time', 'America/Grand_Turk': 'Eastern Standard Time', 'America/Grenada': 'SA Western Standard Time', 'America/Guadeloupe': 'SA Western Standard Time', 'America/Guatemala': 'Central America Standard Time', 'America/Guayaquil': 'SA Pacific Standard Time', 'America/Guyana': 'SA Western Standard Time', 'America/Halifax': 'Atlantic Standard Time', 'America/Glace_Bay': 'Atlantic Standard Time', 'America/Goose_Bay': 'Atlantic Standard Time', 'America/Moncton': 'Atlantic Standard Time', 'America/Hermosillo': 'US Mountain Standard Time', 'America/Indianapolis': 'US Eastern Standard Time', 'America/Indiana/Marengo': 'US Eastern Standard Time', 'America/Indiana/Vevay': 'US Eastern Standard Time', 'America/Jamaica': 'SA Pacific Standard Time', 'America/Kralendijk': 'SA Western Standard Time', 'America/La_Paz': 'SA Western Standard Time', 'America/Lima': 'SA Pacific Standard Time', 'America/Los_Angeles': 'Pacific Standard Time', 'America/Lower_Princes': 'SA Western Standard Time', 'America/Managua': 'Central America Standard Time', 'America/Manaus': 'SA Western Standard Time', 'America/Boa_Vista': 'SA Western Standard Time', 'America/Eirunepe': 'SA Western Standard Time', 'America/Porto_Velho': 'SA Western Standard Time', 'America/Rio_Branco': 'SA Western Standard Time', 'America/Marigot': 'SA Western Standard Time', 'America/Martinique': 'SA Western Standard Time', 'America/Matamoros': 'Central Standard Time', 'America/Mexico_City': 'Central Standard Time (Mexico)', 'America/Bahia_Banderas': 'Central Standard Time (Mexico)', 'America/Cancun': 'Central Standard Time (Mexico)', 'America/Merida': 'Central Standard Time (Mexico)', 'America/Monterrey': 'Central Standard Time (Mexico)', 'America/Montevideo': 'Montevideo Standard Time', 'America/Montserrat': 'SA Western Standard Time', 'America/Nassau': 'Eastern Standard Time', 'America/New_York': 'Eastern Standard Time', 'America/Detroit': 'Eastern Standard Time', 'America/Indiana/Petersburg': 'Eastern Standard Time', 'America/Indiana/Vincennes': 'Eastern Standard Time', 'America/Indiana/Winamac': 'Eastern Standard Time', 'America/Kentucky/Monticello': 'Eastern Standard Time', 'America/Louisville': 'Eastern Standard Time', 'America/Noronha': 'UTC-02', 'America/Ojinaga': 'Mountain Standard Time', 'America/Panama': 'SA Pacific Standard Time', 'America/Paramaribo': 'SA Eastern Standard Time', 'America/Phoenix': 'US Mountain Standard Time', 'America/Port-au-Prince': 'SA Pacific Standard Time', 'America/Port_of_Spain': 'SA Western Standard Time', 'America/Puerto_Rico': 'SA Western Standard Time', 'America/Regina': 'Canada Central Standard Time', 'America/Swift_Current': 'Canada Central Standard Time', 'America/Santa_Isabel': 'Pacific Standard Time (Mexico)', 'America/Santiago': 'Pacific SA Standard Time', 'America/Santo_Domingo': 'SA Western Standard Time', 'America/Sao_Paulo': 'E. South America Standard Time', 'America/Araguaina': 'E. South America Standard Time', 'America/Scoresbysund': 'Azores Standard Time', 'America/St_Barthelemy': 'SA Western Standard Time', 'America/St_Johns': 'Newfoundland Standard Time', 'America/St_Kitts': 'SA Western Standard Time', 'America/St_Lucia': 'SA Western Standard Time', 'America/St_Thomas': 'SA Western Standard Time', 'America/St_Vincent': 'SA Western Standard Time', 'America/Tegucigalpa': 'Central America Standard Time', 'America/Thule': 'Atlantic Standard Time', 'America/Tijuana': 'Pacific Standard Time', 'America/Toronto': 'Eastern Standard Time', 'America/Iqaluit': 'Eastern Standard Time', 'America/Montreal': 'Eastern Standard Time', 'America/Nipigon': 'Eastern Standard Time', 'America/Pangnirtung': 'Eastern Standard Time', 'America/Thunder_Bay': 'Eastern Standard Time', 'America/Tortola': 'SA Western Standard Time', 'America/Whitehorse': 'Pacific Standard Time', 'America/Vancouver': 'Pacific Standard Time', 'America/Dawson': 'Pacific Standard Time', 'America/Winnipeg': 'Central Standard Time', 'America/Rainy_River': 'Central Standard Time', 'America/Rankin_Inlet': 'Central Standard Time', 'America/Resolute': 'Central Standard Time', 'Antarctica/Casey': 'W. Australia Standard Time', 'Antarctica/Davis': 'SE Asia Standard Time', 'Antarctica/DumontDUrville': 'West Pacific Standard Time', 'Antarctica/Macquarie': 'Central Pacific Standard Time', 'Antarctica/Mawson': 'West Asia Standard Time', 'Antarctica/Palmer': 'Pacific SA Standard Time', 'Antarctica/Rothera': 'SA Eastern Standard Time', 'Antarctica/South_Pole': 'New Zealand Standard Time', 'Antarctica/McMurdo': 'New Zealand Standard Time', 'Antarctica/Syowa': 'E. Africa Standard Time', 'Antarctica/Vostok': 'Central Asia Standard Time', 'Arctic/Longyearbyen': 'W. Europe Standard Time', 'Asia/Aden': 'Arab Standard Time', 'Asia/Almaty': 'Central Asia Standard Time', 'Asia/Qyzylorda': 'Central Asia Standard Time', 'Asia/Amman': 'Jordan Standard Time', 'Asia/Ashgabat': 'West Asia Standard Time', 'Asia/Baghdad': 'Arabic Standard Time', 'Asia/Bahrain': 'Arab Standard Time', 'Asia/Baku': 'Azerbaijan Standard Time', 'Asia/Bangkok': 'SE Asia Standard Time', 'Asia/Beirut': 'Middle East Standard Time', 'Asia/Bishkek': 'Central Asia Standard Time', 'Asia/Brunei': 'Singapore Standard Time', 'Asia/Calcutta': 'India Standard Time', 'Asia/Colombo': 'Sri Lanka Standard Time', 'Asia/Damascus': 'Syria Standard Time', 'Asia/Dhaka': 'Bangladesh Standard Time', 'Asia/Dili': 'Tokyo Standard Time', 'Asia/Dubai': 'Arabian Standard Time', 'Asia/Dushanbe': 'West Asia Standard Time', 'Asia/Gaza': 'Egypt Standard Time', 'Asia/Hebron': 'Egypt Standard Time', 'Asia/Hong_Kong': 'China Standard Time', 'Asia/Hovd': 'SE Asia Standard Time', 'Asia/Irkutsk': 'North Asia East Standard Time', 'Asia/Jakarta': 'SE Asia Standard Time', 'Asia/Pontianak': 'SE Asia Standard Time', 'Asia/Jayapura': 'Tokyo Standard Time', 'Asia/Jerusalem': 'Israel Standard Time', 'Asia/Kabul': 'Afghanistan Standard Time', 'Asia/Karachi': 'Pakistan Standard Time', 'Asia/Katmandu': 'Nepal Standard Time', 'Asia/Krasnoyarsk': 'North Asia Standard Time', 'Asia/Kuala_Lumpur': 'Singapore Standard Time', 'Asia/Kuching': 'Singapore Standard Time', 'Asia/Kuwait': 'Arab Standard Time', 'Asia/Macau': 'China Standard Time', 'Asia/Magadan': 'Magadan Standard Time', 'Asia/Anadyr Asia/Kamchatka': 'Magadan Standard Time', 'Asia/Kamchatka': 'Magadan Standard Time', 'Asia/Makassar': 'Singapore Standard Time', 'Asia/Manila': 'Singapore Standard Time', 'Asia/Muscat': 'Arabian Standard Time', 'Asia/Nicosia': 'E. Europe Standard Time', 'Asia/Novosibirsk': 'N. Central Asia Standard Time', 'Asia/Novokuznetsk': 'N. Central Asia Standard Time', 'Asia/Omsk': 'N. Central Asia Standard Time', 'Asia/Oral': 'West Asia Standard Time', 'Asia/Aqtau': 'West Asia Standard Time', 'Asia/Aqtobe': 'West Asia Standard Time', 'Asia/Phnom_Penh': 'SE Asia Standard Time', 'Asia/Pyongyang': 'Korea Standard Time', 'Asia/Qatar': 'Arab Standard Time', 'Asia/Rangoon': 'Myanmar Standard Time', 'Asia/Riyadh': 'Arab Standard Time', 'Asia/Saigon': 'SE Asia Standard Time', 'Asia/Seoul': 'Korea Standard Time', 'Asia/Shanghai': 'China Standard Time', 'Asia/Chongqing': 'China Standard Time', 'Asia/Harbin': 'China Standard Time', 'Asia/Kashgar': 'China Standard Time', 'Asia/Urumqi': 'China Standard Time', 'Asia/Singapore': 'Singapore Standard Time', 'Asia/Taipei': 'Taipei Standard Time', 'Asia/Tashkent': 'West Asia Standard Time', 'Asia/Samarkand': 'West Asia Standard Time', 'Asia/Tbilisi': 'Georgian Standard Time', 'Asia/Tehran': 'Iran Standard Time', 'Asia/Thimphu': 'Bangladesh Standard Time', 'Asia/Tokyo': 'Tokyo Standard Time', 'Asia/Ulaanbaatar': 'Ulaanbaatar Standard Time', 'Asia/Choibalsan': 'Ulaanbaatar Standard Time', 'Asia/Vientiane': 'SE Asia Standard Time', 'Asia/Vladivostok': 'Vladivostok Standard Time', 'Asia/Ust-Nera': 'Vladivostok Standard Time', 'Asia/Sakhalin': 'Vladivostok Standard Time', 'Asia/Yakutsk': 'Yakutsk Standard Time', 'Asia/Khandyga': 'Yakutsk Standard Time', 'Asia/Yekaterinburg': 'Ekaterinburg Standard Time', 'Asia/Yerevan': 'Caucasus Standard Time', 'Atlantic/Azores': 'Azores Standard Time', 'Atlantic/Bermuda': 'Atlantic Standard Time', 'Atlantic/Canary': 'GMT Standard Time', 'Atlantic/Cape_Verde': 'Cape Verde Standard Time', 'Atlantic/Faeroe': 'GMT Standard Time', 'Atlantic/Reykjavik': 'Greenwich Standard Time', 'Atlantic/South_Georgia': 'UTC-02', 'Atlantic/St_Helena': 'Greenwich Standard Time', 'Atlantic/Stanley': 'SA Eastern Standard Time', 'Australia/Adelaide': 'Cen. Australia Standard Time', 'Australia/Broken_Hill': 'Cen. Australia Standard Time', 'Australia/Brisbane': 'E. Australia Standard Time', 'Australia/Lindeman': 'E. Australia Standard Time', 'Australia/Darwin': 'AUS Central Standard Time', 'Australia/Hobart': 'Tasmania Standard Time', 'Australia/Currie': 'Tasmania Standard Time', 'Australia/Perth': 'W. Australia Standard Time', 'Australia/Sydney': 'AUS Eastern Standard Time', 'Australia/Melbourne': 'AUS Eastern Standard Time', 'CST6CDT': 'Central Standard Time', 'EST5EDT': 'Eastern Standard Time', 'Etc/UTC': 'UTC', 'Etc/GMT': 'UTC', 'Etc/GMT+1': 'Cape Verde Standard Time', 'Etc/GMT+10': 'Hawaiian Standard Time', 'Etc/GMT+11': 'UTC-11', 'Etc/GMT+12': 'Dateline Standard Time', 'Etc/GMT+2': 'UTC-02', 'Etc/GMT+3': 'SA Eastern Standard Time', 'Etc/GMT+4': 'SA Western Standard Time', 'Etc/GMT+5': 'SA Pacific Standard Time', 'Etc/GMT+6': 'Central America Standard Time', 'Etc/GMT+7': 'US Mountain Standard Time', 'Etc/GMT-1': 'W. Central Africa Standard Time', 'Etc/GMT-10': 'West Pacific Standard Time', 'Etc/GMT-11': 'Central Pacific Standard Time', 'Etc/GMT-12': 'UTC+12', 'Etc/GMT-13': 'Tonga Standard Time', 'Etc/GMT-2': 'South Africa Standard Time', 'Etc/GMT-3': 'E. Africa Standard Time', 'Etc/GMT-4': 'Arabian Standard Time', 'Etc/GMT-5': 'West Asia Standard Time', 'Etc/GMT-6': 'Central Asia Standard Time', 'Etc/GMT-7': 'SE Asia Standard Time', 'Etc/GMT-8': 'Singapore Standard Time', 'Etc/GMT-9': 'Tokyo Standard Time', 'Europe/Amsterdam': 'W. Europe Standard Time', 'Europe/Andorra': 'W. Europe Standard Time', 'Europe/Athens': 'GTB Standard Time', 'Europe/Belgrade': 'Central Europe Standard Time', 'Europe/Berlin': 'W. Europe Standard Time', 'Europe/Busingen': 'W. Europe Standard Time', 'Europe/Bratislava': 'Central Europe Standard Time', 'Europe/Brussels': 'Romance Standard Time', 'Europe/Bucharest': 'GTB Standard Time', 'Europe/Budapest': 'Central Europe Standard Time', 'Europe/Chisinau': 'GTB Standard Time', 'Europe/Copenhagen': 'Romance Standard Time', 'Europe/Dublin': 'GMT Standard Time', 'Europe/Gibraltar': 'W. Europe Standard Time', 'Europe/Guernsey': 'GMT Standard Time', 'Europe/Helsinki': 'FLE Standard Time', 'Europe/Isle_of_Man': 'GMT Standard Time', 'Europe/Istanbul': 'Turkey Standard Time', 'Europe/Jersey': 'GMT Standard Time', 'Europe/Kaliningrad': 'Kaliningrad Standard Time', 'Europe/Kiev': 'FLE Standard Time', 'Europe/Simferopol': 'FLE Standard Time', 'Europe/Uzhgorod': 'FLE Standard Time', 'Europe/Zaporozhye': 'FLE Standard Time', 'Europe/Lisbon': 'GMT Standard Time', 'Atlantic/Madeira': 'GMT Standard Time', 'Europe/Ljubljana': 'Central Europe Standard Time', 'Europe/London': 'GMT Standard Time', 'Europe/Luxembourg': 'W. Europe Standard Time', 'Europe/Madrid': 'Romance Standard Time', 'Africa/Ceuta': 'Romance Standard Time', 'Europe/Malta': 'W. Europe Standard Time', 'Europe/Mariehamn': 'FLE Standard Time', 'Europe/Minsk': 'Kaliningrad Standard Time', 'Europe/Monaco': 'W. Europe Standard Time', 'Europe/Moscow': 'Russian Standard Time', 'Europe/Volgograd': 'Russian Standard Time', 'Europe/Samara': 'Russian Standard Time', 'Europe/Oslo': 'W. Europe Standard Time', 'Europe/Paris': 'Romance Standard Time', 'Europe/Podgorica': 'Central Europe Standard Time', 'Europe/Prague': 'Central Europe Standard Time', 'Europe/Riga': 'FLE Standard Time', 'Europe/Rome': 'W. Europe Standard Time', 'Europe/San_Marino': 'W. Europe Standard Time', 'Europe/Sarajevo': 'Central European Standard Time', 'Europe/Skopje': 'Central European Standard Time', 'Europe/Sofia': 'FLE Standard Time', 'Europe/Stockholm': 'W. Europe Standard Time', 'Europe/Tallinn': 'FLE Standard Time', 'Europe/Tirane': 'Central Europe Standard Time', 'Europe/Vaduz': 'W. Europe Standard Time', 'Europe/Vatican': 'W. Europe Standard Time', 'Europe/Vienna': 'W. Europe Standard Time', 'Europe/Vilnius': 'FLE Standard Time', 'Europe/Warsaw': 'Central European Standard Time', 'Europe/Zagreb': 'Central European Standard Time', 'Europe/Zurich': 'W. Europe Standard Time', 'Indian/Antananarivo': 'E. Africa Standard Time', 'Indian/Chagos': 'Central Asia Standard Time', 'Indian/Christmas': 'SE Asia Standard Time', 'Indian/Cocos': 'Myanmar Standard Time', 'Indian/Comoro': 'E. Africa Standard Time', 'Indian/Kerguelen': 'West Asia Standard Time', 'Indian/Mahe': 'Mauritius Standard Time', 'Indian/Maldives': 'West Asia Standard Time', 'Indian/Mauritius': 'Mauritius Standard Time', 'Indian/Mayotte': 'E. Africa Standard Time', 'Indian/Reunion': 'Mauritius Standard Time', 'MST7MDT': 'Mountain Standard Time', 'PST8PDT': 'Pacific Standard Time', 'Pacific/Apia': 'Samoa Standard Time', 'Pacific/Auckland': 'New Zealand Standard Time', 'Pacific/Efate': 'Central Pacific Standard Time', 'Pacific/Enderbury': 'Tonga Standard Time', 'Pacific/Fakaofo': 'Tonga Standard Time', 'Pacific/Fiji': 'Fiji Standard Time', 'Pacific/Funafuti': 'UTC+12', 'Pacific/Galapagos': 'Central America Standard Time', 'Pacific/Guadalcanal': 'Central Pacific Standard Time', 'Pacific/Guam': 'West Pacific Standard Time', 'Pacific/Honolulu': 'Hawaiian Standard Time', 'Pacific/Johnston': 'Hawaiian Standard Time', 'Pacific/Majuro Pacific/Kwajalein': 'UTC+12', 'Pacific/Midway': 'UTC-11', 'Pacific/Nauru': 'UTC+12', 'Pacific/Niue': 'UTC-11', 'Pacific/Noumea': 'Central Pacific Standard Time', 'Pacific/Pago_Pago': 'UTC-11', 'Pacific/Palau': 'Tokyo Standard Time', 'Pacific/Ponape': 'Central Pacific Standard Time', 'Pacific/Kosrae': 'Central Pacific Standard Time', 'Pacific/Port_Moresby': 'West Pacific Standard Time', 'Pacific/Rarotonga': 'Hawaiian Standard Time', 'Pacific/Saipan': 'West Pacific Standard Time', 'Pacific/Tahiti': 'Hawaiian Standard Time', 'Pacific/Tarawa': 'UTC+12', 'Pacific/Tongatapu': 'Tonga Standard Time', 'Pacific/Truk': 'West Pacific Standard Time', 'Pacific/Wake': 'UTC+12', 'Pacific/Wallis': 'UTC+12' } def __virtual__(): ''' Only load on windows ''' if salt.utils.is_windows(): return 'timezone' return False def get_zone(): ''' Get current timezone (i.e. America/Denver) CLI Example: .. code-block:: bash salt '*' timezone.get_zone ''' winzone = __salt__['cmd.run']('tzutil /g') for key in LINTOWIN: if LINTOWIN[key] == winzone: return key return False def get_offset(): ''' Get current numeric timezone offset from UCT (i.e. -0700) CLI Example: .. code-block:: bash salt '*' timezone.get_offset ''' string = False zone = __salt__['cmd.run']('tzutil /g') prev = '' for line in __salt__['cmd.run']('tzutil /l').splitlines(): if zone == line: string = prev break else: prev = line if not string: return False reg = re.search(r"\(UTC(.\d\d:\d\d)\) .*", string, re.M) if not reg: ret = '0000' else: ret = reg.group(1).replace(':', '') return ret def get_zonecode(): ''' Get current timezone (i.e. PST, MDT, etc) CLI Example: .. code-block:: bash salt '*' timezone.get_zonecode ''' # Still not implemented on windows return False def set_zone(timezone): ''' Unlinks, then symlinks /etc/localtime to the set timezone. The timezone is crucial to several system processes, each of which SHOULD be restarted (for instance, whatever you system uses as its cron and syslog daemons). This will not be magically done for you! CLI Example: .. code-block:: bash salt '*' timezone.set_zone 'America/Denver' ''' return __salt__['cmd.retcode']('tzutil /s "{0}"'.format(LINTOWIN[timezone])) == 0 def zone_compare(timezone): ''' Checks the md5sum between the given timezone, and the one set in /etc/localtime. Returns True if they match, and False if not. Mostly useful for running state checks. Example: .. code-block:: bash salt '*' timezone.zone_compare 'America/Denver' ''' return __salt__['cmd.run']('tzutil /g') == LINTOWIN[timezone] def get_hwclock(): ''' Get current hardware clock setting (UTC or localtime) CLI Example: .. code-block:: bash salt '*' timezone.get_hwclock ''' # Need to search for a way to figure it out ... return 'localtime' def set_hwclock(clock): ''' Sets the hardware clock to be either UTC or localtime CLI Example: .. code-block:: bash salt '*' timezone.set_hwclock UTC ''' # Need to search for a way to figure it out ... return False salt-0.17.5+ds.orig/salt/modules/freebsdpkg.py0000644000175000017500000003205712270576120017336 0ustar joejoe# -*- coding: utf-8 -*- ''' Package support for FreeBSD ''' # Import python libs import copy import logging import os import re # Import salt libs import salt.utils import salt.utils.decorators as decorators PKG_NAME_VERSION_RE = re.compile(r'(?P.*)-(?P[0-9_\-.]+)') log = logging.getLogger(__name__) def __virtual__(): ''' Set the virtual pkg module if the os is FreeBSD ''' return 'pkg' if __grains__['os'] == 'FreeBSD' else False def _check_pkgng(): ''' Looks to see if pkgng is being used by checking if database exists ''' return os.path.isfile('/var/db/pkg/local.sqlite') @decorators.memoize def _cmd(cmd): return salt.utils.which(cmd) def search(pkg_name): ''' Use `pkg search` if pkg is being used. CLI Example: .. code-block:: bash salt '*' pkg.search 'mysql-server' ''' if _check_pkgng(): res = __salt__['cmd.run_all']('{0} search {1}'.format(_cmd('pkg'), pkg_name)) res = [x for x in res.splitlines()] return {'Results': res} def latest_version(*names, **kwargs): ''' Return the latest version of the named package available for upgrade or installation. If more than one package name is specified, a dict of name/version pairs is returned. If the latest version of a given package is already installed, an empty string will be returned for that package. CLI Example: .. code-block:: bash salt '*' pkg.latest_version salt '*' pkg.latest_version ... ''' refresh = salt.utils.is_true(kwargs.pop('refresh', True)) ret = {} # Refresh before looking for the latest version available if refresh: refresh_db() if _check_pkgng(): for line in __salt__['cmd.run_stdout']('{0} upgrade -nq'.format( _cmd('pkg')) ).splitlines(): if not line.startswith('\t'): continue line = line.strip() _words = line.split() if _words[0] in ('Installing', 'Upgrading', 'Downgrading'): pkg = _words[1].rstrip(':') ver = _words[2] if _words[0] == 'Installing' else _words[4] elif _words[0] in ('Reinstalling'): # Because of packages like 'xen-tools-4.1.3_3', let's use regex pkg, ver = PKG_NAME_VERSION_RE.match(_words[1]).groups() else: # unexpected string continue if pkg in names: ret[pkg] = ver # keep pkg.latest culm for pkg in set(names) - set(ret) - set(list_pkgs()): for line in __salt__['cmd.run']('{0} search -fe -Sname {1}'.format( _cmd('pkg'), pkg) ).splitlines(): if line.startswith('Version'): _, _, ver = line.split()[:3] ret[pkg] = ver break ret.update(dict.fromkeys(set(names) - set(ret), '')) if len(names) == 1: return ret.values()[0] return ret # available_version is being deprecated available_version = latest_version def version(*names, **kwargs): ''' Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. CLI Example: .. code-block:: bash salt '*' pkg.version salt '*' pkg.version ... ''' return __salt__['pkg_resource.version'](*names, **kwargs) def refresh_db(): ''' Use pkg update to get latest repo.txz when using pkgng. Updating with portsnap is not yet supported. CLI Example: .. code-block:: bash salt '*' pkg.refresh_db ''' if _check_pkgng(): __salt__['cmd.run_all']('{0} update'.format(_cmd('pkg'))) return {} def list_pkgs(versions_as_list=False, **kwargs): ''' List the packages currently installed as a dict:: {'': ''} CLI Example: .. code-block:: bash salt '*' pkg.list_pkgs ''' versions_as_list = salt.utils.is_true(versions_as_list) # 'removed' not yet implemented or not applicable if salt.utils.is_true(kwargs.get('removed')): return {} if 'pkg.list_pkgs' in __context__: if versions_as_list: return __context__['pkg.list_pkgs'] else: ret = copy.deepcopy(__context__['pkg.list_pkgs']) __salt__['pkg_resource.stringify'](ret) return ret if _check_pkgng(): pkg_command = '{0} info'.format(_cmd('pkg')) else: pkg_command = '{0}'.format(_cmd('pkg_info')) ret = {} for line in __salt__['cmd.run'](pkg_command).splitlines(): if not line: continue pkg, ver = line.split(' ')[0].rsplit('-', 1) __salt__['pkg_resource.add_pkg'](ret, pkg, ver) __salt__['pkg_resource.sort_pkglist'](ret) __context__['pkg.list_pkgs'] = copy.deepcopy(ret) if not versions_as_list: __salt__['pkg_resource.stringify'](ret) return ret def install(name=None, refresh=False, fromrepo=None, pkgs=None, sources=None, **kwargs): ''' Install the passed package name The name of the package to be installed. refresh Whether or not to refresh the package database before installing. fromrepo Specify a package repository to install from. Multiple Package Installation Options: pkgs A list of packages to install from a software repository. Must be passed as a python list. CLI Example: .. code-block:: bash salt '*' pkg.install pkgs='["foo","bar"]' sources A list of 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.deb"},{"bar": "salt://bar.deb"}]' Return a dict containing the new package names and versions:: {'': {'old': '', 'new': ''}} CLI Example: .. code-block:: bash salt '*' pkg.install ''' pkg_params, pkg_type = __salt__['pkg_resource.parse_targets'](name, pkgs, sources, **kwargs) # Support old "repo" argument repo = kwargs.get('repo', '') if not fromrepo and repo: fromrepo = repo if not pkg_params: return {} env = [] args = [] if _check_pkgng(): cmd = _cmd('pkg') if fromrepo: log.info('Setting PACKAGESITE={0}'.format(fromrepo)) env.append(('PACKAGESITE', fromrepo)) else: cmd = _cmd('pkg_add') if fromrepo: log.info('Setting PACKAGEROOT={0}'.format(fromrepo)) env.append(('PACKAGEROOT', fromrepo)) if pkg_type == 'file': if _check_pkgng(): env.append(('ASSUME_ALWAYS_YES', 'yes')) # might be fixed later args.append('add') elif pkg_type == 'repository': if _check_pkgng(): args.extend(('install', '-y')) # Assume yes when asked if not refresh: args.append('-U') # do not update repo db else: args.append('-r') # use remote repo args.extend(pkg_params) old = list_pkgs() __salt__['cmd.run_all']('{0} {1}'.format(cmd, ' '.join(args)), env=env) __context__.pop('pkg.list_pkgs', None) new = list_pkgs() rehash() return __salt__['pkg_resource.find_changes'](old, new) def upgrade(): ''' Run pkg upgrade, if pkgng used. Otherwise do nothing Return a dict containing the new package names and versions:: {'': {'old': '', 'new': ''}} CLI Example: .. code-block:: bash salt '*' pkg.upgrade ''' if not _check_pkgng(): # There is not easy way to upgrade packages with old package system return {} old = list_pkgs() __salt__['cmd.run_all']('{0} upgrade -y'.format(_cmd('pkg'))) __context__.pop('pkg.list_pkgs', None) new = list_pkgs() return __salt__['pkg_resource.find_changes'](old, new) def remove(name=None, pkgs=None, **kwargs): ''' Remove packages. name The name of the package to be deleted. Multiple Package Options: pkgs A list of packages to delete. Must be passed as a python list. The ``name`` parameter will be ignored if this option is passed. .. versionadded:: 0.16.0 Returns a dict containing the changes. CLI Example: .. code-block:: bash salt '*' pkg.remove salt '*' pkg.remove ,, salt '*' pkg.remove pkgs='["foo", "bar"]' ''' pkg_params = __salt__['pkg_resource.parse_targets'](name, pkgs)[0] old = list_pkgs() targets = [x for x in pkg_params if x in old] if not targets: return {} for_remove = ' '.join(targets) if _check_pkgng(): cmd = '{0} remove -y {1}'.format(_cmd('pkg'), for_remove) else: cmd = '{0} {1}'.format(_cmd('pkg_remove'), for_remove) __salt__['cmd.run_all'](cmd) __context__.pop('pkg.list_pkgs', None) new = list_pkgs() return __salt__['pkg_resource.find_changes'](old, new) def purge(name=None, pkgs=None, **kwargs): ''' Package purges are not supported, this function is identical to ``remove()``. name The name of the package to be deleted. Multiple Package Options: pkgs A list of packages to delete. Must be passed as a python list. The ``name`` parameter will be ignored if this option is passed. .. versionadded:: 0.16.0 Returns a dict containing the changes. CLI Example: .. code-block:: bash salt '*' pkg.purge salt '*' pkg.purge ,, salt '*' pkg.purge pkgs='["foo", "bar"]' ''' return remove(name=name, pkgs=pkgs) def rehash(): ''' Recomputes internal hash table for the PATH variable. Use whenever a new command is created during the current session. CLI Example: .. code-block:: bash salt '*' pkg.rehash ''' shell = __salt__['cmd.run']('echo $SHELL').split('/') if shell[len(shell) - 1] in ['csh', 'tcsh']: __salt__['cmd.run_all']('rehash') def file_list(*packages): ''' List the files that belong to a package. Not specifying any packages will return a list of _every_ file on the system's package database (not generally recommended). CLI Examples: .. code-block:: bash salt '*' pkg.file_list httpd salt '*' pkg.file_list httpd postfix salt '*' pkg.file_list ''' ret = file_dict(*packages) files = [] for pkg_files in ret['files'].values(): files.extend(pkg_files) ret['files'] = files return ret def file_dict(*packages): ''' List the files that belong to a package, grouped by package. Not specifying any packages will return a list of _every_ file on the system's package database (not generally recommended). CLI Examples: .. code-block:: bash salt '*' pkg.file_list httpd salt '*' pkg.file_list httpd postfix salt '*' pkg.file_list ''' errors = [] files = {} if _check_pkgng(): if packages: match_pattern = '%n ~ {0}' matches = [match_pattern.format(p) for p in packages] cmd = '{0} query -e \'{1}\' \'%n %Fp\''.format( _cmd('pkg'), ' || '.join(matches)) else: cmd = '{0} query -a \'%n %Fp\''.format(_cmd('pkg')) for line in __salt__['cmd.run_stdout'](cmd).splitlines(): pkg, fn = line.split(' ', 1) if pkg not in files: files[pkg] = [] files[pkg].append(fn) else: if packages: match_pattern = '\'{0}-[0-9]*\'' matches = [match_pattern.format(p) for p in packages] cmd = '{0} -QL {1}'.format(_cmd('pkg_info'), ' '.join(matches)) else: cmd = '{0} -QLa'.format(_cmd('pkg_info')) ret = __salt__['cmd.run_all'](cmd) for line in ret['stderr'].splitlines(): errors.append(line) pkg = None for line in ret['stdout'].splitlines(): if pkg is not None and line.startswith('/'): files[pkg].append(line) elif ':/' in line: pkg, fn = line.split(':', 1) pkg, ver = pkg.rsplit('-', 1) files[pkg] = [fn] else: continue # unexpected string return {'errors': errors, 'files': files} salt-0.17.5+ds.orig/salt/modules/keystone.py0000644000175000017500000007134512270576114017071 0ustar joejoe# -*- coding: utf-8 -*- ''' Module for handling openstack keystone calls. :optdepends: - keystoneclient Python adapter :configuration: This module is not usable until the following are specified either in a pillar or in the minion's config file:: keystone.user: admin keystone.password: verybadpass keystone.tenant: admin keystone.tenant_id: f80919baedab48ec8931f200c65a50df keystone.insecure: False #(optional) keystone.auth_url: 'http://127.0.0.1:5000/v2.0/' OR (for token based authentication) keystone.token: 'ADMIN' keystone.endpoint: 'http://127.0.0.1:35357/v2.0' If configuration for multiple openstack accounts is required, they can be set up as different configuration profiles: For example:: openstack1: keystone.user: admin keystone.password: verybadpass keystone.tenant: admin keystone.tenant_id: f80919baedab48ec8931f200c65a50df keystone.auth_url: 'http://127.0.0.1:5000/v2.0/' openstack2: keystone.user: admin keystone.password: verybadpass keystone.tenant: admin keystone.tenant_id: f80919baedab48ec8931f200c65a50df keystone.auth_url: 'http://127.0.0.2:5000/v2.0/' With this configuration in place, any of the keystone functions can make use of a configuration profile by declaring it explicitly. For example:: salt '*' keystone.tenant_list profile=openstack1 ''' # Import third party libs HAS_KEYSTONE = False try: from keystoneclient.v2_0 import client import keystoneclient.exceptions HAS_KEYSTONE = True except ImportError: pass def __virtual__(): ''' Only load this module if keystone is installed on this minion. ''' if HAS_KEYSTONE: return 'keystone' return False __opts__ = {} def auth(profile=None): ''' Set up keystone credentials Only intended to be used within Keystone-enabled modules ''' if profile: user = __salt__['config.get']('{0}:keystone.user'.format(profile), 'admin') password = __salt__['config.get']('{0}:keystone.password'.format(profile), 'ADMIN') tenant = __salt__['config.get']('{0}:keystone.tenant'.format(profile), 'admin') tenant_id = __salt__['config.get']('{0}:keystone.tenant_id'.format(profile)) auth_url = __salt__['config.get']('{0}:keystone.auth_url'.format(profile), 'http://127.0.0.1:35357/v2.0/') insecure = __salt__['config.get']('{0}:keystone.insecure'.format(profile), False) token = __salt__['config.get']('{0}:keystone.token'.format(profile)) endpoint = __salt__['config.get']('{0}:keystone.endpoint'.format(profile), 'http://127.0.0.1:35357/v2.0') else: user = __salt__['config.get']('keystone.user', 'admin') password = __salt__['config.get']('keystone.password', 'ADMIN') tenant = __salt__['config.get']('keystone.tenant', 'admin') tenant_id = __salt__['config.get']('keystone.tenant_id') auth_url = __salt__['config.get']('keystone.auth_url', 'http://127.0.0.1:35357/v2.0/') insecure = __salt__['config.get']('keystone.insecure', False) token = __salt__['config.get']('keystone.token') endpoint = __salt__['config.get']('keystone.endpoint', 'http://127.0.0.1:35357/v2.0') kwargs = {} if token: kwargs = {'token': token, 'endpoint': endpoint} else: kwargs = {'username': user, 'password': password, 'tenant_name': tenant, 'tenant_id': tenant_id, 'auth_url': auth_url} # 'insecure' keyword not supported by all v2.0 keystone clients # this ensures it's only passed in when defined if insecure: kwargs[insecure] = True return client.Client(**kwargs) def ec2_credentials_create(user_id=None, name=None, tenant_id=None, tenant=None, profile=None): ''' Create EC2-compatibile credentials for user per tenant CLI Examples: .. code-block:: bash salt '*' keystone.ec2_credentials_create name=admin tenant=admin salt '*' keystone.ec2_credentials_create \ user_id=c965f79c4f864eaaa9c3b41904e67082 \ tenant_id=722787eb540849158668370dc627ec5f ''' kstone = auth(profile) if name: user_id = user_get(name=name)[name]['id'] if not user_id: return {'Error': 'Could not resolve User ID'} if tenant: tenant_id = tenant_get(name=tenant)[tenant]['id'] if not tenant_id: return {'Error': 'Could not resolve Tenant ID'} newec2 = kstone.ec2.create(user_id, tenant_id) return {'access': newec2.access, 'secret': newec2.secret, 'tenant_id': newec2.tenant_id, 'user_id': newec2.user_id} def ec2_credentials_delete(user_id=None, name=None, access_key=None, profile=None): ''' Delete EC2-compatibile credentials CLI Examples: .. code-block:: bash salt '*' keystone.ec2_credentials_delete \ 860f8c2c38ca4fab989f9bc56a061a64 access_key=5f66d2f24f604b8bb9cd28886106f442 salt '*' keystone.ec2_credentials_delete name=admin \ access_key=5f66d2f24f604b8bb9cd28886106f442 ''' kstone = auth(profile) if name: user_id = user_get(name=name)[name]['id'] if not user_id: return {'Error': 'Could not resolve User ID'} kstone.ec2.delete(user_id, access_key) return 'ec2 key "{0}" deleted under user id "{1}"'.format(access_key, user_id) def ec2_credentials_get(user_id=None, name=None, access=None, profile=None): ''' Return ec2_credentials for a user (keystone ec2-credentials-get) CLI Examples: .. code-block:: bash salt '*' keystone.ec2_credentials_get c965f79c4f864eaaa9c3b41904e67082 access=722787eb540849158668370dc627ec5f salt '*' keystone.ec2_credentials_get user_id=c965f79c4f864eaaa9c3b41904e67082 access=722787eb540849158668370dc627ec5f salt '*' keystone.ec2_credentials_get name=nova access=722787eb540849158668370dc627ec5f ''' kstone = auth(profile) ret = {} if name: for user in kstone.users.list(): if user.name == name: user_id = user.id break if not user_id: return {'Error': 'Unable to resolve user id'} if not access: return {'Error': 'Access key is required'} ec2_credentials = kstone.ec2.get(user_id=user_id, access=access) ret[ec2_credentials.user_id] = {'user_id': ec2_credentials.user_id, 'tenant': ec2_credentials.tenant_id, 'access': ec2_credentials.access, 'secret': ec2_credentials.secret} return ret def ec2_credentials_list(user_id=None, name=None, profile=None): ''' Return a list of ec2_credentials for a specific user (keystone ec2-credentials-list) CLI Examples: .. code-block:: bash salt '*' keystone.ec2_credentials_list 298ce377245c4ec9b70e1c639c89e654 salt '*' keystone.ec2_credentials_list user_id=298ce377245c4ec9b70e1c639c89e654 salt '*' keystone.ec2_credentials_list name=jack ''' kstone = auth(profile) ret = {} if name: for user in kstone.users.list(): if user.name == name: user_id = user.id break if not user_id: return {'Error': 'Unable to resolve user id'} for ec2_credential in kstone.ec2.list(user_id): ret[ec2_credential.user_id] = {'user_id': ec2_credential.user_id, 'tenant_id': ec2_credential.tenant_id, 'access': ec2_credential.access, 'secret': ec2_credential.secret} return ret def endpoint_get(service, profile=None): ''' Return a specific endpoint (keystone endpoint-get) CLI Example: .. code-block:: bash salt '*' keystone.endpoint_get ec2 ''' kstone = auth(profile) return kstone.service_catalog.url_for(service_type=service) def endpoint_list(profile=None): ''' Return a list of available endpoints (keystone endpoints-list) CLI Example: .. code-block:: bash salt '*' keystone.endpoint_list ''' kstone = auth(profile) ret = {} for endpoint in kstone.endpoints.list(): ret[endpoint.id] = {'id': endpoint.id, 'region': endpoint.region, 'adminurl': endpoint.adminurl, 'internalurl': endpoint.internalurl, 'publicurl': endpoint.publicurl, 'service_id': endpoint.service_id} return ret def role_create(name, profile=None): ''' Create named role .. code-block:: bash salt '*' keystone.role_create admin ''' kstone = auth(profile) if 'Error' not in role_get(name=name): return {'Error': 'Role "{0}" already exists'.format(name)} role = kstone.roles.create(name) return role_get(name=name) def role_delete(role_id=None, name=None, profile=None): ''' Delete a role (keystone role-delete) CLI Examples: .. code-block:: bash salt '*' keystone.role_delete c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.role_delete role_id=c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.role_delete name=admin ''' kstone = auth(profile) if name: for role in kstone.roles.list(): if role.name == name: role_id = role.id break if not role_id: return {'Error': 'Unable to resolve role id'} role = role_get(role_id) kstone.roles.delete(role) ret = 'Role ID {0} deleted'.format(role_id) if name: ret += ' ({0})'.format(name) return ret def role_get(role_id=None, name=None, profile=None): ''' Return a specific roles (keystone role-get) CLI Examples: .. code-block:: bash salt '*' keystone.role_get c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.role_get role_id=c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.role_get name=nova ''' kstone = auth(profile) ret = {} if name: for role in kstone.roles.list(): if role.name == name: role_id = role.id break if not role_id: return {'Error': 'Unable to resolve role id'} role = kstone.roles.get(role_id) ret[role.name] = {'id': role.id, 'name': role.name} return ret def role_list(profile=None): ''' Return a list of available roles (keystone role-list) CLI Example: .. code-block:: bash salt '*' keystone.role_list ''' kstone = auth(profile) ret = {} for role in kstone.roles.list(): ret[role.name] = {'id': role.id, 'name': role.name} return ret def service_create(name, service_type, description=None, profile=None): ''' Add service to Keystone service catalog CLI Examples: .. code-block:: bash salt '*' keystone.service_create nova compute \ 'OpenStack Compute Service' ''' kstone = auth(profile) service = kstone.services.create(name, service_type, description) return service_get(service.id) def service_delete(service_id=None, name=None, profile=None): ''' Delete a service from Keystone service catalog CLI Examples: .. code-block:: bash salt '*' keystone.service_delete c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.service_delete name=nova ''' kstone = auth(profile) if name: service_id = service_get(name=name)[name]['id'] service = kstone.services.delete(service_id) return 'Keystone service ID "{0}" deleted'.format(service_id) def service_get(service_id=None, name=None, profile=None): ''' Return a specific services (keystone service-get) CLI Examples: .. code-block:: bash salt '*' keystone.service_get c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.service_get service_id=c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.service_get name=nova ''' kstone = auth(profile) ret = {} if name: for service in kstone.services.list(): if service.name == name: service_id = service.id break if not service_id: return {'Error': 'Unable to resolve service id'} service = kstone.services.get(service_id) ret[service.name] = {'id': service.id, 'name': service.name, 'type': service.type, 'description': service.description} return ret def service_list(profile=None): ''' Return a list of available services (keystone services-list) CLI Example: .. code-block:: bash salt '*' keystone.service_list ''' kstone = auth(profile) ret = {} for service in kstone.services.list(): ret[service.name] = {'id': service.id, 'name': service.name, 'description': service.description, 'type': service.type} return ret def tenant_create(name, description=None, enabled=True, profile=None): ''' Create a keystone tenant CLI Examples: .. code-block:: bash salt '*' keystone.tenant_create nova description='nova tenant' salt '*' keystone.tenant_create test enabled=False ''' kstone = auth(profile) new = kstone.tenants.create(name, description, enabled) return tenant_get(new.id) def tenant_delete(tenant_id=None, name=None, profile=None): ''' Delete a tenant (keystone tenant-delete) CLI Examples: .. code-block:: bash salt '*' keystone.tenant_delete c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.tenant_delete tenant_id=c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.tenant_delete name=demo ''' kstone = auth(profile) if name: for tenant in kstone.tenants.list(): if tenant.name == name: tenant_id = tenant.id break if not tenant_id: return {'Error': 'Unable to resolve tenant id'} kstone.tenants.delete(tenant_id) ret = 'Tenant ID {0} deleted'.format(tenant_id) if name: ret += ' ({0})'.format(name) return ret def tenant_get(tenant_id=None, name=None, profile=None): ''' Return a specific tenants (keystone tenant-get) CLI Examples: .. code-block:: bash salt '*' keystone.tenant_get c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.tenant_get tenant_id=c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.tenant_get name=nova ''' kstone = auth(profile) ret = {} if name: for tenant in kstone.tenants.list(): if tenant.name == name: tenant_id = tenant.id break if not tenant_id: return {'Error': 'Unable to resolve tenant id'} tenant = kstone.tenants.get(tenant_id) ret[tenant.name] = {'id': tenant.id, 'name': tenant.name, 'description': tenant.description, 'enabled': tenant.enabled} return ret def tenant_list(profile=None): ''' Return a list of available tenants (keystone tenants-list) CLI Example: .. code-block:: bash salt '*' keystone.tenant_list ''' kstone = auth(profile) ret = {} for tenant in kstone.tenants.list(): ret[tenant.name] = {'id': tenant.id, 'name': tenant.name, 'description': tenant.description, 'enabled': tenant.enabled} return ret def tenant_update(tenant_id=None, name=None, email=None, enabled=None, profile=None): ''' Update a tenant's information (keystone tenant-update) The following fields may be updated: name, email, enabled. Can only update name if targeting by ID CLI Examples: .. code-block:: bash salt '*' keystone.tenant_update name=admin enabled=True salt '*' keystone.tenant_update c965f79c4f864eaaa9c3b41904e67082 name=admin email=admin@domain.com ''' kstone = auth(profile) if not tenant_id: for tenant in kstone.tenants.list(): if tenant.name == name: tenant_id = tenant.id break if not tenant_id: return {'Error': 'Unable to resolve tenant id'} tenant = kstone.tenants.get(tenant_id) if not name: name = tenant.name if not email: email = tenant.email if enabled is None: enabled = tenant.enabled kstone.tenants.update(tenant_id, name, email, enabled) def token_get(profile=None): ''' Return the configured tokens (keystone token-get) CLI Example: .. code-block:: bash salt '*' keystone.token_get c965f79c4f864eaaa9c3b41904e67082 ''' kstone = auth(profile) token = kstone.service_catalog.get_token() return {'id': token['id'], 'expires': token['expires'], 'user_id': token['user_id'], 'tenant_id': token['tenant_id']} def user_list(profile=None): ''' Return a list of available users (keystone user-list) CLI Example: .. code-block:: bash salt '*' keystone.user_list ''' kstone = auth(profile) ret = {} for user in kstone.users.list(): ret[user.name] = {'id': user.id, 'name': user.name, 'email': user.email, 'enabled': user.enabled} if hasattr(user.__dict__, 'tenantId'): ret[user.name]['tenant_id'] = user.__dict__['tenantId'] return ret def user_get(user_id=None, name=None, profile=None): ''' Return a specific users (keystone user-get) CLI Examples: .. code-block:: bash salt '*' keystone.user_get c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.user_get user_id=c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.user_get name=nova ''' kstone = auth(profile) ret = {} if name: for user in kstone.users.list(): if user.name == name: user_id = user.id break if not user_id: return {'Error': 'Unable to resolve user id'} user = kstone.users.get(user_id) ret[user.name] = {'id': user.id, 'name': user.name, 'email': user.email, 'enabled': user.enabled} if hasattr(user.__dict__, 'tenantId'): ret[user.name]['tenant_id'] = user.__dict__['tenantId'] return ret def user_create(name, password, email, tenant_id=None, enabled=True, profile=None): ''' Create a user (keystone user-create) CLI Examples: .. code-block:: bash salt '*' keystone.user_create name=jack password=zero email=jack@halloweentown.org tenant_id=a28a7b5a999a455f84b1f5210264375e enabled=True ''' kstone = auth(profile) item = kstone.users.create(name=name, password=password, email=email, tenant_id=tenant_id, enabled=enabled) return user_get(item.id) def user_delete(user_id=None, name=None, profile=None): ''' Delete a user (keystone user-delete) CLI Examples: .. code-block:: bash salt '*' keystone.user_delete c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.user_delete user_id=c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.user_delete name=nova ''' kstone = auth(profile) if name: for user in kstone.users.list(): if user.name == name: user_id = user.id break if not user_id: return {'Error': 'Unable to resolve user id'} kstone.users.delete(user_id) ret = 'User ID {0} deleted'.format(user_id) if name: ret += ' ({0})'.format(name) return ret def user_update(user_id=None, name=None, email=None, enabled=None, tenant=None, profile=None): ''' Update a user's information (keystone user-update) The following fields may be updated: name, email, enabled, tenant. Because the name is one of the fields, a valid user id is required. CLI Examples: .. code-block:: bash salt '*' keystone.user_update user_id=c965f79c4f864eaaa9c3b41904e67082 name=newname salt '*' keystone.user_update c965f79c4f864eaaa9c3b41904e67082 name=newname email=newemail@domain.com ''' kstone = auth(profile) if not user_id: for user in kstone.users.list(): if user.name == name: user_id = user.id break if not user_id: return {'Error': 'Unable to resolve user id'} user = kstone.users.get(user_id) # Keep previous settings if not updating them if not name: name = user.name if not email: email = user.email if enabled is None: enabled = user.enabled kstone.users.update(user=user_id, name=name, email=email, enabled=enabled) if tenant: for t in kstone.tenants.list(): if t.name == tenant: tenant_id = t.id break kstone.users.update_tenant(user_id, tenant_id) ret = 'Info updated for user ID {0}'.format(user_id) return ret def user_verify_password(user_id=None, name=None, password=None, profile=None): ''' Verify a user's password CLI Examples: .. code-block:: bash salt '*' keystone.user_verify_password name=test password=foobar salt '*' keystone.user_verify_password user_id=c965f79c4f864eaaa9c3b41904e67082 password=foobar ''' kstone = auth(profile) auth_url = __salt__['config.option']('keystone.endpoint', 'http://127.0.0.1:35357/v2.0') if user_id: for user in kstone.users.list(): if user.id == user_id: name = user.name break if not name: return {'Error': 'Unable to resolve user name'} kwargs = {'username': name, 'password': password, 'auth_url': auth_url} try: userauth = client.Client(**kwargs) except keystoneclient.exceptions.Unauthorized: return False return True def user_password_update(user_id=None, name=None, password=None, profile=None): ''' Update a user's password (keystone user-password-update) CLI Examples: .. code-block:: bash salt '*' keystone.user_delete c965f79c4f864eaaa9c3b41904e67082 password=12345 salt '*' keystone.user_delete user_id=c965f79c4f864eaaa9c3b41904e67082 password=12345 salt '*' keystone.user_delete name=nova password=12345 ''' kstone = auth(profile) if name: for user in kstone.users.list(): if user.name == name: user_id = user.id break if not user_id: return {'Error': 'Unable to resolve user id'} kstone.users.update_password(user=user_id, password=password) ret = 'Password updated for user ID {0}'.format(user_id) if name: ret += ' ({0})'.format(name) return ret def user_role_add(user_id=None, user=None, tenant_id=None, tenant=None, role_id=None, role=None, profile=None): ''' Add role for user in tenant (keystone user-role-add) CLI Examples: .. code-block:: bash salt '*' keystone.user_role_add \ user_id=298ce377245c4ec9b70e1c639c89e654 \ tenant_id=7167a092ece84bae8cead4bf9d15bb3b \ role_id=ce377245c4ec9b70e1c639c89e8cead4 salt '*' keystone.user_role_add user=admin tenant=admin role=admin ''' kstone = auth(profile) if user: user_id = user_get(name=user)[user]['id'] else: user = user_get(user_id).keys()[0]['name'] if not user_id: return {'Error': 'Unable to resolve user id'} if tenant: tenant_id = tenant_get(name=tenant)[tenant]['id'] else: tenant = tenant_get(tenant_id).keys()[0]['name'] if not tenant_id: return {'Error': 'Unable to resolve tenant id'} if role: role_id = role_get(name=role)[role]['id'] else: role = role_get(role_id).keys()[0]['name'] if not role_id: return {'Error': 'Unable to resolve role id'} kstone.roles.add_user_role(user_id, role_id, tenant_id) ret_msg = '"{0}" role added for user "{1}" for "{2}" tenant' return ret_msg.format(role, user, tenant) def user_role_remove(user_id=None, user=None, tenant_id=None, tenant=None, role_id=None, role=None, profile=None): ''' Remove role for user in tenant (keystone user-role-remove) CLI Examples: .. code-block:: bash salt '*' keystone.user_role_remove \ user_id=298ce377245c4ec9b70e1c639c89e654 \ tenant_id=7167a092ece84bae8cead4bf9d15bb3b \ role_id=ce377245c4ec9b70e1c639c89e8cead4 salt '*' keystone.user_role_remove user=admin tenant=admin role=admin ''' kstone = auth(profile) if user: user_id = user_get(name=user)[user]['id'] else: user = user_get(user_id).keys()[0]['name'] if not user_id: return {'Error': 'Unable to resolve user id'} if tenant: tenant_id = tenant_get(name=tenant)[tenant]['id'] else: tenant = tenant_get(tenant_id).keys()[0]['name'] if not tenant_id: return {'Error': 'Unable to resolve tenant id'} if role: role_id = role_get(name=role)[role]['id'] else: role = role_get(role_id).keys()[0]['name'] if not role_id: return {'Error': 'Unable to resolve role id'} kstone.roles.remove_user_role(user_id, role_id, tenant_id) ret_msg = '"{0}" role removed for user "{1}" under "{2}" tenant' return ret_msg.format(role, user, tenant) def user_role_list(user_id=None, tenant_id=None, user_name=None, tenant_name=None, profile=None): ''' Return a list of available user_roles (keystone user-roles-list) CLI Examples: .. code-block:: bash salt '*' keystone.user_role_list \ user_id=298ce377245c4ec9b70e1c639c89e654 \ tenant_id=7167a092ece84bae8cead4bf9d15bb3b salt '*' keystone.user_role_list user_name=admin tenant_name=admin ''' kstone = auth(profile) ret = {} if user_name: for user in kstone.users.list(): if user.name == user_name: user_id = user.id break if tenant_name: for tenant in kstone.tenants.list(): if tenant.name == tenant_name: tenant_id = tenant.id break if not user_id or not tenant_id: return {'Error': 'Unable to resolve user or tenant id'} for role in kstone.roles.roles_for_user(user=user_id, tenant=tenant_id): ret[role.name] = {'id': role.id, 'name': role.name, 'user_id': user_id, 'tenant_id': tenant_id} return ret def _item_list(profile=None): ''' Template for writing list functions Return a list of available items (keystone items-list) CLI Example: .. code-block:: bash salt '*' keystone.item_list ''' kstone = auth(profile) ret = [] for item in kstone.items.list(): ret.append(item.__dict__) #ret[item.name] = { # 'id': item.id, # 'name': item.name, # } return ret #The following is a list of functions that need to be incorporated in the #keystone module. This list should be updated as functions are added. # #endpoint-create Create a new endpoint associated with a service #endpoint-delete Delete a service endpoint #discover Discover Keystone servers and show authentication # protocols and #bootstrap Grants a new role to a new user on a new tenant, after # creating each. salt-0.17.5+ds.orig/salt/modules/puppet.py0000644000175000017500000001346112270576114016540 0ustar joejoe# -*- coding: utf-8 -*- ''' Execute puppet routines ''' # Import salt libs import salt.utils def __virtual__(): ''' Only load if puppet is installed ''' if salt.utils.which('facter'): return 'puppet' return False def _check_puppet(): ''' Checks if puppet is installed ''' # I thought about making this a virtual module, but then I realized that I # would require the minion to restart if puppet was installed after the # minion was started, and that would be rubbish salt.utils.check_or_die('puppet') def _check_facter(): ''' Checks if facter is installed ''' salt.utils.check_or_die('facter') def _format_fact(output): try: fact, value = output.split(' => ', 1) value = value.strip() except ValueError: fact = None value = None return (fact, value) class _Puppet(object): ''' Puppet helper class. Used to format command for execution. ''' def __init__(self): ''' Setup a puppet instance, based on the premis that default usage is to run 'puppet agent --test'. Configuration and run states are stored in the default locations. ''' self.subcmd = 'agent' self.subcmd_args = [] # eg. /a/b/manifest.pp self.kwargs = {'color': 'false'} # eg. --tags=apache::server self.args = [] # eg. --noop self.vardir = '/var/lib/puppet' self.confdir = '/etc/puppet' if 'Enterprise' in __salt__['cmd.run']('puppet --version'): self.vardir = '/var/opt/lib/pe-puppet' self.confdir = '/etc/puppetlabs/puppet' def __repr__(self): ''' Format the command string to executed using cmd.run_all. ''' cmd = 'puppet {subcmd} --vardir {vardir} --confdir {confdir}'.format( **self.__dict__ ) args = ' '.join(self.subcmd_args) args += ''.join( [' --{0}'.format(k) for k in self.args] # single spaces ) args += ''.join([ ' --{0} {1}'.format(k, v) for k, v in self.kwargs.items()] ) return '{0} {1}'.format(cmd, args) def arguments(self, args=None): ''' Read in arguments for the current subcommand. These are added to the cmd line without '--' appended. Any others are redirected as standard options with the double hyphen prefixed. ''' # permits deleting elements rather than using slices args = args and list(args) or [] # match against all known/supported subcmds if self.subcmd == 'apply': # apply subcommand requires a manifest file to execute self.subcmd_args = [args[0]] del args[0] if self.subcmd == 'agent': # no arguments are required args.extend([ 'onetime', 'verbose', 'ignorecache', 'no-daemonize', 'no-usecacheonfailure', 'no-splay', 'show_diff' ]) # finally do this after subcmd has been matched for all remaining args self.args = args def run(*args, **kwargs): ''' Execute a puppet run and return a dict with the stderr, stdout, return code, etc. The first positional argument given is checked as a subcommand. Following positional arguments should be ordered with arguments required by the subcommand first, followed by non-keyvalue pair options. Tags are specified by a tag keyword and comma separated list of values. -- http://projects.puppetlabs.com/projects/1/wiki/Using_Tags CLI Examples: .. code-block:: bash salt '*' puppet.run salt '*' puppet.run tags=basefiles::edit,apache::server salt '*' puppet.run agent onetime no-daemonize no-usecacheonfailure no-splay ignorecache salt '*' puppet.run debug salt '*' puppet.run apply /a/b/manifest.pp modulepath=/a/b/modules tags=basefiles::edit,apache::server ''' _check_puppet() puppet = _Puppet() if args: # based on puppet documentation action must come first. making the same # assertion. need to ensure the list of supported cmds here matches # those defined in _Puppet.arguments() if args[0] in ['agent', 'apply']: puppet.subcmd = args[0] puppet.arguments(args[1:]) else: # args will exist as an empty list even if none have been provided puppet.arguments(args) puppet.kwargs.update(salt.utils.clean_kwargs(**kwargs)) return __salt__['cmd.run_all'](repr(puppet)) def noop(*args, **kwargs): ''' Execute a puppet noop run and return a dict with the stderr, stdout, return code, etc. Usage is the same as for puppet.run. CLI Example: .. code-block:: bash salt '*' puppet.noop salt '*' puppet.noop tags=basefiles::edit,apache::server salt '*' puppet.noop debug salt '*' puppet.noop apply /a/b/manifest.pp modulepath=/a/b/modules tags=basefiles::edit,apache::server ''' args += ('noop',) return run(*args, **kwargs) def facts(): ''' Run facter and return the results CLI Example: .. code-block:: bash salt '*' puppet.facts ''' _check_facter() ret = {} output = __salt__['cmd.run']('facter') # Loop over the facter output and properly # parse it into a nice dictionary for using # elsewhere for line in output.splitlines(): if not line: continue fact, value = _format_fact(line) if not fact: continue ret[fact] = value return ret def fact(name): ''' Run facter for a specific fact CLI Example: .. code-block:: bash salt '*' puppet.fact kernel ''' _check_facter() ret = __salt__['cmd.run']('facter {0}'.format(name)) if not ret: return '' return ret salt-0.17.5+ds.orig/salt/modules/yumpkg.py0000644000175000017500000011750712270576114016545 0ustar joejoe# -*- coding: utf-8 -*- ''' Support for YUM :depends: - yum Python module - rpmUtils Python module This module uses the python interface to YUM. Note that with a default /etc/yum.conf, this will cause messages to be sent to sent to syslog on /dev/log, with a log facility of :strong:`LOG_USER`. This is in addition to whatever is logged to /var/log/yum.log. See the manpage for ``yum.conf(5)`` for information on how to use the ``syslog_facility`` and ``syslog_device`` config parameters to configure how syslog is handled, or take the above defaults into account when configuring your syslog daemon. ''' # Import python libs import copy import logging import os import re import yaml # Import salt libs import salt.utils from salt.exceptions import CommandExecutionError, SaltInvocationError # Import third party libs try: import yum import yum.logginglevels import rpmUtils.arch HAS_YUMDEPS = True class _YumLogger(yum.rpmtrans.RPMBaseCallback): ''' A YUM callback handler that logs failed packages with their associated script output to the minion log, and logs install/remove/update/etc. activity to the yum log (usually /var/log/yum.log). See yum.rpmtrans.NoOutputCallBack in the yum package for base implementation. ''' def __init__(self): yum.rpmtrans.RPMBaseCallback.__init__(self) self.messages = {} self.failed = [] self.action = { yum.constants.TS_UPDATE: yum._('Updating'), yum.constants.TS_ERASE: yum._('Erasing'), yum.constants.TS_INSTALL: yum._('Installing'), yum.constants.TS_TRUEINSTALL: yum._('Installing'), yum.constants.TS_OBSOLETED: yum._('Obsoleted'), yum.constants.TS_OBSOLETING: yum._('Installing'), yum.constants.TS_UPDATED: yum._('Cleanup'), 'repackaging': yum._('Repackaging') } # The fileaction are not translated, most sane IMHO / Tim self.fileaction = { yum.constants.TS_UPDATE: 'Updated', yum.constants.TS_ERASE: 'Erased', yum.constants.TS_INSTALL: 'Installed', yum.constants.TS_TRUEINSTALL: 'Installed', yum.constants.TS_OBSOLETED: 'Obsoleted', yum.constants.TS_OBSOLETING: 'Installed', yum.constants.TS_UPDATED: 'Cleanup' } self.logger = logging.getLogger( 'yum.filelogging.RPMInstallCallback') def event(self, package, action, te_current, te_total, ts_current, ts_total): # This would be used for a progress counter according to Yum docs pass def log_accumulated_errors(self): ''' Convenience method for logging all messages from failed packages ''' for pkg in self.failed: log.error('{0} {1}'.format(pkg, self.messages[pkg])) def errorlog(self, msg): # Log any error we receive log.error(msg) def filelog(self, package, action): if action == yum.constants.TS_FAILED: self.failed.append(package) else: if action in self.fileaction: msg = '{0}: {1}'.format(self.fileaction[action], package) else: msg = '{0}: {1}'.format(package, action) self.logger.info(msg) def scriptout(self, package, msgs): # This handler covers ancillary messages coming from the RPM script # Will sometimes contain more detailed error messages. self.messages[package] = msgs class _YumBase(yum.YumBase): def doLoggingSetup(self, debuglevel, errorlevel, syslog_indent=None, syslog_facility=None, syslog_device='/dev/log'): ''' This method is overridden in salt because we don't want syslog logging to happen. Additionally, no logging will be setup for yum. The logging handlers configure for yum were to ``sys.stdout``, ``sys.stderr`` and ``syslog``. We don't want none of those. Any logging will go through salt's logging handlers. ''' # Just set the log levels to yum if debuglevel is not None: logging.getLogger('yum.verbose').setLevel( yum.logginglevels.logLevelFromDebugLevel(debuglevel) ) if errorlevel is not None: logging.getLogger('yum.verbose').setLevel( yum.logginglevels.logLevelFromErrorLevel(errorlevel) ) logging.getLogger('yum.filelogging').setLevel(logging.INFO) except (ImportError, AttributeError): HAS_YUMDEPS = False log = logging.getLogger(__name__) def __virtual__(): ''' Confine this module to yum based systems ''' if not HAS_YUMDEPS: return False # Work only on RHEL/Fedora based distros with python 2.6 or greater # TODO: Someone decide if we can just test os_family and pythonversion os_grain = __grains__['os'] os_family = __grains__['os_family'] try: os_major = int(__grains__['osrelease'].split('.')[0]) except ValueError: os_major = 0 if os_grain == 'Fedora': # Fedora <= 10 used Python 2.5 and below if os_major >= 11: return 'pkg' elif os_grain == 'XCP': if os_major >= 2: return 'pkg' elif os_grain == 'XenServer': if os_major > 6: return 'pkg' elif os_family == 'RedHat' and os_major >= 6: return 'pkg' return False def list_upgrades(refresh=True): ''' Check whether or not an upgrade is available for all packages CLI Example: .. code-block:: bash salt '*' pkg.list_upgrades ''' if salt.utils.is_true(refresh): refresh_db() pkgs = list_pkgs() yumbase = _YumBase() versions_list = {} for pkgtype in ['updates']: pkglist = yumbase.doPackageLists(pkgtype) for pkg in pkgs: exactmatch, matched, unmatched = yum.packages.parsePackages( pkglist, [pkg] ) for pkg in exactmatch: if pkg.arch in rpmUtils.arch.legitMultiArchesInSameLib() \ or pkg.arch == 'noarch': versions_list[pkg['name']] = '-'.join( [pkg['version'], pkg['release']] ) return versions_list def _set_repo_options(yumbase, **kwargs): ''' Accepts a _YumBase() object and runs member functions to enable/disable repos as needed. ''' # Get repo options from the kwargs fromrepo = kwargs.get('fromrepo', '') repo = kwargs.get('repo', '') disablerepo = kwargs.get('disablerepo', '') enablerepo = kwargs.get('enablerepo', '') # Support old 'repo' argument if repo and not fromrepo: fromrepo = repo try: if fromrepo: log.info('Restricting to repo {0!r}'.format(fromrepo)) yumbase.repos.disableRepo('*') yumbase.repos.enableRepo(fromrepo) else: if disablerepo: log.info('Disabling repo {0!r}'.format(disablerepo)) yumbase.repos.disableRepo(disablerepo) if enablerepo: log.info('Enabling repo {0!r}'.format(enablerepo)) yumbase.repos.enableRepo(enablerepo) except yum.Errors.RepoError as exc: return exc def _pkg_arch(name): ''' Returns a 2-tuple of the name and arch parts of the passed string. Note that packages that are for the system architecture should not have the architecture specified in the passed string. ''' all_arches = rpmUtils.arch.getArchList() if not any(name.endswith('.{0}'.format(x)) for x in all_arches): return name, __grains__['cpuarch'] try: pkgname, pkgarch = name.rsplit('.', 1) except ValueError: return name, __grains__['cpuarch'] else: return pkgname, pkgarch def latest_version(*names, **kwargs): ''' Return the latest version of the named package available for upgrade or installation. If more than one package name is specified, a dict of name/version pairs is returned. If the latest version of a given package is already installed, an empty string will be returned for that package. A specific repo can be requested using the ``fromrepo`` keyword argument. CLI Example: .. code-block:: bash salt '*' pkg.latest_version salt '*' pkg.latest_version fromrepo=epel-testing salt '*' pkg.latest_version ... ''' refresh = salt.utils.is_true(kwargs.pop('refresh', True)) # FIXME: do stricter argument checking that somehow takes # _get_repo_options() into account if len(names) == 0: return '' ret = {} namearch_map = {} # Initialize the return dict with empty strings, and populate the namearch # dict for name in names: ret[name] = '' pkgname, pkgarch = _pkg_arch(name) namearch_map.setdefault(name, {})['name'] = pkgname namearch_map[name]['arch'] = pkgarch # Refresh before looking for the latest version available if refresh: refresh_db() yumbase = _YumBase() error = _set_repo_options(yumbase, **kwargs) if error: log.error(error) suffix_notneeded = rpmUtils.arch.legitMultiArchesInSameLib() + ['noarch'] # look for available packages only, if package is already installed with # latest version it will not show up here. If we want to use wildcards # here we can, but for now its exact match only. for pkgtype in ('available', 'updates'): pkglist = yumbase.doPackageLists(pkgtype) exactmatch, matched, unmatched = yum.packages.parsePackages( pkglist, [namearch_map[x]['name'] for x in names] ) for name in names: for pkg in (x for x in exactmatch if x.name == namearch_map[name]['name']): if (all(x in suffix_notneeded for x in (namearch_map[name]['arch'], pkg.arch)) or namearch_map[name]['arch'] == pkg.arch): ret[name] = '-'.join([pkg.version, pkg.release]) # Return a string if only one package name passed if len(names) == 1: return ret[names[0]] return ret # available_version is being deprecated available_version = latest_version def upgrade_available(name): ''' Check whether or not an upgrade is available for a given package CLI Example: .. code-block:: bash salt '*' pkg.upgrade_available ''' return latest_version(name) != '' def version(*names, **kwargs): ''' Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. CLI Example: .. code-block:: bash salt '*' pkg.version salt '*' pkg.version ... ''' return __salt__['pkg_resource.version'](*names, **kwargs) def list_pkgs(versions_as_list=False, **kwargs): ''' List the packages currently installed in a dict:: {'': ''} CLI Example: .. code-block:: bash salt '*' pkg.list_pkgs ''' versions_as_list = salt.utils.is_true(versions_as_list) # 'removed' not yet implemented or not applicable if salt.utils.is_true(kwargs.get('removed')): return {} if 'pkg.list_pkgs' in __context__: if versions_as_list: return __context__['pkg.list_pkgs'] else: ret = copy.deepcopy(__context__['pkg.list_pkgs']) __salt__['pkg_resource.stringify'](ret) return ret ret = {} yb = _YumBase() for p in yb.rpmdb: name = p.name if __grains__.get('cpuarch', '') == 'x86_64' \ and re.match(r'i\d86', p.arch): name += '.{0}'.format(p.arch) pkgver = p.version if p.release: pkgver += '-{0}'.format(p.release) __salt__['pkg_resource.add_pkg'](ret, name, pkgver) __salt__['pkg_resource.sort_pkglist'](ret) __context__['pkg.list_pkgs'] = copy.deepcopy(ret) if not versions_as_list: __salt__['pkg_resource.stringify'](ret) return ret def check_db(*names, **kwargs): ''' .. versionadded:: 0.17.0 Returns a dict containing the following information for each specified package: 1. A key ``found``, which will be a boolean value denoting if a match was found in the package database. 2. If ``found`` is ``False``, then a second key called ``suggestions`` will be present, which will contain a list of possible matches. The ``fromrepo``, ``enablerepo``, and ``disablerepo`` arguments are supported, as used in pkg states. CLI Examples: .. code-block:: bash salt '*' pkg.check_db salt '*' pkg.check_db fromrepo=epel-testing ''' yumbase = _YumBase() error = _set_repo_options(yumbase, **kwargs) if error: log.error(error) return {} ret = {} for name in names: pkgname, pkgarch = _pkg_arch(name) ret.setdefault(name, {})['found'] = bool( [x for x in yumbase.searchPackages(('name', 'arch'), (pkgname,)) if x.name == pkgname and x.arch in (pkgarch, 'noarch')] ) if ret[name]['found'] is False: provides = [ x for x in yumbase.whatProvides( pkgname, None, None ).returnPackages() if x.arch in (pkgarch, 'noarch') ] if provides: for pkg in provides: ret[name].setdefault('suggestions', []).append(pkg.name) else: ret[name]['suggestions'] = [] return ret def refresh_db(): ''' Since yum refreshes the database automatically, this runs a yum clean, so that the next yum operation will have a clean database CLI Example: .. code-block:: bash salt '*' pkg.refresh_db ''' yumbase = _YumBase() yumbase.cleanMetadata() return True def clean_metadata(): ''' Cleans local yum metadata. CLI Example: .. code-block:: bash salt '*' pkg.clean_metadata ''' return refresh_db() def group_install(name=None, groups=None, skip=None, include=None, **kwargs): ''' Install the passed package group(s). This is basically a wrapper around pkg.install, which performs package group resolution for the user. This function is currently considered "experimental", and should be expected to undergo changes before it becomes official. name The name of a single package group to install. Note that this option is ignored if "groups" is passed. groups The names of multiple packages which are to be installed. CLI Example: .. code-block:: bash salt '*' pkg.group_install groups='["Group 1", "Group 2"]' skip The name(s), in a list, of any packages that would normally be installed by the package group ("default" packages), which should not be installed. CLI Examples: .. code-block:: bash salt '*' pkg.group_install 'My Group' skip='["foo", "bar"]' include The name(s), in a list, of any packages which are included in a group, which would not normally be installed ("optional" packages). Note that this will nor enforce group membership; if you include packages which are not members of the specified groups, they will still be installed. CLI Examples: .. code-block:: bash salt '*' pkg.group_install 'My Group' include='["foo", "bar"]' other arguments Because this is essentially a wrapper around pkg.install, any argument which can be passed to pkg.install may also be included here, and it will be passed along wholesale. ''' pkg_groups = [] if groups: pkg_groups = yaml.safe_load(groups) else: pkg_groups.append(name) skip_pkgs = [] if skip: skip_pkgs = yaml.safe_load(skip) include = [] if include: include = yaml.safe_load(include) pkgs = [] for group in pkg_groups: group_detail = group_info(group) for package in group_detail.get('mandatory packages', {}): pkgs.append(package) for package in group_detail.get('default packages', {}): if package not in skip_pkgs: pkgs.append(package) for package in include: pkgs.append(package) install_pkgs = yaml.safe_dump(pkgs) return install(pkgs=install_pkgs, **kwargs) def install(name=None, refresh=False, skip_verify=False, pkgs=None, sources=None, **kwargs): ''' Install the passed package(s), add refresh=True to clean the yum database 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. 32-bit packages can be installed on 64-bit systems by appending the architecture designation (``.i686``, ``.i586``, etc.) to the end of the package name. CLI Example: .. code-block:: bash salt '*' pkg.install refresh Whether or not to update the yum database before executing. skip_verify Skip the GPG verification check. (e.g., ``--nogpgcheck``) version Install a specific version of the package, e.g. 1.2.3-4.el6. Ignored if "pkgs" or "sources" is passed. Repository Options: fromrepo Specify a package repository (or repositories) from which to install. (e.g., ``yum --disablerepo='*' --enablerepo='somerepo'``) enablerepo Specify a disabled package repository (or repositories) to enable. (e.g., ``yum --enablerepo='somerepo'``) disablerepo Specify an enabled package repository (or repositories) to disable. (e.g., ``yum --disablerepo='somerepo'``) 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. CLI Examples: .. code-block:: bash salt '*' pkg.install pkgs='["foo", "bar"]' salt '*' pkg.install pkgs='["foo", {"bar": "1.2.3-4.el6"}]' 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"}]' Returns a dict containing the new package names and versions:: {'': {'old': '', 'new': ''}} ''' if salt.utils.is_true(refresh): refresh_db() pkg_params, pkg_type = __salt__['pkg_resource.parse_targets'](name, pkgs, sources, **kwargs) if pkg_params is None or len(pkg_params) == 0: return {} old = list_pkgs() yumbase = _YumBase() setattr(yumbase.conf, 'assumeyes', True) setattr(yumbase.conf, 'gpgcheck', not skip_verify) version = kwargs.get('version') if version: if pkgs is None and sources is None: # Allow "version" to work for single package target pkg_params = {name: version} else: log.warning('"version" parameter will be ignored for multiple ' 'package targets') error = _set_repo_options(yumbase, **kwargs) if error: log.error(error) return {} try: for pkgname in pkg_params: if pkg_type == 'file': log.info( 'Selecting "{0}" for local installation'.format(pkgname) ) installed = yumbase.installLocal(pkgname) # if yum didn't install anything, maybe its a downgrade? log.debug('Added {0} transactions'.format(len(installed))) if len(installed) == 0 and pkgname not in old.keys(): log.info('Upgrade failed, trying local downgrade') yumbase.downgradeLocal(pkgname) else: version = pkg_params[pkgname] if version is not None: if __grains__.get('cpuarch', '') == 'x86_64': try: arch = re.search(r'(\.i\d86)$', pkgname).group(1) except AttributeError: arch = '' else: # Remove arch from pkgname pkgname = pkgname[:-len(arch)] else: arch = '' target = '{0}-{1}{2}'.format(pkgname, version, arch) else: target = pkgname log.info('Selecting "{0}" for installation'.format(target)) # Changed to pattern to allow specific package versions installed = yumbase.install(pattern=target) # if yum didn't install anything, maybe its a downgrade? log.debug('Added {0} transactions'.format(len(installed))) if len(installed) == 0 and target not in old.keys(): log.info('Upgrade failed, trying downgrade') yumbase.downgrade(pattern=target) # Resolve Deps before attempting install. This needs to be improved by # also tracking any deps that may get upgraded/installed during this # process. For now only the version of the package(s) you request be # installed is tracked. log.info('Resolving dependencies') yumbase.resolveDeps() log.info('Processing transaction') yumlogger = _YumLogger() yumbase.processTransaction(rpmDisplay=yumlogger) yumlogger.log_accumulated_errors() yumbase.closeRpmDB() except Exception as e: log.error('Install failed: {0}'.format(e)) __context__.pop('pkg.list_pkgs', None) new = list_pkgs() return __salt__['pkg_resource.find_changes'](old, new) def upgrade(refresh=True): ''' Run a full system upgrade, a yum upgrade Return a dict containing the new package names and versions:: {'': {'old': '', 'new': ''}} CLI Example: .. code-block:: bash salt '*' pkg.upgrade ''' if salt.utils.is_true(refresh): refresh_db() yumbase = _YumBase() setattr(yumbase.conf, 'assumeyes', True) old = list_pkgs() try: # ideally we would look in the yum transaction and get info on all the # packages that are going to be upgraded and only look up old/new # version info on those packages. yumbase.update() log.info('Resolving dependencies') yumbase.resolveDeps() log.info('Processing transaction') yumlogger = _YumLogger() yumbase.processTransaction(rpmDisplay=yumlogger) yumlogger.log_accumulated_errors() yumbase.closeRpmDB() except Exception as e: log.error('Upgrade failed: {0}'.format(e)) __context__.pop('pkg.list_pkgs', None) new = list_pkgs() return __salt__['pkg_resource.find_changes'](old, new) def remove(name=None, pkgs=None, **kwargs): ''' Removes packages using python API for yum. name The name of the package to be deleted. Multiple Package Options: pkgs A list of packages to delete. Must be passed as a python list. The ``name`` parameter will be ignored if this option is passed. .. versionadded:: 0.16.0 Returns a dict containing the changes. CLI Example: .. code-block:: bash salt '*' pkg.remove salt '*' pkg.remove ,, salt '*' pkg.remove pkgs='["foo", "bar"]' ''' pkg_params = __salt__['pkg_resource.parse_targets'](name, pkgs)[0] old = list_pkgs() targets = [x for x in pkg_params if x in old] if not targets: return {} yumbase = _YumBase() setattr(yumbase.conf, 'assumeyes', True) # same comments as in upgrade for remove. for target in targets: if __grains__.get('cpuarch', '') == 'x86_64': try: arch = re.search(r'(\.i\d86)$', target).group(1) except AttributeError: arch = None else: # Remove arch from pkgname target = target[:-len(arch)] arch = arch.lstrip('.') else: arch = None yumbase.remove(name=target, arch=arch) log.info('Performing transaction test') try: callback = yum.callbacks.ProcessTransNoOutputCallback() result = yumbase._doTestTransaction(callback) except yum.Errors.YumRPMCheckError as exc: raise CommandExecutionError('\n'.join(exc.__dict__['value'])) log.info('Resolving dependencies') yumbase.resolveDeps() log.info('Processing transaction') yumlogger = _YumLogger() yumbase.processTransaction(rpmDisplay=yumlogger) yumlogger.log_accumulated_errors() yumbase.closeRpmDB() __context__.pop('pkg.list_pkgs', None) new = list_pkgs() return __salt__['pkg_resource.find_changes'](old, new) def purge(name=None, pkgs=None, **kwargs): ''' Package purges are not supported by yum, this function is identical to ``remove()``. name The name of the package to be deleted. Multiple Package Options: pkgs A list of packages to delete. Must be passed as a python list. The ``name`` parameter will be ignored if this option is passed. .. versionadded:: 0.16.0 Returns a dict containing the changes. CLI Example: .. code-block:: bash salt '*' pkg.purge salt '*' pkg.purge ,, salt '*' pkg.purge pkgs='["foo", "bar"]' ''' return remove(name=name, pkgs=pkgs) def verify(*package): ''' Runs an rpm -Va on a system, and returns the results in a dict CLI Example: .. code-block:: bash salt '*' pkg.verify ''' return __salt__['lowpkg.verify'](*package) def group_list(): ''' Lists all groups known by yum on this system CLI Example: .. code-block:: bash salt '*' pkg.group_list ''' ret = {'installed': [], 'available': [], 'available languages': {}} yumbase = _YumBase() (installed, available) = yumbase.doGroupLists() for group in installed: ret['installed'].append(group.name) for group in available: if group.langonly: ret['available languages'][group.name] = { 'name': group.name, 'language': group.langonly} else: ret['available'].append(group.name) return ret def group_info(groupname): ''' Lists packages belonging to a certain group CLI Example: .. code-block:: bash salt '*' pkg.group_info 'Perl Support' ''' yumbase = _YumBase() (installed, available) = yumbase.doGroupLists() for group in installed + available: if group.name.lower() == groupname.lower(): return {'mandatory packages': group.mandatory_packages, 'optional packages': group.optional_packages, 'default packages': group.default_packages, 'conditional packages': group.conditional_packages, 'description': group.description} def group_diff(groupname): ''' Lists packages belonging to a certain group, and which are installed CLI Example: .. code-block:: bash salt '*' pkg.group_diff 'Perl Support' ''' ret = { 'mandatory packages': {'installed': [], 'not installed': []}, 'optional packages': {'installed': [], 'not installed': []}, 'default packages': {'installed': [], 'not installed': []}, 'conditional packages': {'installed': [], 'not installed': []}, } pkgs = list_pkgs() yumbase = _YumBase() (installed, available) = yumbase.doGroupLists() for group in installed: if group.name == groupname: for pkg in group.mandatory_packages: if pkg in pkgs: ret['mandatory packages']['installed'].append(pkg) else: ret['mandatory packages']['not installed'].append(pkg) for pkg in group.optional_packages: if pkg in pkgs: ret['optional packages']['installed'].append(pkg) else: ret['optional packages']['not installed'].append(pkg) for pkg in group.default_packages: if pkg in pkgs: ret['default packages']['installed'].append(pkg) else: ret['default packages']['not installed'].append(pkg) for pkg in group.conditional_packages: if pkg in pkgs: ret['conditional packages']['installed'].append(pkg) else: ret['conditional packages']['not installed'].append(pkg) return {groupname: ret} def list_repos(basedir='/etc/yum.repos.d'): ''' Lists all repos in (default: /etc/yum.repos.d/). CLI Example: .. code-block:: bash salt '*' pkg.list_repos ''' repos = {} for repofile in os.listdir(basedir): repopath = '{0}/{1}'.format(basedir, repofile) if not repofile.endswith('.repo'): continue header, filerepos = _parse_repo_file(repopath) for reponame in filerepos.keys(): repo = filerepos[reponame] repo['file'] = repopath repos[reponame] = repo return repos def get_repo(repo, basedir='/etc/yum.repos.d', **kwargs): ''' Display a repo from (default basedir: /etc/yum.repos.d). CLI Examples: .. code-block:: bash salt '*' pkg.get_repo myrepo salt '*' pkg.get_repo myrepo basedir=/path/to/dir ''' repos = list_repos(basedir) # Find out what file the repo lives in repofile = '' for arepo in repos.keys(): if arepo == repo: repofile = repos[arepo]['file'] if not repofile: raise Exception('repo {0} was not found in {1}'.format(repo, basedir)) # Return just one repo header, filerepos = _parse_repo_file(repofile) return filerepos[repo] def del_repo(repo, basedir='/etc/yum.repos.d', **kwargs): ''' Delete a repo from (default basedir: /etc/yum.repos.d). If the .repo file that the repo exists in does not contain any other repo configuration, the file itself will be deleted. CLI Examples: .. code-block:: bash salt '*' pkg.del_repo myrepo salt '*' pkg.del_repo myrepo basedir=/path/to/dir ''' repos = list_repos(basedir) if repo not in repos: return 'Error: the {0} repo does not exist in {1}'.format( repo, basedir) # Find out what file the repo lives in repofile = '' for arepo in repos: if arepo == repo: repofile = repos[arepo]['file'] # See if the repo is the only one in the file onlyrepo = True for arepo in repos.keys(): if arepo == repo: continue if repos[arepo]['file'] == repofile: onlyrepo = False # If this is the only repo in the file, delete the file itself if onlyrepo: os.remove(repofile) return 'File {0} containing repo {1} has been removed'.format( repofile, repo) # There must be other repos in this file, write the file with them header, filerepos = _parse_repo_file(repofile) content = header for stanza in filerepos.keys(): if stanza == repo: continue comments = '' if 'comments' in filerepos[stanza]: comments = '\n'.join(filerepos[stanza]['comments']) del filerepos[stanza]['comments'] content += '\n[{0}]'.format(stanza) for line in filerepos[stanza]: content += '\n{0}={1}'.format(line, filerepos[stanza][line]) content += '\n{0}\n'.format(comments) with salt.utils.fopen(repofile, 'w') as fileout: fileout.write(content) return 'Repo {0} has been removed from {1}'.format(repo, repofile) def mod_repo(repo, basedir=None, **kwargs): ''' Modify one or more values for a repo. If the repo does not exist, it will be created, so long as the following values are specified: repo name by which the yum refers to the repo name a human-readable name for the repo baseurl the URL for yum to reference mirrorlist the URL for yum to reference Key/Value pairs may also be removed from a repo's configuration by setting a key to a blank value. Bear in mind that a name cannot be deleted, and a baseurl can only be deleted if a mirrorlist is specified (or vice versa). CLI Examples: .. code-block:: bash salt '*' pkg.mod_repo reponame enabled=1 gpgcheck=1 salt '*' pkg.mod_repo reponame basedir=/path/to/dir enabled=1 salt '*' pkg.mod_repo reponame baseurl= mirrorlist=http://host.com/ ''' # Filter out '__pub' arguments repo_opts = dict((x, kwargs[x]) for x in kwargs if not x.startswith('__')) if all(x in repo_opts for x in ('mirrorlist', 'baseurl')): raise SaltInvocationError( 'Only one of \'mirrorlist\' and \'baseurl\' can be specified' ) # Build a list of keys to be deleted todelete = [] for key in repo_opts: if repo_opts[key] != 0 and not repo_opts[key]: del repo_opts[key] todelete.append(key) # Add baseurl or mirrorlist to the 'todelete' list if the other was # specified in the repo_opts if 'mirrorlist' in repo_opts: todelete.append('baseurl') elif 'baseurl' in repo_opts: todelete.append('mirrorlist') # Fail if the user tried to delete the name if 'name' in todelete: raise SaltInvocationError('The repo name cannot be deleted') # Give the user the ability to change the basedir repos = {} if basedir: repos = list_repos(basedir) else: repos = list_repos() basedir = '/etc/yum.repos.d' repofile = '' header = '' filerepos = {} if repo not in repos: # If the repo doesn't exist, create it in a new file repofile = '{0}/{1}.repo'.format(basedir, repo) if 'name' not in repo_opts: raise SaltInvocationError( 'The repo does not exist and needs to be created, but a name ' 'was not given' ) if 'baseurl' not in repo_opts and 'mirrorlist' not in repo_opts: raise SaltInvocationError( 'The repo does not exist and needs to be created, but either ' 'a baseurl or a mirrorlist needs to be given' ) filerepos[repo] = {} else: # The repo does exist, open its file repofile = repos[repo]['file'] header, filerepos = _parse_repo_file(repofile) # Error out if they tried to delete baseurl or mirrorlist improperly if 'baseurl' in todelete: if 'mirrorlist' not in repo_opts and 'mirrorlist' \ not in filerepos[repo].keys(): raise SaltInvocationError( 'Cannot delete baseurl without specifying mirrorlist' ) if 'mirrorlist' in todelete: if 'baseurl' not in repo_opts and 'baseurl' \ not in filerepos[repo].keys(): raise SaltInvocationError( 'Cannot delete mirrorlist without specifying baseurl' ) # Delete anything in the todelete list for key in todelete: if key in filerepos[repo].keys(): del filerepos[repo][key] # Old file or new, write out the repos(s) filerepos[repo].update(repo_opts) content = header for stanza in filerepos.keys(): comments = '' if 'comments' in filerepos[stanza].keys(): comments = '\n'.join(filerepos[stanza]['comments']) del filerepos[stanza]['comments'] content += '\n[{0}]'.format(stanza) for line in filerepos[stanza].keys(): content += '\n{0}={1}'.format(line, filerepos[stanza][line]) content += '\n{0}\n'.format(comments) with salt.utils.fopen(repofile, 'w') as fileout: fileout.write(content) return {repofile: filerepos} def _parse_repo_file(filename): ''' Turn a single repo file into a dict ''' repos = {} header = '' repo = '' with salt.utils.fopen(filename, 'r') as rfile: for line in rfile: if line.startswith('['): repo = line.strip().replace('[', '').replace(']', '') repos[repo] = {} # Even though these are essentially uselss, I want to allow the # user to maintain their own comments, etc if not line: if not repo: header += line if line.startswith('#'): if not repo: header += line else: if 'comments' not in repos[repo]: repos[repo]['comments'] = [] repos[repo]['comments'].append(line.strip()) continue # These are the actual configuration lines that matter if '=' in line: comps = line.strip().split('=') repos[repo][comps[0].strip()] = '='.join(comps[1:]) return (header, repos) def file_list(*packages): ''' List the files that belong to a package. Not specifying any packages will return a list of _every_ file on the system's rpm database (not generally recommended). CLI Examples: .. code-block:: bash salt '*' pkg.file_list httpd salt '*' pkg.file_list httpd postfix salt '*' pkg.file_list ''' return __salt__['lowpkg.file_list'](*packages) def file_dict(*packages): ''' List the files that belong to a package, grouped by package. Not specifying any packages will return a list of _every_ file on the system's rpm database (not generally recommended). CLI Examples: .. code-block:: bash salt '*' pkg.file_list httpd salt '*' pkg.file_list httpd postfix salt '*' pkg.file_list ''' return __salt__['lowpkg.file_dict'](*packages) def expand_repo_def(repokwargs): ''' Take a repository definition and expand it to the full pkg repository dict that can be used for comparison. This is a helper function to make certain repo managers sane for comparison in the pkgrepo states. There is no use to calling this function via the CLI. ''' # YUM doesn't need the data massaged. return repokwargs salt-0.17.5+ds.orig/salt/modules/linux_sysctl.py0000644000175000017500000001477012270576114017767 0ustar joejoe# -*- coding: utf-8 -*- ''' Module for viewing and modifying sysctl parameters ''' # Import python libs import logging import os import re # Import salt libs import salt.utils from salt._compat import string_types from salt.exceptions import CommandExecutionError from salt.modules.systemd import _sd_booted log = logging.getLogger(__name__) # TODO: Add unpersist() to remove either a sysctl or sysctl/value combo from # the config def __virtual__(): ''' Only run on Linux systems ''' if __grains__['kernel'] != 'Linux': return False global _sd_booted _sd_booted = salt.utils.namespaced_function(_sd_booted, globals()) return 'sysctl' def default_config(): ''' Linux hosts using systemd 207 or later ignore ``/etc/sysctl.conf`` and only load from ``/etc/sysctl.d/*.conf``. This function will do the proper checks and return a default config file which will be valid for the Minion. Hosts running systemd >= 207 will use ``/etc/sysctl.d/99-salt.conf``. CLI Example: .. code-block:: bash salt -G 'kernel:Linux' sysctl.default_config ''' if _sd_booted(): for line in __salt__['cmd.run_stdout']( 'systemctl --version' ).splitlines(): if line.startswith('systemd '): version = line.split()[-1] try: if int(version) >= 207: return '/etc/sysctl.d/99-salt.conf' except ValueError: log.error( 'Unexpected non-numeric systemd version {0!r} ' 'detected'.format(version) ) break return '/etc/sysctl.conf' def show(): ''' Return a list of sysctl parameters for this minion CLI Example: .. code-block:: bash salt '*' sysctl.show ''' cmd = 'sysctl -a' ret = {} for line in __salt__['cmd.run_stdout'](cmd).splitlines(): if not line or ' = ' not in line: continue comps = line.split(' = ', 1) ret[comps[0]] = comps[1] return ret def get(name): ''' Return a single sysctl parameter for this minion CLI Example: .. code-block:: bash salt '*' sysctl.get net.ipv4.ip_forward ''' cmd = 'sysctl -n {0}'.format(name) out = __salt__['cmd.run'](cmd) return out def assign(name, value): ''' Assign a single sysctl parameter for this minion CLI Example: .. code-block:: bash salt '*' sysctl.assign net.ipv4.ip_forward 1 ''' value = str(value) sysctl_file = '/proc/sys/{0}'.format(name.replace('.', '/')) if not os.path.exists(sysctl_file): raise CommandExecutionError('sysctl {0} does not exist'.format(name)) ret = {} cmd = 'sysctl -w {0}="{1}"'.format(name, value) data = __salt__['cmd.run_all'](cmd) out = data['stdout'] # Example: # # sysctl -w net.ipv4.tcp_rmem="4096 87380 16777216" # net.ipv4.tcp_rmem = 4096 87380 16777216 regex = re.compile(r'^{0}\s+=\s+{1}$'.format(re.escape(name), re.escape(value))) if not regex.match(out): if data['retcode'] != 0 and data['stderr']: error = data['stderr'] else: error = out raise CommandExecutionError('sysctl -w failed: {0}'.format(error)) new_name, new_value = out.split(' = ', 1) ret[new_name] = new_value return ret def persist(name, value, config=None): ''' Assign and persist a simple sysctl parameter for this minion. If ``config`` is not specified, a sensible default will be chosen using :mod:`sysctl.default_config `. CLI Example: .. code-block:: bash salt '*' sysctl.persist net.ipv4.ip_forward 1 ''' if config is None: config = default_config() running = show() edited = False # If the sysctl.conf is not present, add it if not os.path.isfile(config): try: with salt.utils.fopen(config, 'w+') as _fh: _fh.write('#\n# Kernel sysctl configuration\n#\n') except (IOError, OSError): msg = 'Could not write to file: {0}' raise CommandExecutionError(msg.format(config)) # Read the existing sysctl.conf nlines = [] try: with salt.utils.fopen(config, 'r') as _fh: # Use readlines because this should be a small file # and it seems unnecessary to indent the below for # loop since it is a fairly large block of code. config_data = _fh.readlines() except (IOError, OSError): msg = 'Could not read from file: {0}' raise CommandExecutionError(msg.format(config)) for line in config_data: if line.startswith('#'): nlines.append(line) continue if '=' not in line: nlines.append(line) continue # Strip trailing whitespace and split the k,v comps = [i.strip() for i in line.split('=', 1)] # On Linux procfs, files such as /proc/sys/net/ipv4/tcp_rmem or any # other sysctl with whitespace in it consistently uses 1 tab. Lets # allow our users to put a space or tab between multi-value sysctls # and have salt not try to set it every single time. if isinstance(comps[1], string_types) and ' ' in comps[1]: comps[1] = re.sub(r'\s+', '\t', comps[1]) # Do the same thing for the value 'just in case' if isinstance(value, string_types) and ' ' in value: value = re.sub(r'\s+', '\t', value) if len(comps) < 2: nlines.append(line) continue if name == comps[0]: # This is the line to edit if str(comps[1]) == str(value): # It is correct in the config, check if it is correct in /proc if name in running: if str(running[name]) != str(value): assign(name, value) return 'Updated' return 'Already set' nlines.append('{0} = {1}\n'.format(name, value)) edited = True continue else: nlines.append(line) if not edited: nlines.append('{0} = {1}\n'.format(name, value)) try: with salt.utils.fopen(config, 'w+') as _fh: _fh.writelines(nlines) except (IOError, OSError): msg = 'Could not write to file: {0}' raise CommandExecutionError(msg.format(config)) assign(name, value) return 'Updated' salt-0.17.5+ds.orig/salt/modules/guestfs.py0000644000175000017500000000254712263042153016677 0ustar joejoe# -*- coding: utf-8 -*- ''' Interact with virtual machine images via libguestfs :depends: - libguestfs ''' # Import Python libs import os import tempfile import hashlib import random # Import Salt libs import salt.utils def __virtual__(): ''' Only load if libguestfs python bindings are installed ''' if salt.utils.which('guestmount'): return 'guestfs' return False def mount(location, access='rw'): ''' Mount an image CLI Example: .. code-block:: bash salt '*' guest.mount /srv/images/fedora.qcow ''' root = os.path.join( tempfile.gettempdir(), 'guest', location.lstrip(os.sep).replace('/', '.') ) if not os.path.isdir(root): try: os.makedirs(root) except OSError: # somehow the directory already exists pass while True: if os.listdir(root): # Stuf is in there, don't use it rand = hashlib.md5(str(random.randint(1, 1000000))).hexdigest() root = os.path.join( tempfile.gettempdir(), 'guest', location.lstrip(os.sep).replace('/', '.') + rand ) else: break cmd = 'guestmount -i -a {0} --{1} {2}'.format(location, access, root) __salt__['cmd.run'](cmd) return root salt-0.17.5+ds.orig/salt/modules/ddns.py0000644000175000017500000001075712270576114016160 0ustar joejoe# -*- coding: utf-8 -*- ''' Support for RFC 2136 dynamic DNS updates. Requires dnspython module. ''' # Import python libs import logging log = logging.getLogger(__name__) try: import dns.query import dns.update dns_support = True except ImportError as e: dns_support = False def __virtual__(): ''' Confirm dnspython is available. ''' if dns_support: return 'ddns' return False def add_host(zone, name, ttl, ip, nameserver='127.0.0.1', replace=True): ''' Add, replace, or update the A and PTR (reverse) records for a host. CLI Example: .. code-block:: bash salt ns1 ddns.add_host example.com host1 60 10.1.1.1 ''' res = update(zone, name, ttl, 'A', ip, nameserver, replace) if res is False: return False fqdn = '{0}.{1}.'.format(name, zone) zone = 'in-addr.arpa.' parts = ip.split('.') popped = [] # Iterate over possible reverse zones while len(parts) > 1: p = parts.pop(0) popped.append(p) zone = '{0}.{1}'.format(p, zone) name = ip.replace('{0}.'.format('.'.join(popped)), '', 1) ptr = update(zone, name, ttl, 'PTR', fqdn, nameserver, replace) if ptr: return True return res def delete_host(zone, name, nameserver='127.0.0.1'): ''' Delete the forward and reverse records for a host. Returns true if any records are deleted. CLI Example: .. code-block:: bash salt ns1 ddns.delete_host example.com host1 ''' fqdn = '{0}.{1}'.format(name, zone) request = dns.message.make_query(fqdn, 'A') answer = dns.query.udp(request, nameserver) try: ips = [i.address for i in answer.answer[0].items] except IndexError: ips = [] res = delete(zone, name, nameserver=nameserver) fqdn = fqdn + '.' for ip in ips: zone = 'in-addr.arpa.' parts = ip.split('.') popped = [] # Iterate over possible reverse zones while len(parts) > 1: p = parts.pop(0) popped.append(p) zone = '{0}.{1}'.format(p, zone) name = ip.replace('{0}.'.format('.'.join(popped)), '', 1) ptr = delete(zone, name, 'PTR', fqdn, nameserver=nameserver) if ptr: res = True return res def update(zone, name, ttl, rdtype, data, nameserver='127.0.0.1', replace=False): ''' Add, replace, or update a DNS record. nameserver must be an IP address and the minion running this module must have update privileges on that server. If replace is true, first deletes all records for this name and type. CLI Example: .. code-block:: bash salt ns1 ddns.update example.com host1 60 A 10.0.0.1 ''' name = str(name) fqdn = '{0}.{1}'.format(name, zone) request = dns.message.make_query(fqdn, rdtype) answer = dns.query.udp(request, nameserver) rdtype = dns.rdatatype.from_text(rdtype) rdata = dns.rdata.from_text(dns.rdataclass.IN, rdtype, data) is_update = False for rrset in answer.answer: if rdata in rrset.items: rr = rrset.items if ttl == rrset.ttl: if replace and (len(answer.answer) > 1 or len(rrset.items) > 1): is_update = True break return None is_update = True break dns_update = dns.update.Update(zone) if is_update: dns_update.replace(name, ttl, rdata) else: dns_update.add(name, ttl, rdata) answer = dns.query.udp(dns_update, nameserver) if answer.rcode() > 0: return False return True def delete(zone, name, rdtype=None, data=None, nameserver='127.0.0.1'): ''' Delete a DNS record. CLI Example: .. code-block:: bash salt ns1 ddns.delete example.com host1 A ''' name = str(name) fqdn = '{0}.{1}'.format(name, zone) request = dns.message.make_query(fqdn, (rdtype or 'ANY')) answer = dns.query.udp(request, nameserver) if not answer.answer: return None dns_update = dns.update.Update(zone) if rdtype: rdtype = dns.rdatatype.from_text(rdtype) if data: rdata = dns.rdata.from_text(dns.rdataclass.IN, rdtype, data) dns_update.delete(name, rdata) else: dns_update.delete(name, rdtype) else: dns_update.delete(name) answer = dns.query.udp(dns_update, nameserver) if answer.rcode() > 0: return False return True salt-0.17.5+ds.orig/salt/modules/pillar.py0000644000175000017500000000540212270576114016502 0ustar joejoe# -*- coding: utf-8 -*- ''' Extract the pillar data for this minion ''' # Import third party libs import yaml # Import salt libs import salt.pillar import salt.utils def get(key, default=''): ''' .. versionadded:: 0.14 Attempt to retrieve the named value from pillar, if the named value is not available return the passed default. The default return is an empty string. The value can also represent a value in a nested dict using a ":" delimiter for the dict. This means that if a dict in pillar looks like this:: {'pkg': {'apache': 'httpd'}} To retrieve the value associated with the apache key in the pkg dict this key can be passed:: pkg:apache CLI Example: .. code-block:: bash salt '*' pillar.get pkg:apache ''' return salt.utils.traverse_dict(__pillar__, key, default) def items(*args): ''' This function calls the master for a fresh pillar and generates the pillar data on the fly, unlike pillar.raw which returns the pillar data which is currently loaded into the minion. CLI Example: .. code-block:: bash salt '*' pillar.items ''' # Preserve backwards compatibility if args: return item(*args) pillar = salt.pillar.get_pillar( __opts__, __grains__, __opts__['id'], __opts__['environment']) return pillar.compile_pillar() # Allow pillar.data to also be used to return pillar data data = items def item(*args): ''' .. versionadded:: 0.16.2 Return one ore more pillar entries CLI Examples: .. code-block:: bash salt '*' pillar.item foo salt '*' pillar.item foo bar baz ''' ret = {} pillar = items() for arg in args: try: ret[arg] = pillar[arg] except KeyError: pass return ret def raw(key=None): ''' Return the raw pillar data that is available in the module. This will show the pillar as it is loaded as the __pillar__ dict. CLI Example: .. code-block:: bash salt '*' pillar.raw With the optional key argument, you can select a subtree of the pillar raw data.:: salt '*' pillar.raw key='roles' ''' if key: ret = __pillar__.get(key, {}) else: ret = __pillar__ return ret def ext(external): ''' Generate the pillar and apply an explicit external pillar CLI Example: .. code-block:: bash salt '*' pillar.ext 'libvirt: _' ''' if isinstance(external, basestring): external = yaml.safe_load(external) pillar = salt.pillar.get_pillar( __opts__, __grains__, __opts__['id'], __opts__['environment'], external) ret = pillar.compile_pillar() return ret salt-0.17.5+ds.orig/salt/modules/win_servermanager.py0000644000175000017500000000550412270576114020740 0ustar joejoe# -*- coding: utf-8 -*- ''' Manage Windows features via the ServerManager powershell module ''' # Import salt libs import salt.utils def __virtual__(): ''' Load only on windows ''' if salt.utils.is_windows(): return 'win_servermanager' return False def _srvmgr(func): ''' Execute a function from the ServerManager PS module and return the STDOUT ''' return __salt__['cmd.run']('powershell -InputFormat None -Command "& {{ Import-Module ServerManager ; {0} }}"'.format(func)) def _parse_powershell_list(lst): ''' Parse command output when piped to format-list ''' ret = {} for line in lst.splitlines(): if line: splt = line.split() ret[splt[0]] = splt[2] return ret def list_available(): ''' List available features to install CLI Example: .. code-block:: bash salt '*' win_servermanager.list_available ''' return _srvmgr('Get-WindowsFeature -erroraction silentlycontinue') def list_installed(): ''' List installed features CLI Example: .. code-block:: bash salt '*' win_servermanager.list_installed ''' ret = {} for line in list_available().splitlines()[2:]: splt = line.split() if splt[0] == '[X]': name = splt.pop(-1) splt.pop(0) display_name = ' '.join(splt) ret[name] = display_name return ret def install(feature, recurse=False): ''' Install a feature Note: Some features requires reboot after un/installation, if so until the server is restarted Other features can not be installed ! Note: Some features takes a long time to complete un/installation, set -t with a long timeout CLI Example: .. code-block:: bash salt '*' win_servermanager.install Telnet-Client salt '*' win_servermanager.install SNMP-Services True ''' sub = '' if recurse: sub = '-IncludeAllSubFeature' out = _srvmgr('Add-WindowsFeature -Name {0} {1} -erroraction silentlycontinue | format-list'.format(feature, sub)) return _parse_powershell_list(out) def remove(feature): ''' Remove an installed feature .. note:: Some features require a reboot after installation/uninstallation. If one of these features are modified, then other features cannot be installed until the server is restarted. Additionally, some features take a while to complete installation/uninstallation, so it is a good idea to use the ``-t`` option to set a longer timeout. CLI Example: .. code-block:: bash salt -t 600 '*' win_servermanager.remove Telnet-Client ''' out = _srvmgr('Remove-WindowsFeature -Name {0} -erroraction silentlycontinue | format-list'.format(feature)) return _parse_powershell_list(out) salt-0.17.5+ds.orig/salt/modules/netbsd_sysctl.py0000644000175000017500000000637612270576114020112 0ustar joejoe# -*- coding: utf-8 -*- ''' Module for viewing and modifying sysctl parameters ''' import re # Import salt libs import salt.utils from salt.exceptions import CommandExecutionError def __virtual__(): ''' Only run on NetBSD systems ''' return 'sysctl' if __grains__['os'] == 'NetBSD' else False def show(): ''' Return a list of sysctl parameters for this minion CLI Example: .. code-block:: bash salt '*' sysctl.show ''' roots = ( 'kern', 'vm', 'vfs', 'net', 'hw', 'machdep', 'user', 'ddb', 'proc', 'emul', 'security', 'init' ) cmd = 'sysctl -ae' ret = {} out = __salt__['cmd.run'](cmd).splitlines() comps = [''] for line in out: if any([line.startswith('{0}.'.format(root)) for root in roots]): comps = re.split('[=:]', line, 1) ret[comps[0]] = comps[1] elif comps[0]: ret[comps[0]] += '{0}\n'.format(line) else: continue return ret def get(name): ''' Return a single sysctl parameter for this minion CLI Example: .. code-block:: bash salt '*' sysctl.get hw.physmem ''' cmd = 'sysctl -n {0}'.format(name) out = __salt__['cmd.run'](cmd) return out def assign(name, value): ''' Assign a single sysctl parameter for this minion CLI Example: .. code-block:: bash salt '*' sysctl.assign net.inet.icmp.icmplim 50 ''' ret = {} cmd = 'sysctl -w {0}="{1}"'.format(name, value) data = __salt__['cmd.run_all'](cmd) if data['retcode'] != 0: raise CommandExecutionError('sysctl failed: {0}'.format( data['stderr'])) new_name, new_value = data['stdout'].split(':', 1) ret[new_name] = new_value.split(' -> ')[-1] return ret def persist(name, value): ''' Assign and persist a simple sysctl parameter for this minion CLI Example: .. code-block:: bash salt '*' sysctl.persist net.inet.icmp.icmplim 50 ''' nlines = [] edited = False value = str(value) config = '/etc/sysctl.conf' with salt.utils.fopen(config, 'r') as ifile: for line in ifile: m = re.match(r'{0}(\??=)'.format(name), line) if not m: nlines.append(line) continue else: key, rest = line.split('=', 1) if rest.startswith('"'): _, rest_v, rest = rest.split('"', 2) elif rest.startswith('\''): _, rest_v, rest = rest.split('\'', 2) else: rest_v = rest.split()[0] rest = rest[len(rest_v):] if rest_v == value: return 'Already set' new_line = '{0}{1}{2}{3}'.format(name, m.group(1), value, rest) nlines.append(new_line) edited = True if not edited: newline = '{0}{1}{2}'.format(name, m.group(1), value) nlines.append("{0}\n".format(newline)) with salt.utils.fopen(config, 'w+') as ofile: ofile.writelines(nlines) assign(name, value) return 'Updated' # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 salt-0.17.5+ds.orig/salt/modules/status.py0000644000175000017500000003245212270576114016547 0ustar joejoe# -*- coding: utf-8 -*- ''' Module for returning various status data about a minion. These data can be useful for compiling into stats later. ''' # Import python libs import os import re import fnmatch # Import salt libs import salt.utils __opts__ = {} # TODO: Make this module support windows hosts # TODO: Make this module support BSD hosts properly, this is very Linux specific def __virtual__(): if salt.utils.is_windows(): return False return 'status' def _number(text): ''' Convert a string to a number. Returns an integer if the string represents an integer, a floating point number if the string is a real number, or the string unchanged otherwise. ''' if text.isdigit(): return int(text) try: return float(text) except ValueError: return text def procs(): ''' Return the process data CLI Example: .. code-block:: bash salt '*' status.procs ''' # Get the user, pid and cmd ret = {} uind = 0 pind = 0 cind = 0 plines = __salt__['cmd.run'](__grains__['ps']).splitlines() guide = plines.pop(0).split() if 'USER' in guide: uind = guide.index('USER') elif 'UID' in guide: uind = guide.index('UID') if 'PID' in guide: pind = guide.index('PID') if 'COMMAND' in guide: cind = guide.index('COMMAND') elif 'CMD' in guide: cind = guide.index('CMD') for line in plines: if not line: continue comps = line.split() ret[comps[pind]] = {'user': comps[uind], 'cmd': ' '.join(comps[cind:])} return ret def custom(): ''' Return a custom composite of status data and info for this minion, based on the minion config file. An example config like might be:: status.cpustats.custom: [ 'cpu', 'ctxt', 'btime', 'processes' ] Where status refers to status.py, cpustats is the function where we get our data, and custom is this function It is followed by a list of keys that we want returned. This function is meant to replace all_status(), which returns anything and everything, which we probably don't want. By default, nothing is returned. Warning: Depending on what you include, there can be a LOT here! CLI Example: .. code-block:: bash salt '*' status.custom ''' ret = {} conf = __salt__['config.dot_vals']('status') for key, val in conf.items(): func = '{0}()'.format(key.split('.')[1]) vals = eval(func) for item in val: ret[item] = vals[item] return ret def uptime(): ''' Return the uptime for this minion CLI Example: .. code-block:: bash salt '*' status.uptime ''' return __salt__['cmd.run']('uptime') def loadavg(): ''' Return the load averages for this minion CLI Example: .. code-block:: bash salt '*' status.loadavg ''' load_avg = os.getloadavg() return {'1-min': load_avg[0], '5-min': load_avg[1], '15-min': load_avg[2]} def cpustats(): ''' Return the CPU stats for this minion CLI Example: .. code-block:: bash salt '*' status.cpustats ''' procf = '/proc/stat' if not os.path.isfile(procf): return {} stats = salt.utils.fopen(procf, 'r').read().splitlines() ret = {} for line in stats: if not line: continue comps = line.split() if comps[0] == 'cpu': ret[comps[0]] = {'idle': _number(comps[4]), 'iowait': _number(comps[5]), 'irq': _number(comps[6]), 'nice': _number(comps[2]), 'softirq': _number(comps[7]), 'steal': _number(comps[8]), 'system': _number(comps[3]), 'user': _number(comps[1])} elif comps[0] == 'intr': ret[comps[0]] = {'total': _number(comps[1]), 'irqs': [_number(x) for x in comps[2:]]} elif comps[0] == 'softirq': ret[comps[0]] = {'total': _number(comps[1]), 'softirqs': [_number(x) for x in comps[2:]]} else: ret[comps[0]] = _number(comps[1]) return ret def meminfo(): ''' Return the CPU stats for this minion CLI Example: .. code-block:: bash salt '*' status.meminfo ''' procf = '/proc/meminfo' if not os.path.isfile(procf): return {} stats = salt.utils.fopen(procf, 'r').read().splitlines() ret = {} for line in stats: if not line: continue comps = line.split() comps[0] = comps[0].replace(':', '') ret[comps[0]] = { 'value': comps[1], } if len(comps) > 2: ret[comps[0]]['unit'] = comps[2] return ret def cpuinfo(): ''' Return the CPU info for this minion CLI Example: .. code-block:: bash salt '*' status.cpuinfo ''' procf = '/proc/cpuinfo' if not os.path.isfile(procf): return {} stats = salt.utils.fopen(procf, 'r').read().splitlines() ret = {} for line in stats: if not line: continue comps = line.split(':') comps[0] = comps[0].strip() if comps[0] == 'flags': ret[comps[0]] = comps[1].split() else: ret[comps[0]] = comps[1].strip() return ret def diskstats(): ''' Return the disk stats for this minion CLI Example: .. code-block:: bash salt '*' status.diskstats ''' procf = '/proc/diskstats' if not os.path.isfile(procf): return {} stats = salt.utils.fopen(procf, 'r').read().splitlines() ret = {} for line in stats: if not line: continue comps = line.split() ret[comps[2]] = {'major': _number(comps[0]), 'minor': _number(comps[1]), 'device': _number(comps[2]), 'reads_issued': _number(comps[3]), 'reads_merged': _number(comps[4]), 'sectors_read': _number(comps[5]), 'ms_spent_reading': _number(comps[6]), 'writes_completed': _number(comps[7]), 'writes_merged': _number(comps[8]), 'sectors_written': _number(comps[9]), 'ms_spent_writing': _number(comps[10]), 'io_in_progress': _number(comps[11]), 'ms_spent_in_io': _number(comps[12]), 'weighted_ms_spent_in_io': _number(comps[13])} return ret def diskusage(*args): ''' Return the disk usage for this minion Usage:: salt '*' status.diskusage [paths and/or filesystem types] CLI Example: .. code-block:: bash salt '*' status.diskusage # usage for all filesystems salt '*' status.diskusage / /tmp # usage for / and /tmp salt '*' status.diskusage ext? # usage for ext[234] filesystems salt '*' status.diskusage / ext? # usage for / and all ext filesystems ''' procf = '/proc/mounts' if not os.path.isfile(procf): return {} selected = set() fstypes = set() if not args: # select all filesystems fstypes.add('*') else: for arg in args: if arg.startswith('/'): # select path selected.add(arg) else: # select fstype fstypes.add(arg) if fstypes: # determine which mount points host the specified fstypes regex = re.compile( '|'.join( fnmatch.translate(fstype).format('(%s)') for fstype in fstypes ) ) with salt.utils.fopen(procf, 'r') as ifile: for line in ifile: comps = line.split() if len(comps) >= 3: mntpt = comps[1] fstype = comps[2] if regex.match(fstype): selected.add(mntpt) # query the filesystems disk usage ret = {} for path in selected: fsstats = os.statvfs(path) blksz = fsstats.f_bsize available = fsstats.f_bavail * blksz total = fsstats.f_blocks * blksz ret[path] = {"available": available, "total": total} return ret def vmstats(): ''' Return the virtual memory stats for this minion CLI Example: .. code-block:: bash salt '*' status.vmstats ''' procf = '/proc/vmstat' if not os.path.isfile(procf): return {} stats = salt.utils.fopen(procf, 'r').read().splitlines() ret = {} for line in stats: if not line: continue comps = line.split() ret[comps[0]] = _number(comps[1]) return ret def netstats(): ''' Return the network stats for this minion CLI Example: .. code-block:: bash salt '*' status.netstats ''' procf = '/proc/net/netstat' if not os.path.isfile(procf): return {} stats = salt.utils.fopen(procf, 'r').read().splitlines() ret = {} headers = [''] for line in stats: if not line: continue comps = line.split() if comps[0] == headers[0]: index = len(headers) - 1 row = {} for field in range(index): if field < 1: continue else: row[headers[field]] = _number(comps[field]) rowname = headers[0].replace(':', '') ret[rowname] = row else: headers = comps return ret def netdev(): ''' Return the network device stats for this minion CLI Example: .. code-block:: bash salt '*' status.netdev ''' procf = '/proc/net/dev' if not os.path.isfile(procf): return {} stats = salt.utils.fopen(procf, 'r').read().splitlines() ret = {} for line in stats: if not line: continue if line.find(':') < 0: continue comps = line.split() # Fix lines like eth0:9999..' comps[0] = line.split(':')[0].strip() #Support lines both like eth0:999 and eth0: 9999 comps.insert(1, line.split(':')[1].strip().split()[0]) ret[comps[0]] = {'iface': comps[0], 'rx_bytes': _number(comps[1]), 'rx_compressed': _number(comps[7]), 'rx_drop': _number(comps[4]), 'rx_errs': _number(comps[3]), 'rx_fifo': _number(comps[5]), 'rx_frame': _number(comps[6]), 'rx_multicast': _number(comps[8]), 'rx_packets': _number(comps[2]), 'tx_bytes': _number(comps[9]), 'tx_carrier': _number(comps[15]), 'tx_colls': _number(comps[14]), 'tx_compressed': _number(comps[16]), 'tx_drop': _number(comps[12]), 'tx_errs': _number(comps[11]), 'tx_fifo': _number(comps[13]), 'tx_packets': _number(comps[10])} return ret def w(): # pylint: disable=C0103 ''' Return a list of logged in users for this minion, using the w command CLI Example: .. code-block:: bash salt '*' status.w ''' user_list = [] users = __salt__['cmd.run']('w -h').splitlines() for row in users: if not row: continue comps = row.split() rec = {'idle': comps[3], 'jcpu': comps[4], 'login': comps[2], 'pcpu': comps[5], 'tty': comps[1], 'user': comps[0], 'what': ' '.join(comps[6:])} user_list.append(rec) return user_list def all_status(): ''' Return a composite of all status data and info for this minion. Warning: There is a LOT here! CLI Example: .. code-block:: bash salt '*' status.all_status ''' return {'cpuinfo': cpuinfo(), 'cpustats': cpustats(), 'diskstats': diskstats(), 'diskusage': diskusage(), 'loadavg': loadavg(), 'meminfo': meminfo(), 'netdev': netdev(), 'netstats': netstats(), 'uptime': uptime(), 'vmstats': vmstats(), 'w': w()} def pid(sig): ''' Return the PID or an empty string if the process is running or not. Pass a signature to use to find the process via ps. CLI Example: .. code-block:: bash salt '*' status.pid ''' # Check whether the sig is already quoted (we check at the end in case they # send a sig like `-E 'someregex'` to use egrep) and doesn't begin with a # dash (again, like `-E someregex`). Quote sigs that qualify. if (not sig.endswith('"') and not sig.endswith("'") and not sig.startswith('-')): sig = "'" + sig + "'" cmd = ("{0[ps]} | grep {1} | grep -v grep | fgrep -v status.pid | " "awk '{{print $2}}'".format(__grains__, sig)) return (__salt__['cmd.run_stdout'](cmd) or '') salt-0.17.5+ds.orig/salt/modules/nova.py0000644000175000017500000004765312270576114016200 0ustar joejoe# -*- coding: utf-8 -*- ''' Module for handling OpenStack Nova calls. :depends: - novaclient Python module :configuration: This module is not usable until the user, password, tenant, and auth URL are specified either in a pillar or in the minion's config file. For example:: keystone.user: admin keystone.password: verybadpass keystone.tenant: admin keystone.auth_url: 'http://127.0.0.1:5000/v2.0/' # Optional keystone.region_name: 'regionOne' If configuration for multiple OpenStack accounts is required, they can be set up as different configuration profiles: For example:: openstack1: keystone.user: admin keystone.password: verybadpass keystone.tenant: admin keystone.auth_url: 'http://127.0.0.1:5000/v2.0/' openstack2: keystone.user: admin keystone.password: verybadpass keystone.tenant: admin keystone.auth_url: 'http://127.0.0.2:5000/v2.0/' With this configuration in place, any of the nova functions can make use of a configuration profile by declaring it explicitly. For example:: salt '*' nova.flavor_list profile=openstack1 ''' # Import third party libs HAS_NOVA = False try: from novaclient.v1_1 import client HAS_NOVA = True except ImportError: pass # Import salt libs import salt.utils # Function alias to not shadow built-in's __func_alias__ = { 'list_': 'list' } def __virtual__(): ''' Only load this module if nova is installed on this minion. ''' if HAS_NOVA: return 'nova' return False __opts__ = {} def _auth(profile=None): ''' Set up nova credentials ''' if profile: credentials = __salt__['config.option'](profile) user = credentials['keystone.user'] password = credentials['keystone.password'] tenant = credentials['keystone.tenant'] auth_url = credentials['keystone.auth_url'] region_name = credentials.get('keystone.region_name', None) else: user = __salt__['config.option']('keystone.user') password = __salt__['config.option']('keystone.password') tenant = __salt__['config.option']('keystone.tenant') auth_url = __salt__['config.option']('keystone.auth_url') region_name = __salt__['config.option']('keystone.region_name') kwargs = { 'username': user, 'api_key': password, 'project_id': tenant, 'auth_url': auth_url, 'service_type': 'compute', } if region_name: kwargs['region_name'] = region_name return client.Client(**kwargs) def boot(name, flavor_id=0, image_id=0, profile=None): ''' Boot (create) a new instance Name of the new instance (must be first) Unique integer ID for the flavor Unique integer ID for the image CLI Example: .. code-block:: bash salt '*' nova.boot myinstance flavor_id=4596 image_id=2 The flavor_id and image_id are obtained from nova.flavor_list and nova.image_list .. code-block:: bash salt '*' nova.flavor_list salt '*' nova.image_list ''' nt_ks = _auth(profile) response = nt_ks.servers.create( name=name, flavor=flavor_id, image=image_id ) return server_show(response.id) def delete(instance_id, profile=None): ''' Boot (create) a new instance ID of the instance to be deleted CLI Example: .. code-block:: bash salt '*' nova.delete 1138 ''' nt_ks = _auth(profile) response = nt_ks.servers.delete(instance_id) return True def flavor_list(profile=None): ''' Return a list of available flavors (nova flavor-list) CLI Example: .. code-block:: bash salt '*' nova.flavor_list ''' nt_ks = _auth(profile) ret = {} for flavor in nt_ks.flavors.list(): links = {} for link in flavor.links: links[link['rel']] = link['href'] ret[flavor.name] = { 'disk': flavor.disk, 'id': flavor.id, 'name': flavor.name, 'ram': flavor.ram, 'swap': flavor.swap, 'vcpus': flavor.vcpus, 'links': links, } if hasattr(flavor, 'rxtx_factor'): ret[flavor.name]['rxtx_factor'] = flavor.rxtx_factor return ret def flavor_create(name, # pylint: disable=C0103 id=0, # pylint: disable=C0103 ram=0, disk=0, vcpus=1, profile=None): ''' Add a flavor to nova (nova flavor-create). The following parameters are required: Name of the new flavor (must be first) Unique integer ID for the new flavor Memory size in MB Disk size in GB Number of vcpus CLI Example: .. code-block:: bash salt '*' nova.flavor_create myflavor id=6 ram=4096 disk=10 vcpus=1 ''' nt_ks = _auth(profile) nt_ks.flavors.create( name=name, flavorid=id, ram=ram, disk=disk, vcpus=vcpus ) return {'name': name, 'id': id, 'ram': ram, 'disk': disk, 'vcpus': vcpus} def flavor_delete(id, profile=None): # pylint: disable=C0103 ''' Delete a flavor from nova by id (nova flavor-delete) CLI Example: .. code-block:: bash salt '*' nova.flavor_delete 7' ''' nt_ks = _auth(profile) nt_ks.flavors.delete(id) return 'Flavor deleted: {0}'.format(id) def keypair_list(profile=None): ''' Return a list of available keypairs (nova keypair-list) CLI Example: .. code-block:: bash salt '*' nova.keypair_list ''' nt_ks = _auth(profile) ret = {} for keypair in nt_ks.keypairs.list(): ret[keypair.name] = { 'name': keypair.name, 'fingerprint': keypair.fingerprint, 'public_key': keypair.public_key, } return ret def keypair_add(name, pubfile=None, pubkey=None, profile=None): ''' Add a keypair to nova (nova keypair-add) CLI Examples: .. code-block:: bash salt '*' nova.keypair_add mykey pubfile='/home/myuser/.ssh/id_rsa.pub' salt '*' nova.keypair_add mykey pubkey='ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAuGj4A7HcPLPl/etc== myuser@mybox' ''' nt_ks = _auth(profile) if pubfile: ifile = salt.utils.fopen(pubfile, 'r') pubkey = ifile.read() if not pubkey: return False nt_ks.keypairs.create(name, public_key=pubkey) ret = {'name': name, 'pubkey': pubkey} return ret def keypair_delete(name, profile=None): ''' Add a keypair to nova (nova keypair-delete) CLI Example: .. code-block:: bash salt '*' nova.keypair_delete mykey' ''' nt_ks = _auth(profile) nt_ks.keypairs.delete(name) return 'Keypair deleted: {0}'.format(name) def image_list(name=None, profile=None): ''' Return a list of available images (nova images-list + nova image-show) If a name is provided, only that image will be displayed. CLI Examples: .. code-block:: bash salt '*' nova.image_list salt '*' nova.image_list myimage ''' nt_ks = _auth(profile) ret = {} for image in nt_ks.images.list(): links = {} for link in image.links: links[link['rel']] = link['href'] ret[image.name] = { 'name': image.name, 'id': image.id, 'status': image.status, 'progress': image.progress, 'created': image.created, 'updated': image.updated, 'metadata': image.metadata, 'links': links, } if hasattr(image, 'minDisk'): ret[image.name]['minDisk'] = image.minDisk if hasattr(image, 'minRam'): ret[image.name]['minRam'] = image.minRam if name: return {name: ret[name]} return ret def image_meta_set(id=None, name=None, profile=None, **kwargs): # pylint: disable=C0103 ''' Sets a key=value pair in the metadata for an image (nova image-meta set) CLI Examples: .. code-block:: bash salt '*' nova.image_meta_set id=6f52b2ff-0b31-4d84-8fd1-af45b84824f6 cheese=gruyere salt '*' nova.image_meta_set name=myimage salad=pasta beans=baked ''' nt_ks = _auth(profile) if name: for image in nt_ks.images.list(): if image.name == name: id = image.id # pylint: disable=C0103 if not id: return {'Error': 'A valid image name or id was not specified'} nt_ks.images.set_meta(id, kwargs) return {id: kwargs} def image_meta_delete(id=None, # pylint: disable=C0103 name=None, keys=None, profile=None): ''' Delete a key=value pair from the metadata for an image (nova image-meta set) CLI Examples: .. code-block:: bash salt '*' nova.image_meta_delete id=6f52b2ff-0b31-4d84-8fd1-af45b84824f6 keys=cheese salt '*' nova.image_meta_delete name=myimage keys=salad,beans ''' nt_ks = _auth(profile) if name: for image in nt_ks.images.list(): if image.name == name: id = image.id # pylint: disable=C0103 pairs = keys.split(',') if not id: return {'Error': 'A valid image name or id was not specified'} nt_ks.images.delete_meta(id, pairs) return {id: 'Deleted: {0}'.format(pairs)} def list_(profile=None): ''' To maintain the feel of the nova command line, this function simply calls the server_list function. ''' return server_list(profile=profile) def server_list(profile=None): ''' Return list of active servers CLI Example: .. code-block:: bash salt '*' nova.show ''' nt_ks = _auth(profile) ret = {} for item in nt_ks.servers.list(): ret[item.name] = { 'id': item.id, 'name': item.name, 'status': item.status, 'accessIPv4': item.accessIPv4, 'accessIPv6': item.accessIPv6, 'flavor': {'id': item.flavor['id'], 'links': item.flavor['links']}, 'image': {'id': item.image['id'], 'links': item.image['links']}, } return ret def show(server_id, profile=None): ''' To maintain the feel of the nova command line, this function simply calls the server_show function. ''' return server_show(server_id, profile) def server_list_detailed(profile=None): ''' Return detailed list of active servers CLI Example: .. code-block:: bash salt '*' nova.server_list_detailed ''' nt_ks = _auth(profile) ret = {} for item in nt_ks.servers.list(): ret[item.name] = { 'OS-EXT-SRV-ATTR': {}, 'OS-EXT-STS': {}, 'accessIPv4': item.accessIPv4, 'accessIPv6': item.accessIPv6, 'addresses': item.addresses, 'config_drive': item.config_drive, 'created': item.created, 'flavor': {'id': item.flavor['id'], 'links': item.flavor['links']}, 'hostId': item.hostId, 'id': item.id, 'image': {'id': item.image['id'], 'links': item.image['links']}, 'key_name': item.key_name, 'links': item.links, 'metadata': item.metadata, 'name': item.name, 'progress': item.progress, 'status': item.status, 'tenant_id': item.tenant_id, 'updated': item.updated, 'user_id': item.user_id, } if hasattr(item.__dict__, 'OS-DCF:diskConfig'): ret[item.name]['OS-DCF'] = { 'diskConfig': item.__dict__['OS-DCF:diskConfig'] } if hasattr(item.__dict__, 'OS-EXT-SRV-ATTR:host'): ret[item.name]['OS-EXT-SRV-ATTR']['host'] = item.__dict__['OS-EXT-SRV-ATTR:host'] if hasattr(item.__dict__, 'OS-EXT-SRV-ATTR:hypervisor_hostname'): ret[item.name]['OS-EXT-SRV-ATTR']['hypervisor_hostname'] = item.__dict__['OS-EXT-SRV-ATTR:hypervisor_hostname'] if hasattr(item.__dict__, 'OS-EXT-SRV-ATTR:instance_name'): ret[item.name]['OS-EXT-SRV-ATTR']['instance_name'] = item.__dict__['OS-EXT-SRV-ATTR:instance_name'] if hasattr(item.__dict__, 'OS-EXT-STS:power_state'): ret[item.name]['OS-EXT-STS']['power_state'] = item.__dict__['OS-EXT-STS:power_state'] if hasattr(item.__dict__, 'OS-EXT-STS:task_state'): ret[item.name]['OS-EXT-STS']['task_state'] = item.__dict__['OS-EXT-STS:task_state'] if hasattr(item.__dict__, 'OS-EXT-STS:vm_state'): ret[item.name]['OS-EXT-STS']['vm_state'] = item.__dict__['OS-EXT-STS:vm_state'] if hasattr(item.__dict__, 'security_groups'): ret[item.name]['security_groups'] = item.__dict__['security_groups'] return ret def server_show(server_id, profile=None): ''' Return detailed information for an active server CLI Example: .. code-block:: bash salt '*' nova.server_show ''' ret = {} server_list = server_list_detailed(profile) for item in server_list: id_ = server_list[item]['id'] if str(id_) == server_id: ret[server_list[item]['name']] = server_list[item] return ret def secgroup_create(name, description, profile=None): ''' Add a secgroup to nova (nova secgroup-create) CLI Example: .. code-block:: bash salt '*' nova.secgroup_create mygroup 'This is my security group' ''' nt_ks = _auth(profile) nt_ks.security_groups.create(name, description) ret = {'name': name, 'description': description} return ret def secgroup_delete(name, profile=None): ''' Delete a secgroup to nova (nova secgroup-delete) CLI Example: .. code-block:: bash salt '*' nova.secgroup_delete mygroup ''' nt_ks = _auth(profile) for item in nt_ks.security_groups.list(): if item.name == name: nt_ks.security_groups.delete(item.id) return {name: 'Deleted security group: {0}'.format(name)} return 'Security group not found: {0}'.format(name) def secgroup_list(profile=None): ''' Return a list of available security groups (nova items-list) CLI Example: .. code-block:: bash salt '*' nova.secgroup_list ''' nt_ks = _auth(profile) ret = {} for item in nt_ks.security_groups.list(): ret[item.name] = { 'name': item.name, 'description': item.description, 'id': item.id, 'tenant_id': item.tenant_id, 'rules': item.rules, } return ret def _item_list(profile=None): ''' Template for writing list functions Return a list of available items (nova items-list) CLI Example: .. code-block:: bash salt '*' nova.item_list ''' nt_ks = _auth(profile) ret = [] for item in nt_ks.items.list(): ret.append(item.__dict__) #ret[item.name] = { # 'name': item.name, # } return ret #The following is a list of functions that need to be incorporated in the #nova module. This list should be updated as functions are added. # #absolute-limits Print a list of absolute limits for a user #actions Retrieve server actions. #add-fixed-ip Add new IP address to network. #add-floating-ip Add a floating IP address to a server. #aggregate-add-host Add the host to the specified aggregate. #aggregate-create Create a new aggregate with the specified details. #aggregate-delete Delete the aggregate by its id. #aggregate-details Show details of the specified aggregate. #aggregate-list Print a list of all aggregates. #aggregate-remove-host # Remove the specified host from the specified aggregate. #aggregate-set-metadata # Update the metadata associated with the aggregate. #aggregate-update Update the aggregate's name and optionally # availability zone. #cloudpipe-create Create a cloudpipe instance for the given project #cloudpipe-list Print a list of all cloudpipe instances. #console-log Get console log output of a server. #credentials Show user credentials returned from auth #describe-resource Show details about a resource #diagnostics Retrieve server diagnostics. #dns-create Create a DNS entry for domain, name and ip. #dns-create-private-domain # Create the specified DNS domain. #dns-create-public-domain # Create the specified DNS domain. #dns-delete Delete the specified DNS entry. #dns-delete-domain Delete the specified DNS domain. #dns-domains Print a list of available dns domains. #dns-list List current DNS entries for domain and ip or domain # and name. #endpoints Discover endpoints that get returned from the # authenticate services #floating-ip-create Allocate a floating IP for the current tenant. #floating-ip-delete De-allocate a floating IP. #floating-ip-list List floating ips for this tenant. #floating-ip-pool-list # List all floating ip pools. #get-vnc-console Get a vnc console to a server. #host-action Perform a power action on a host. #host-update Update host settings. #image-create Create a new image by taking a snapshot of a running # server. #image-delete Delete an image. #live-migration Migrates a running instance to a new machine. #lock Lock a server. #meta Set or Delete metadata on a server. #migrate Migrate a server. #pause Pause a server. #rate-limits Print a list of rate limits for a user #reboot Reboot a server. #rebuild Shutdown, re-image, and re-boot a server. #remove-fixed-ip Remove an IP address from a server. #remove-floating-ip Remove a floating IP address from a server. #rename Rename a server. #rescue Rescue a server. #resize Resize a server. #resize-confirm Confirm a previous resize. #resize-revert Revert a previous resize (and return to the previous # VM). #resume Resume a server. #root-password Change the root password for a server. #secgroup-add-group-rule # Add a source group rule to a security group. #secgroup-add-rule Add a rule to a security group. #secgroup-delete-group-rule # Delete a source group rule from a security group. #secgroup-delete-rule # Delete a rule from a security group. #secgroup-list-rules # List rules for a security group. #ssh SSH into a server. #suspend Suspend a server. #unlock Unlock a server. #unpause Unpause a server. #unrescue Unrescue a server. #usage-list List usage data for all tenants #volume-attach Attach a volume to a server. #volume-create Add a new volume. #volume-delete Remove a volume. #volume-detach Detach a volume from a server. #volume-list List all the volumes. #volume-show Show details about a volume. #volume-snapshot-create # Add a new snapshot. #volume-snapshot-delete # Remove a snapshot. #volume-snapshot-list # List all the snapshots. #volume-snapshot-show # Show details about a snapshot. #volume-type-create Create a new volume type. #volume-type-delete Delete a specific flavor #volume-type-list Print a list of available 'volume types'. #x509-create-cert Create x509 cert for a user in tenant #x509-get-root-cert Fetches the x509 root cert. salt-0.17.5+ds.orig/salt/modules/useradd.py0000644000175000017500000003062712270576114016655 0ustar joejoe# -*- coding: utf-8 -*- ''' Manage users with the useradd command ''' # Import python libs import re try: import grp import pwd except ImportError: pass import logging import copy # Import salt libs import salt.utils from salt._compat import string_types log = logging.getLogger(__name__) RETCODE_12_ERROR_REGEX = re.compile( r'userdel(.*)warning(.*)/var/mail(.*)No such file or directory' ) def __virtual__(): ''' Set the user module if the kernel is Linux or OpenBSD and remove some of the functionality on OS X ''' return ( 'user' if __grains__['kernel'] in ('Linux', 'OpenBSD', 'NetBSD') else False ) def _get_gecos(name): ''' Retrieve GECOS field info and return it in dictionary form ''' gecos_field = pwd.getpwnam(name).pw_gecos.split(',', 3) if not gecos_field: return {} else: # Assign empty strings for any unspecified trailing GECOS fields while len(gecos_field) < 4: gecos_field.append('') return {'fullname': str(gecos_field[0]), 'roomnumber': str(gecos_field[1]), 'workphone': str(gecos_field[2]), 'homephone': str(gecos_field[3])} def _build_gecos(gecos_dict): ''' Accepts a dictionary entry containing GECOS field names and their values, and returns a full GECOS comment string, to be used with usermod. ''' return '{0},{1},{2},{3}'.format(gecos_dict.get('fullname', ''), gecos_dict.get('roomnumber', ''), gecos_dict.get('workphone', ''), gecos_dict.get('homephone', '')) def add(name, uid=None, gid=None, groups=None, home=None, shell=None, unique=True, system=False, fullname='', roomnumber='', workphone='', homephone='', createhome=True): ''' Add a user to the minion CLI Example: .. code-block:: bash salt '*' user.add name ''' cmd = ['useradd'] if shell: cmd.extend(['-s', shell]) if uid not in (None, ''): cmd.extend(['-u', str(uid)]) if gid not in (None, ''): cmd.extend(['-g', str(gid)]) elif groups is not None and name in groups: try: for line in salt.utils.fopen('/etc/login.defs'): if not 'USERGROUPS_ENAB' in line[:15]: continue if 'yes' in line: cmd.extend([ '-g', str(__salt__['file.group_to_gid'](name)) ]) # We found what we wanted, let's break out of the loop break except OSError: log.debug('Error reading /etc/login.defs', exc_info=True) if createhome: cmd.append('-m') elif createhome is False: cmd.append('-M') if home is not None: cmd.extend(['-d', home]) if not unique: cmd.append('-o') if system and __grains__['kernel'] != 'NetBSD': cmd.append('-r') cmd.append(name) ret = __salt__['cmd.run_all'](' '.join(cmd)) if ret['retcode'] != 0: return False # At this point, the user was successfully created, so return true # regardless of the outcome of the below functions. If there is a # problem wth changing any of the user's info below, it will be raised # in a future highstate call. If anyone has a better idea on how to do # this, feel free to change it, but I didn't think it was a good idea # to return False when the user was successfully created since A) the # user does exist, and B) running useradd again would result in a # nonzero exit status and be interpreted as a False result. if groups: chgroups(name, groups) if fullname: chfullname(name, fullname) if roomnumber: chroomnumber(name, roomnumber) if workphone: chworkphone(name, workphone) if homephone: chhomephone(name, homephone) return True def delete(name, remove=False, force=False): ''' Remove a user from the minion CLI Example: .. code-block:: bash salt '*' user.delete name remove=True force=True ''' cmd = ['userdel'] if remove: cmd.append('-r') if force: cmd.append('-f') cmd.append(name) ret = __salt__['cmd.run_all'](' '.join(cmd)) if ret['retcode'] == 0: # Command executed with no errors return True if ret['retcode'] == 12: # There's a known bug in Debian based distributions, at least, that # makes the command exit with 12, see: # https://bugs.launchpad.net/ubuntu/+source/shadow/+bug/1023509 if __grains__['os_family'] not in ('Debian',): return False if RETCODE_12_ERROR_REGEX.match(ret['stderr']) is not None: # We've hit the bug, let's log it and not fail log.debug( 'While the userdel exited with code 12, this is a know bug on ' 'debian based distributions. See http://goo.gl/HH3FzT' ) return True return False def getent(): ''' Return the list of all info for all users CLI Example: .. code-block:: bash salt '*' user.getent ''' if 'user.getent' in __context__: return __context__['user.getent'] ret = [] for data in pwd.getpwall(): ret.append(_format_info(data)) __context__['user.getent'] = ret return ret def chuid(name, uid): ''' Change the uid for a named user CLI Example: .. code-block:: bash salt '*' user.chuid foo 4376 ''' pre_info = info(name) if uid == pre_info['uid']: return True cmd = 'usermod -u {0} {1}'.format(uid, name) __salt__['cmd.run'](cmd) post_info = info(name) if post_info['uid'] != pre_info['uid']: return post_info['uid'] == uid return False def chgid(name, gid): ''' Change the default group of the user CLI Example: .. code-block:: bash salt '*' user.chgid foo 4376 ''' pre_info = info(name) if gid == pre_info['gid']: return True cmd = 'usermod -g {0} {1}'.format(gid, name) __salt__['cmd.run'](cmd) post_info = info(name) if post_info['gid'] != pre_info['gid']: return post_info['gid'] == gid return False def chshell(name, shell): ''' Change the default shell of the user CLI Example: .. code-block:: bash salt '*' user.chshell foo /bin/zsh ''' pre_info = info(name) if shell == pre_info['shell']: return True cmd = 'usermod -s {0} {1}'.format(shell, name) __salt__['cmd.run'](cmd) post_info = info(name) if post_info['shell'] != pre_info['shell']: return post_info['shell'] == shell return False def chhome(name, home, persist=False): ''' Change the home directory of the user, pass true for persist to copy files to the new home dir CLI Example: .. code-block:: bash salt '*' user.chhome foo /home/users/foo True ''' pre_info = info(name) if home == pre_info['home']: return True cmd = 'usermod -d {0} '.format(home) if persist: cmd += ' -m ' cmd += name __salt__['cmd.run'](cmd) post_info = info(name) if post_info['home'] != pre_info['home']: return post_info['home'] == home return False def chgroups(name, groups, append=False): ''' Change the groups this user belongs to, add append to append the specified groups CLI Example: .. code-block:: bash salt '*' user.chgroups foo wheel,root True ''' if isinstance(groups, string_types): groups = groups.split(',') ugrps = set(list_groups(name)) if ugrps == set(groups): return True cmd = 'usermod ' if append: cmd += '-a ' cmd += '-G "{0}" {1}'.format(','.join(groups), name) return not __salt__['cmd.retcode'](cmd) def chfullname(name, fullname): ''' Change the user's Full Name CLI Example: .. code-block:: bash salt '*' user.chfullname foo "Foo Bar" ''' fullname = str(fullname) pre_info = _get_gecos(name) if not pre_info: return False if fullname == pre_info['fullname']: return True gecos_field = copy.deepcopy(pre_info) gecos_field['fullname'] = fullname cmd = 'usermod -c "{0}" {1}'.format(_build_gecos(gecos_field), name) __salt__['cmd.run'](cmd) post_info = info(name) if post_info['fullname'] != pre_info['fullname']: return post_info['fullname'] == fullname return False def chroomnumber(name, roomnumber): ''' Change the user's Room Number CLI Example: .. code-block:: bash salt '*' user.chroomnumber foo 123 ''' roomnumber = str(roomnumber) pre_info = _get_gecos(name) if not pre_info: return False if roomnumber == pre_info['roomnumber']: return True gecos_field = copy.deepcopy(pre_info) gecos_field['roomnumber'] = roomnumber cmd = 'usermod -c "{0}" {1}'.format(_build_gecos(gecos_field), name) __salt__['cmd.run'](cmd) post_info = info(name) if post_info['roomnumber'] != pre_info['roomnumber']: return post_info['roomnumber'] == roomnumber return False def chworkphone(name, workphone): ''' Change the user's Work Phone CLI Example: .. code-block:: bash salt '*' user.chworkphone foo "7735550123" ''' workphone = str(workphone) pre_info = _get_gecos(name) if not pre_info: return False if workphone == pre_info['workphone']: return True gecos_field = copy.deepcopy(pre_info) gecos_field['workphone'] = workphone cmd = 'usermod -c "{0}" {1}'.format(_build_gecos(gecos_field), name) __salt__['cmd.run'](cmd) post_info = info(name) if post_info['workphone'] != pre_info['workphone']: return post_info['workphone'] == workphone return False def chhomephone(name, homephone): ''' Change the user's Home Phone CLI Example: .. code-block:: bash salt '*' user.chhomephone foo "7735551234" ''' homephone = str(homephone) pre_info = _get_gecos(name) if not pre_info: return False if homephone == pre_info['homephone']: return True gecos_field = copy.deepcopy(pre_info) gecos_field['homephone'] = homephone cmd = 'usermod -c "{0}" {1}'.format(_build_gecos(gecos_field), name) __salt__['cmd.run'](cmd) post_info = info(name) if post_info['homephone'] != pre_info['homephone']: return post_info['homephone'] == homephone return False def info(name): ''' Return user information CLI Example: .. code-block:: bash salt '*' user.info root ''' try: data = pwd.getpwnam(name) except KeyError: return {} else: return _format_info(data) def _format_info(data): ''' Return user information in a pretty way ''' # Put GECOS info into a list gecos_field = data.pw_gecos.split(',', 3) # Make sure our list has at least four elements while len(gecos_field) < 4: gecos_field.append('') return {'gid': data.pw_gid, 'groups': list_groups(data.pw_name), 'home': data.pw_dir, 'name': data.pw_name, 'passwd': data.pw_passwd, 'shell': data.pw_shell, 'uid': data.pw_uid, 'fullname': gecos_field[0], 'roomnumber': gecos_field[1], 'workphone': gecos_field[2], 'homephone': gecos_field[3]} def list_groups(name): ''' Return a list of groups the named user belongs to CLI Example: .. code-block:: bash salt '*' user.list_groups foo ''' ugrp = set() # Add the primary user's group try: ugrp.add(grp.getgrgid(pwd.getpwnam(name).pw_gid).gr_name) except KeyError: # The user's applied default group is undefined on the system, so # it does not exist pass groups = grp.getgrall() # Now, all other groups the user belongs to for group in groups: if name in group.gr_mem: ugrp.add(group.gr_name) return sorted(list(ugrp)) def list_users(): ''' Return a list of all users CLI Example: .. code-block:: bash salt '*' user.list_users ''' return sorted([user.pw_name for user in pwd.getpwall()]) salt-0.17.5+ds.orig/salt/modules/file.py0000644000175000017500000023357612270576114016155 0ustar joejoe# -*- coding: utf-8 -*- ''' Manage information about regular files, directories, and special files on the minion, set/read user, group, mode, and data ''' # TODO: We should add the capability to do u+r type operations here # some time in the future from __future__ import print_function # Import python libs import contextlib # For < 2.7 compat import datetime import difflib import errno import fileinput import fnmatch import getpass import hashlib import itertools import logging import operator import os import re import shutil import stat import sys import tempfile import time try: import grp import pwd except ImportError: pass # Import salt libs import salt.utils import salt.utils.find import salt.utils.filebuffer from salt.exceptions import CommandExecutionError, SaltInvocationError import salt._compat log = logging.getLogger(__name__) def __virtual__(): ''' Only work on POSIX-like systems ''' # win_file takes care of windows if salt.utils.is_windows(): return False return 'file' def __clean_tmp(sfn): ''' Clean out a template temp file ''' if sfn.startswith(tempfile.gettempdir()): # Don't remove if it exists in file_roots (any env) all_roots = itertools.chain.from_iterable( __opts__['file_roots'].itervalues()) in_roots = any(sfn.startswith(root) for root in all_roots) # Only clean up files that exist if os.path.exists(sfn) and not in_roots: os.remove(sfn) def _error(ret, err_msg): ret['result'] = False ret['comment'] = err_msg return ret def _binary_replace(old, new): ''' This function does NOT do any diffing, it just checks the old and new files to see if either is binary, and provides an appropriate string noting the difference between the two files. If neither file is binary, an empty string is returned. This function should only be run AFTER it has been determined that the files differ. ''' old_isbin = not salt.utils.istextfile(old) new_isbin = not salt.utils.istextfile(new) if any((old_isbin, new_isbin)): if all((old_isbin, new_isbin)): return 'Replace binary file' elif old_isbin: return 'Replace binary file with text file' elif new_isbin: return 'Replace text file with binary file' return '' def _get_bkroot(): ''' Get the location of the backup dir in the minion cache ''' # Get the cachedir from the minion config return os.path.join(__salt__['config.get']('cachedir'), 'file_backup') def gid_to_group(gid): ''' Convert the group id to the group name on this system CLI Example: .. code-block:: bash salt '*' file.gid_to_group 0 ''' try: gid = int(gid) except ValueError: # This is not an integer, maybe it's already the group name? gid = group_to_gid(gid) if gid == '': # Don't even bother to feed it to grp return '' try: return grp.getgrgid(gid).gr_name except KeyError: return '' def group_to_gid(group): ''' Convert the group to the gid on this system CLI Example: .. code-block:: bash salt '*' file.group_to_gid root ''' if not group: return '' try: return grp.getgrnam(group).gr_gid except KeyError: return '' def get_gid(path, follow_symlinks=True): ''' Return the id of the group that owns a given file CLI Example: .. code-block:: bash salt '*' file.get_gid /etc/passwd .. versionchanged:: 0.16.4 ``follow_symlinks`` option added ''' if not os.path.exists(path): try: # Broken symlinks will return false, but still have a uid and gid return os.lstat(path).st_gid except OSError: pass return -1 return os.stat(path).st_gid if follow_symlinks else os.lstat(path).st_gid def get_group(path, follow_symlinks=True): ''' Return the group that owns a given file CLI Example: .. code-block:: bash salt '*' file.get_group /etc/passwd .. versionchanged:: 0.16.4 ``follow_symlinks`` option added ''' gid = get_gid(path, follow_symlinks) if gid == -1: return False return gid_to_group(gid) def uid_to_user(uid): ''' Convert a uid to a user name CLI Example: .. code-block:: bash salt '*' file.uid_to_user 0 ''' try: return pwd.getpwuid(uid).pw_name except KeyError: return '' def user_to_uid(user): ''' Convert user name to a uid CLI Example: .. code-block:: bash salt '*' file.user_to_uid root ''' if not user: user = getpass.getuser() try: return pwd.getpwnam(user).pw_uid except KeyError: return '' def get_uid(path, follow_symlinks=True): ''' Return the id of the user that owns a given file CLI Example: .. code-block:: bash salt '*' file.get_uid /etc/passwd .. versionchanged:: 0.16.4 ``follow_symlinks`` option added ''' if not os.path.exists(path): try: # Broken symlinks will return false, but still have a uid and gid return os.lstat(path).st_uid except OSError: pass return -1 return os.stat(path).st_uid if follow_symlinks else os.lstat(path).st_uid def get_user(path, follow_symlinks=True): ''' Return the user that owns a given file CLI Example: .. code-block:: bash salt '*' file.get_user /etc/passwd .. versionchanged:: 0.16.4 ``follow_symlinks`` option added ''' uid = get_uid(path, follow_symlinks) if uid == -1: return False return uid_to_user(uid) def get_mode(path): ''' Return the mode of a file CLI Example: .. code-block:: bash salt '*' file.get_mode /etc/passwd ''' if not os.path.exists(path): return '' mode = str(oct(os.stat(path).st_mode)[-4:]) if mode.startswith('0'): return mode[1:] return mode def set_mode(path, mode): ''' Set the mode of a file CLI Example: .. code-block:: bash salt '*' file.set_mode /etc/passwd 0644 ''' mode = str(mode).lstrip('0') if not mode: mode = '0' if not os.path.exists(path): return 'File not found' try: os.chmod(path, int(mode, 8)) except Exception: return 'Invalid Mode ' + mode return get_mode(path) def chown(path, user, group): ''' Chown a file, pass the file the desired user and group CLI Example: .. code-block:: bash salt '*' file.chown /etc/passwd root root ''' uid = user_to_uid(user) gid = group_to_gid(group) err = '' if uid == '': if user: err += 'User does not exist\n' else: uid = -1 if gid == '': if group: err += 'Group does not exist\n' else: gid = -1 if not os.path.exists(path): try: # Broken symlinks will return false, but still need to be chowned return os.lchown(path, uid, gid) except OSError: pass err += 'File not found' if err: return err return os.chown(path, uid, gid) def chgrp(path, group): ''' Change the group of a file CLI Example: .. code-block:: bash salt '*' file.chgrp /etc/passwd root ''' user = get_user(path) return chown(path, user, group) def get_sum(path, form='md5'): ''' Return the sum for the given file, default is md5, sha1, sha224, sha256, sha384, sha512 are supported CLI Example: .. code-block:: bash salt '*' file.get_sum /etc/passwd sha512 ''' if not os.path.isfile(path): return 'File not found' try: with salt.utils.fopen(path, 'rb') as ifile: return getattr(hashlib, form)(ifile.read()).hexdigest() except (IOError, OSError) as err: return 'File Error: {0}'.format(err) except AttributeError: return 'Hash {0} not supported'.format(form) except NameError: return 'Hashlib unavailable - please fix your python install' except Exception as err: return str(err) def get_hash(path, form='md5', chunk_size=4096): ''' Get the hash sum of a file This is better than ``get_sum`` for the following reasons: - It does not read the entire file into memory. - It does not return a string on error. The returned value of ``get_sum`` cannot really be trusted since it is vulnerable to collisions: ``get_sum(..., 'xyz') == 'Hash xyz not supported'`` CLI Example: .. code-block:: bash salt '*' file.get_hash /etc/shadow ''' return salt.utils.get_hash(path, form, chunk_size) def check_hash(path, hash): ''' Check if a file matches the given hash string Returns true if the hash matched, otherwise false. Raises ValueError if the hash was not formatted correctly. path A file path hash A string in the form =. For example: ``md5=e138491e9d5b97023cea823fe17bac22`` CLI Example: .. code-block:: bash salt '*' file.check_hash /etc/fstab md5= ''' hash_parts = hash.split('=', 1) if len(hash_parts) != 2: raise ValueError('Bad hash format: {0!r}'.format(hash)) hash_form, hash_value = hash_parts return get_hash(path, hash_form) == hash_value def find(path, **kwargs): ''' Approximate the Unix ``find(1)`` command and return a list of paths that meet the specified criteria. The options include match criteria:: name = path-glob # case sensitive iname = path-glob # case insensitive regex = path-regex # case sensitive iregex = path-regex # case insensitive type = file-types # match any listed type user = users # match any listed user group = groups # match any listed group size = [+-]number[size-unit] # default unit = byte mtime = interval # modified since date grep = regex # search file contents and/or actions:: delete [= file-types] # default type = 'f' exec = command [arg ...] # where {} is replaced by pathname print [= print-opts] The default action is 'print=path'. file-glob:: * = match zero or more chars ? = match any char [abc] = match a, b, or c [!abc] or [^abc] = match anything except a, b, and c [x-y] = match chars x through y [!x-y] or [^x-y] = match anything except chars x through y {a,b,c} = match a or b or c path-regex: a Python re (regular expression) pattern to match pathnames file-types: a string of one or more of the following:: a: all file types b: block device c: character device d: directory p: FIFO (named pipe) f: plain file l: symlink s: socket users: a space and/or comma separated list of user names and/or uids groups: a space and/or comma separated list of group names and/or gids size-unit:: b: bytes k: kilobytes m: megabytes g: gigabytes t: terabytes interval:: [w] [d] [h] [m] [s] where: w: week d: day h: hour m: minute s: second print-opts: a comma and/or space separated list of one or more of the following:: group: group name md5: MD5 digest of file contents mode: file permissions (as integer) mtime: last modification time (as time_t) name: file basename path: file absolute path size: file size in bytes type: file type user: user name CLI Examples: .. code-block:: bash salt '*' file.find / type=f name=\\*.bak size=+10m salt '*' file.find /var mtime=+30d size=+10m print=path,size,mtime salt '*' file.find /var/log name=\\*.[0-9] mtime=+30d size=+10m delete ''' try: finder = salt.utils.find.Finder(kwargs) except ValueError as ex: return 'error: {0}'.format(ex) ret = [p for p in finder.find(path)] ret.sort() return ret def _sed_esc(string, escape_all=False): ''' Escape single quotes and forward slashes ''' special_chars = "^.[$()|*+?{" string = string.replace("'", "'\"'\"'").replace("/", "\\/") if escape_all is True: for char in special_chars: string = string.replace(char, "\\" + char) return string def sed(path, before, after, limit='', backup='.bak', options='-r -e', flags='g', escape_all=False, negate_match=False): ''' .. deprecated:: 0.17.1 Use :func:`replace` instead. Make a simple edit to a file Equivalent to:: sed "// s/// " path The full path to the file to be edited before A pattern to find in order to replace with ``after`` after Text that will replace ``before`` limit : ``''`` An initial pattern to search for before searching for ``before`` backup : ``.bak`` The file will be backed up before edit with this file extension; **WARNING:** each time ``sed``/``comment``/``uncomment`` is called will overwrite this backup options : ``-r -e`` Options to pass to sed flags : ``g`` Flags to modify the sed search; e.g., ``i`` for case-insensitve pattern matching negate_match : False Negate the search command (``!``) .. versionadded:: 0.17 Forward slashes and single quotes will be escaped automatically in the ``before`` and ``after`` patterns. CLI Example: .. code-block:: bash salt '*' file.sed /etc/httpd/httpd.conf 'LogLevel warn' 'LogLevel info' ''' # Largely inspired by Fabric's contrib.files.sed() # XXX:dc: Do we really want to always force escaping? # if not os.path.exists(path): return False # Mandate that before and after are strings before = str(before) after = str(after) before = _sed_esc(before, escape_all) after = _sed_esc(after, escape_all) limit = _sed_esc(limit, escape_all) if sys.platform == 'darwin': options = options.replace('-r', '-E') cmd = ( r'''sed {backup}{options} '{limit}{negate_match}s/{before}/{after}/{flags}' {path}''' .format( backup='-i{0} '.format(backup) if backup else '-i ', options=options, limit='/{0}/ '.format(limit) if limit else '', before=before, after=after, flags=flags, path=path, negate_match='!' if negate_match else '', ) ) return __salt__['cmd.run_all'](cmd) def sed_contains(path, text, limit='', flags='g'): ''' .. deprecated:: 0.17.1 Use :func:`search` instead. Return True if the file at ``path`` contains ``text``. Utilizes sed to perform the search (line-wise search). Note: the ``p`` flag will be added to any flags you pass in. CLI Example: .. code-block:: bash salt '*' file.contains /etc/crontab 'mymaintenance.sh' ''' # Largely inspired by Fabric's contrib.files.contains() if not os.path.exists(path): return False before = _sed_esc(str(text), False) limit = _sed_esc(str(limit), False) options = '-n -r -e' if sys.platform == 'darwin': options = options.replace('-r', '-E') cmd = r"sed {options} '{limit}s/{before}/$/{flags}' {path}".format( options=options, limit='/{0}/ '.format(limit) if limit else '', before=before, flags='p{0}'.format(flags), path=path) result = __salt__['cmd.run'](cmd) return bool(result) def psed(path, before, after, limit='', backup='.bak', flags='gMS', escape_all=False, multi=False): ''' .. deprecated:: 0.17.1 Use :func:`replace` instead. Make a simple edit to a file (pure Python version) Equivalent to:: sed "// s/// " path The full path to the file to be edited before A pattern to find in order to replace with ``after`` after Text that will replace ``before`` limit : ``''`` An initial pattern to search for before searching for ``before`` backup : ``.bak`` The file will be backed up before edit with this file extension; **WARNING:** each time ``sed``/``comment``/``uncomment`` is called will overwrite this backup flags : ``gMS`` Flags to modify the search. Valid values are: - ``g``: Replace all occurrences of the pattern, not just the first. - ``I``: Ignore case. - ``L``: Make ``\\w``, ``\\W``, ``\\b``, ``\\B``, ``\\s`` and ``\\S`` dependent on the locale. - ``M``: Treat multiple lines as a single line. - ``S``: Make `.` match all characters, including newlines. - ``U``: Make ``\\w``, ``\\W``, ``\\b``, ``\\B``, ``\\d``, ``\\D``, ``\\s`` and ``\\S`` dependent on Unicode. - ``X``: Verbose (whitespace is ignored). multi: ``False`` If True, treat the entire file as a single line Forward slashes and single quotes will be escaped automatically in the ``before`` and ``after`` patterns. CLI Example: .. code-block:: bash salt '*' file.sed /etc/httpd/httpd.conf 'LogLevel warn' 'LogLevel info' ''' # Largely inspired by Fabric's contrib.files.sed() # XXX:dc: Do we really want to always force escaping? # # Mandate that before and after are strings multi = bool(multi) before = str(before) after = str(after) before = _sed_esc(before, escape_all) # The pattern to replace with does not need to be escaped!!! #after = _sed_esc(after, escape_all) limit = _sed_esc(limit, escape_all) shutil.copy2(path, '{0}{1}'.format(path, backup)) ofile = salt.utils.fopen(path, 'w') with salt.utils.fopen('{0}{1}'.format(path, backup), 'r') as ifile: if multi is True: for line in ifile.readline(): ofile.write(_psed(line, before, after, limit, flags)) else: ofile.write(_psed(ifile.read(), before, after, limit, flags)) ofile.close() RE_FLAG_TABLE = {'I': re.I, 'L': re.L, 'M': re.M, 'S': re.S, 'U': re.U, 'X': re.X} def _psed(text, before, after, limit, flags): ''' Does the actual work for file.psed, so that single lines can be passed in ''' atext = text if limit: limit = re.compile(limit) comps = text.split(limit) atext = ''.join(comps[1:]) count = 1 if 'g' in flags: count = 0 flags = flags.replace('g', '') aflags = 0 for flag in flags: aflags |= RE_FLAG_TABLE[flag] before = re.compile(before, flags=aflags) text = re.sub(before, after, atext, count=count) return text def uncomment(path, regex, char='#', backup='.bak'): ''' .. deprecated:: 0.17.1 Use :func:`replace` instead. Uncomment specified commented lines in a file path The full path to the file to be edited regex A regular expression used to find the lines that are to be uncommented. This regex should not include the comment character. A leading ``^`` character will be stripped for convenience (for easily switching between comment() and uncomment()). char : ``#`` The character to remove in order to uncomment a line backup : ``.bak`` The file will be backed up before edit with this file extension; **WARNING:** each time ``sed``/``comment``/``uncomment`` is called will overwrite this backup CLI Example: .. code-block:: bash salt '*' file.uncomment /etc/hosts.deny 'ALL: PARANOID' ''' # Largely inspired by Fabric's contrib.files.uncomment() return sed(path, before=r'^([[:space:]]*){0}'.format(char), after=r'\1', limit=regex.lstrip('^'), backup=backup) def comment(path, regex, char='#', backup='.bak'): ''' .. deprecated:: 0.17.1 Use :func:`replace` instead. Comment out specified lines in a file path The full path to the file to be edited regex A regular expression used to find the lines that are to be commented; this pattern will be wrapped in parenthesis and will move any preceding/trailing ``^`` or ``$`` characters outside the parenthesis (e.g., the pattern ``^foo$`` will be rewritten as ``^(foo)$``) char : ``#`` The character to be inserted at the beginning of a line in order to comment it out backup : ``.bak`` The file will be backed up before edit with this file extension .. warning:: This backup will be overwritten each time ``sed`` / ``comment`` / ``uncomment`` is called. Meaning the backup will only be useful after the first invocation. CLI Example: .. code-block:: bash salt '*' file.comment /etc/modules pcspkr ''' # Largely inspired by Fabric's contrib.files.comment() regex = '{0}({1}){2}'.format( '^' if regex.startswith('^') else '', regex.lstrip('^').rstrip('$'), '$' if regex.endswith('$') else '') return sed(path, before=regex, after=r'{0}\1'.format(char), backup=backup) def _get_flags(flags): ''' Return an integer appropriate for use as a flag for the re module from a list of human-readable strings >>> _get_flags(['MULTILINE', 'IGNORECASE']) 10 ''' if isinstance(flags, list): _flags_acc = [] for flag in flags: _flag = getattr(re, flag.upper()) if not isinstance(_flag, int): raise SaltInvocationError( 'Invalid re flag given: {0}'.format(flag) ) _flags_acc.append(_flag) return reduce(operator.__or__, _flags_acc) return flags def replace(path, pattern, repl, count=0, flags=0, bufsize=1, backup='.bak', dry_run=False, search_only=False, show_changes=True, ): ''' Replace occurances of a pattern in a file .. versionadded:: 0.17.1 This is a pure Python implementation that wraps Python's :py:func:`~re.sub`. :param path: Filesystem path to the file to be edited :param pattern: The PCRE search :param repl: The replacement text :param count: Maximum number of pattern occurrences to be replaced :param flags: A list of flags defined in the :ref:`re module documentation `. Each list item should be a string that will correlate to the human-friendly flag name. E.g., ``['IGNORECASE', 'MULTILINE']``. Note: multiline searches must specify ``file`` as the ``bufsize`` argument below. :type flags: list or int :param bufsize: How much of the file to buffer into memory at once. The default value ``1`` processes one line at a time. The special value ``file`` may be specified which will read the entire file into memory before processing. Note: multiline searches must specify ``file`` buffering. :type bufsize: int or str :param backup: The file extension to use for a backup of the file before editing. Set to ``False`` to skip making a backup. :param dry_run: Don't make any edits to the file :param search_only: Just search for the pattern; ignore the replacement; stop on the first match :param show_changes: Output a unified diff of the old file and the new file. If ``False`` return a boolean if any changes were made. Note: using this option will store two copies of the file in-memory (the original version and the edited version) in order to generate the diff. :rtype: bool or str CLI Example: .. code-block:: bash salt '*' file.replace /etc/httpd/httpd.conf 'LogLevel warn' 'LogLevel info' salt '*' file.replace /some/file 'before' 'after' flags='[MULTILINE, IGNORECASE]' ''' if not os.path.exists(path): raise SaltInvocationError('File not found: {0}'.format(path)) if not salt.utils.istextfile(path): raise SaltInvocationError( 'Cannot perform string replacements on a binary file: {0}' .format(path) ) flags_num = _get_flags(flags) cpattern = re.compile(pattern, flags_num) if bufsize == 'file': bufsize = os.path.getsize(path) # Search the file; track if any changes have been made for the return val has_changes = False orig_file = [] # used if show_changes new_file = [] # used if show_changes if not salt.utils.is_windows(): pre_user = get_user(path) pre_group = get_group(path) pre_mode = __salt__['config.manage_mode'](get_mode(path)) # Avoid TypeErrors by forcing repl to be a string repl = str(repl) for line in fileinput.input(path, inplace=not dry_run, backup=False if dry_run else backup, bufsize=bufsize, mode='rb'): if search_only: # Just search; bail as early as a match is found result = re.search(cpattern, line) if result: return True else: result = re.sub(cpattern, repl, line, count) # Identity check each potential change until one change is made if has_changes is False and not result == line: has_changes = True if show_changes: orig_file.append(line) new_file.append(result) if not dry_run: print(result, end='', file=sys.stdout) if not dry_run and not salt.utils.is_windows(): check_perms(path, None, pre_user, pre_group, pre_mode) if show_changes: return ''.join(difflib.unified_diff(orig_file, new_file)) return has_changes def search(path, pattern, flags=0, bufsize=1, ): ''' Search for occurances of a pattern in a file .. versionadded:: 0.17 Params are identical to :py:func:`~salt.modules.file.replace`. CLI Example: .. code-block:: bash salt '*' file.search /etc/crontab 'mymaintenance.sh' ''' # This function wraps file.replace on purpose in order to enforce # consistent usage, compatible regex's, expected behavior, *and* bugs. :) # Any enhancements or fixes to one should affect the other. return replace(path, pattern, '', flags=flags, bufsize=bufsize, dry_run=True, search_only=True, show_changes=False) def patch(originalfile, patchfile, options='', dry_run=False): ''' .. versionadded:: 0.10.4 Apply a patch to a file Equivalent to:: patch originalfile The full path to the file or directory to be patched patchfile A patch file to apply to ``originalfile`` options Options to pass to patch. CLI Example: .. code-block:: bash salt '*' file.patch /opt/file.txt /tmp/file.txt.patch ''' if dry_run: if __grains__['kernel'] in ('FreeBSD', 'OpenBSD'): dry_run_opt = ' -C' else: dry_run_opt = ' --dry-run' else: dry_run_opt = '' cmd = 'patch {0}{1} {2} {3}'.format( options, dry_run_opt, originalfile, patchfile) return __salt__['cmd.run_all'](cmd) def contains(path, text): ''' .. deprecated:: 0.17.1 Use :func:`search` instead. Return ``True`` if the file at ``path`` contains ``text`` CLI Example: .. code-block:: bash salt '*' file.contains /etc/crontab 'mymaintenance.sh' ''' if not os.path.exists(path): return False stripped_text = str(text).strip() try: with salt.utils.filebuffer.BufferedReader(path) as breader: for chunk in breader: if stripped_text in chunk: return True return False except (IOError, OSError): return False def contains_regex(path, regex, lchar=''): ''' .. deprecated:: 0.17.1 Use :func:`search` instead. Return True if the given regular expression matches on any line in the text of a given file. If the lchar argument (leading char) is specified, it will strip `lchar` from the left side of each line before trying to match CLI Example: .. code-block:: bash salt '*' file.contains_regex /etc/crontab ''' if not os.path.exists(path): return False try: with salt.utils.fopen(path, 'r') as target: for line in target: if lchar: line = line.lstrip(lchar) if re.search(regex, line): return True return False except (IOError, OSError): return False def contains_regex_multiline(path, regex): ''' .. deprecated:: 0.17.1 Use :func:`search` instead. Return True if the given regular expression matches anything in the text of a given file Traverses multiple lines at a time, via the salt BufferedReader (reads in chunks) CLI Example: .. code-block:: bash salt '*' file.contains_regex_multiline /etc/crontab '^maint' ''' if not os.path.exists(path): return False try: with salt.utils.filebuffer.BufferedReader(path) as breader: for chunk in breader: if re.search(regex, chunk, re.MULTILINE): return True return False except (IOError, OSError): return False def contains_glob(path, glob): ''' .. deprecated:: 0.17.1 Use :func:`search` instead. Return True if the given glob matches a string in the named file CLI Example: .. code-block:: bash salt '*' file.contains_glob /etc/foobar '*cheese*' ''' if not os.path.exists(path): return False try: with salt.utils.filebuffer.BufferedReader(path) as breader: for chunk in breader: if fnmatch.fnmatch(chunk, glob): return True return False except (IOError, OSError): return False def append(path, *args): ''' .. versionadded:: 0.9.5 Append text to the end of a file CLI Example: .. code-block:: bash salt '*' file.append /etc/motd \\ "With all thine offerings thou shalt offer salt." \\ "Salt is what makes things taste bad when it isn't in them." ''' # Largely inspired by Fabric's contrib.files.append() with salt.utils.fopen(path, "a") as ofile: for line in args: ofile.write('{0}\n'.format(line)) return 'Wrote {0} lines to "{1}"'.format(len(args), path) def touch(name, atime=None, mtime=None): ''' .. versionadded:: 0.9.5 Just like the ``touch`` command, create a file if it doesn't exist or simply update the atime and mtime if it already does. atime: Access time in Unix epoch time mtime: Last modification in Unix epoch time CLI Example: .. code-block:: bash salt '*' file.touch /var/log/emptyfile ''' if atime and atime.isdigit(): atime = int(atime) if mtime and mtime.isdigit(): mtime = int(mtime) try: if not os.path.exists(name): salt.utils.fopen(name, 'a') if not atime and not mtime: times = None elif not mtime and atime: times = (atime, time.time()) elif not atime and mtime: times = (time.time(), mtime) else: times = (atime, mtime) os.utime(name, times) except TypeError: raise SaltInvocationError('atime and mtime must be integers') except (IOError, OSError) as exc: raise CommandExecutionError(exc.strerror) return os.path.exists(name) def symlink(src, link): ''' Create a symbolic link to a file CLI Example: .. code-block:: bash salt '*' file.symlink /path/to/file /path/to/link ''' if not os.path.isabs(src): raise SaltInvocationError('File path must be absolute.') try: os.symlink(src, link) return True except (OSError, IOError): raise CommandExecutionError('Could not create {0!r}'.format(link)) return False def rename(src, dst): ''' Rename a file or directory CLI Example: .. code-block:: bash salt '*' file.rename /path/to/src /path/to/dst ''' if not os.path.isabs(src): raise SaltInvocationError('File path must be absolute.') try: os.rename(src, dst) return True except OSError: raise CommandExecutionError( 'Could not rename {0!r} to {1!r}'.format(src, dst) ) return False def copy(src, dst): ''' Copy a file or directory CLI Example: .. code-block:: bash salt '*' file.copy /path/to/src /path/to/dst ''' if not os.path.isabs(src): raise SaltInvocationError('File path must be absolute.') if not salt.utils.is_windows(): pre_user = get_user(src) pre_group = get_group(src) pre_mode = __salt__['config.manage_mode'](get_mode(src)) try: shutil.copyfile(src, dst) except OSError: raise CommandExecutionError( 'Could not copy {0!r} to {1!r}'.format(src, dst) ) if not salt.utils.is_windows(): check_perms(dst, None, pre_user, pre_group, pre_mode) return True def stats(path, hash_type='md5', follow_symlink=False): ''' Return a dict containing the stats for a given file CLI Example: .. code-block:: bash salt '*' file.stats /etc/passwd ''' ret = {} if not os.path.exists(path): return ret if follow_symlink: pstat = os.stat(path) else: pstat = os.lstat(path) ret['inode'] = pstat.st_ino ret['uid'] = pstat.st_uid ret['gid'] = pstat.st_gid ret['group'] = gid_to_group(pstat.st_gid) ret['user'] = uid_to_user(pstat.st_uid) ret['atime'] = pstat.st_atime ret['mtime'] = pstat.st_mtime ret['ctime'] = pstat.st_ctime ret['size'] = pstat.st_size ret['mode'] = str(oct(stat.S_IMODE(pstat.st_mode))) ret['sum'] = get_sum(path, hash_type) ret['type'] = 'file' if stat.S_ISDIR(pstat.st_mode): ret['type'] = 'dir' if stat.S_ISCHR(pstat.st_mode): ret['type'] = 'char' if stat.S_ISBLK(pstat.st_mode): ret['type'] = 'block' if stat.S_ISREG(pstat.st_mode): ret['type'] = 'file' if stat.S_ISLNK(pstat.st_mode): ret['type'] = 'link' if stat.S_ISFIFO(pstat.st_mode): ret['type'] = 'pipe' if stat.S_ISSOCK(pstat.st_mode): ret['type'] = 'socket' ret['target'] = os.path.realpath(path) return ret def remove(path): ''' Remove the named file CLI Example: .. code-block:: bash salt '*' file.remove /tmp/foo ''' if not os.path.isabs(path): raise SaltInvocationError('File path must be absolute.') try: if os.path.isfile(path) or os.path.islink(path): os.remove(path) return True elif os.path.isdir(path): shutil.rmtree(path) return True except (OSError, IOError) as exc: raise CommandExecutionError( 'Could not remove {0!r}: {1}'.format(path, exc) ) return False def directory_exists(path): ''' Tests to see if path is a valid directory. Returns True/False. CLI Example: .. code-block:: bash salt '*' file.directory_exists /etc ''' return os.path.isdir(path) def file_exists(path): ''' Tests to see if path is a valid file. Returns True/False. CLI Example: .. code-block:: bash salt '*' file.file_exists /etc/passwd ''' return os.path.isfile(path) def restorecon(path, recursive=False): ''' Reset the SELinux context on a given path CLI Example: .. code-block:: bash salt '*' file.restorecon /home/user/.ssh/authorized_keys ''' if recursive: cmd = 'restorecon -FR {0}'.format(path) else: cmd = 'restorecon -F {0}'.format(path) return not __salt__['cmd.retcode'](cmd) def get_selinux_context(path): ''' Get an SELinux context from a given path CLI Example: .. code-block:: bash salt '*' file.get_selinux_context /etc/hosts ''' out = __salt__['cmd.run']('ls -Z {0}'.format(path)) return out.split(' ')[4] def set_selinux_context(path, user=None, role=None, type=None, range=None): ''' Set a specific SELinux label on a given path CLI Example: .. code-block:: bash salt '*' file.set_selinux_context path ''' if not any((user, role, type, range)): return False cmd = 'chcon ' if user: cmd += '-u {0} '.format(user) if role: cmd += '-r {0} '.format(role) if type: cmd += '-t {0} '.format(type) if range: cmd += '-l {0} '.format(range) cmd += path ret = not __salt__['cmd.retcode'](cmd) if ret: return get_selinux_context(path) else: return ret def source_list(source, source_hash, env): ''' Check the source list and return the source to use CLI Example: .. code-block:: bash salt '*' file.source_list salt://http/httpd.conf '{hash_type: 'md5', 'hsum': }' base ''' # get the master file list if isinstance(source, list): mfiles = __salt__['cp.list_master'](env) mdirs = __salt__['cp.list_master_dirs'](env) for single in source: if isinstance(single, dict): single = next(iter(single)) try: sname, senv = single.split('?env=') except ValueError: continue else: mfiles += ["{0}?env={1}".format(f, senv) for f in __salt__['cp.list_master'](senv)] mdirs += ["{0}?env={1}".format(d, senv) for d in __salt__['cp.list_master_dirs'](senv)] for single in source: if isinstance(single, dict): # check the proto, if it is http or ftp then download the file # to check, if it is salt then check the master list if len(single) != 1: continue single_src = next(iter(single)) single_hash = single[single_src] proto = salt._compat.urlparse(single_src).scheme if proto == 'salt': if single_src[7:] in mfiles or single_src[7:] in mdirs: source = single_src break elif proto.startswith('http') or proto == 'ftp': dest = salt.utils.mkstemp() fn_ = __salt__['cp.get_url'](single_src, dest) os.remove(fn_) if fn_: source = single_src source_hash = single_hash break elif isinstance(single, salt._compat.string_types): if single[7:] in mfiles or single[7:] in mdirs: source = single break return source, source_hash def get_managed( name, template, source, source_hash, user, group, mode, env, context, defaults, **kwargs): ''' Return the managed file data for file.managed CLI Example: .. code-block:: bash salt '*' file.get_managed /etc/httpd/conf.d/httpd.conf jinja salt://http/httpd.conf '{hash_type: 'md5', 'hsum': }' root root '755' base None None ''' # If the file is a template and the contents is managed # then make sure to copy it down and templatize things. sfn = '' source_sum = {} if template and source: sfn = __salt__['cp.cache_file'](source, env) if not os.path.exists(sfn): return sfn, {}, 'Source file {0} not found'.format(source) if template in salt.utils.templates.TEMPLATE_REGISTRY: context_dict = defaults if defaults else {} if context: context_dict.update(context) data = salt.utils.templates.TEMPLATE_REGISTRY[template]( sfn, name=name, source=source, user=user, group=group, mode=mode, env=env, context=context_dict, salt=__salt__, pillar=__pillar__, grains=__grains__, opts=__opts__, **kwargs) else: return sfn, {}, ('Specified template format {0} is not supported' ).format(template) if data['result']: sfn = data['data'] hsum = get_hash(sfn) source_sum = {'hash_type': 'md5', 'hsum': hsum} else: __clean_tmp(sfn) return sfn, {}, data['data'] else: # Copy the file down if there is a source if source: if salt._compat.urlparse(source).scheme == 'salt': source_sum = __salt__['cp.hash_file'](source, env) if not source_sum: return '', {}, 'Source file {0} not found'.format(source) elif source_hash: protos = ['salt', 'http', 'https', 'ftp'] if salt._compat.urlparse(source_hash).scheme in protos: # The source_hash is a file on a server hash_fn = __salt__['cp.cache_file'](source_hash) if not hash_fn: return '', {}, 'Source hash file {0} not found'.format( source_hash) hash_fn_fopen = salt.utils.fopen(hash_fn, 'r') for line in hash_fn_fopen.read().splitlines(): line = line.strip() if ' ' not in line: hashstr = line break elif line.startswith('{0} '.format(name)): hashstr = line.split()[1] break else: hashstr = '' # NOT FOUND comps = hashstr.split('=') if len(comps) < 2: return '', {}, ('Source hash file {0} contains an ' 'invalid hash format, it must be in ' 'the format =' ).format(source_hash) source_sum['hsum'] = comps[1].strip() source_sum['hash_type'] = comps[0].strip() else: # The source_hash is a hash string comps = source_hash.split('=') if len(comps) < 2: return '', {}, ('Source hash file {0} contains an ' 'invalid hash format, it must be in ' 'the format =' ).format(source_hash) source_sum['hsum'] = comps[1].strip() source_sum['hash_type'] = comps[0].strip() else: return '', {}, ('Unable to determine upstream hash of' ' source file {0}').format(source) return sfn, source_sum, '' def check_perms(name, ret, user, group, mode): ''' Check the permissions on files and chown if needed CLI Example: .. code-block:: bash salt '*' file.check_perms /etc/sudoers '{}' root root 400 ''' if not ret: ret = {'name': name, 'changes': {}, 'comment': [], 'result': True} orig_comment = '' else: orig_comment = ret['comment'] ret['comment'] = [] # Check permissions perms = {} perms['luser'] = get_user(name) perms['lgroup'] = get_group(name) perms['lmode'] = __salt__['config.manage_mode'](get_mode(name)) # Mode changes if needed if mode is not None: mode = __salt__['config.manage_mode'](mode) if mode != perms['lmode']: if __opts__['test'] is True: ret['changes']['mode'] = mode else: set_mode(name, mode) if mode != __salt__['config.manage_mode'](get_mode(name)): ret['result'] = False ret['comment'].append( 'Failed to change mode to {0}'.format(mode) ) else: ret['changes']['mode'] = mode # user/group changes if needed, then check if it worked if user: if user != perms['luser']: perms['cuser'] = user if group: if group != perms['lgroup']: perms['cgroup'] = group if 'cuser' in perms or 'cgroup' in perms: if not __opts__['test']: if user is None: user = perms['luser'] if group is None: group = perms['lgroup'] try: chown(name, user, group) except OSError: ret['result'] = False if user: if user != get_user(name): if __opts__['test'] is True: ret['changes']['user'] = user else: ret['result'] = False ret['comment'].append('Failed to change user to {0}' .format(user)) elif 'cuser' in perms: ret['changes']['user'] = user if group: if group != get_group(name): if __opts__['test'] is True: ret['changes']['group'] = group else: ret['result'] = False ret['comment'].append('Failed to change group to {0}' .format(group)) elif 'cgroup' in perms: ret['changes']['group'] = group if isinstance(orig_comment, basestring): if orig_comment: ret['comment'].insert(0, orig_comment) ret['comment'] = '; '.join(ret['comment']) if __opts__['test'] is True and ret['changes']: ret['result'] = None return ret, perms def check_managed( name, source, source_hash, user, group, mode, template, makedirs, context, defaults, env, contents=None, **kwargs): ''' Check to see what changes need to be made for a file CLI Example: .. code-block:: bash salt '*' file.check_managed /etc/httpd/conf.d/httpd.conf salt://http/httpd.conf '{hash_type: 'md5', 'hsum': }' root, root, '755' jinja True None None base ''' # If the source is a list then find which file exists source, source_hash = source_list(source, source_hash, env) sfn = '' source_sum = None if contents is None: # Gather the source file from the server sfn, source_sum, comments = get_managed( name, template, source, source_hash, user, group, mode, env, context, defaults, **kwargs) if comments: __clean_tmp(sfn) return False, comments changes = check_file_meta(name, sfn, source, source_sum, user, group, mode, env, template, contents) __clean_tmp(sfn) if changes: log.info(changes) comments = ['The following values are set to be changed:\n'] comments.extend('{0}: {1}\n'.format(key, val) for key, val in changes.iteritems()) return None, ''.join(comments) return True, 'The file {0} is in the correct state'.format(name) def check_file_meta( name, sfn, source, source_sum, user, group, mode, env, template=None, contents=None): ''' Check for the changes in the file metadata. CLI Example: .. code-block:: bash salt '*' file.check_file_meta /etc/httpd/conf.d/httpd.conf salt://http/httpd.conf '{hash_type: 'md5', 'hsum': }' root, root, '755' base ''' changes = {} if not source_sum: source_sum = dict() lstats = stats(name, source_sum.get('hash_type'), 'md5') if not lstats: changes['newfile'] = name return changes if 'hsum' in source_sum: if source_sum['hsum'] != lstats['sum']: if not sfn and source: sfn = __salt__['cp.cache_file'](source, env) if sfn: if __salt__['config.option']('obfuscate_templates'): changes['diff'] = '' else: # Check to see if the files are bins bdiff = _binary_replace(name, sfn) if bdiff: changes['diff'] = bdiff else: with contextlib.nested( salt.utils.fopen(sfn, 'rb'), salt.utils.fopen(name, 'rb')) as (src, name_): slines = src.readlines() nlines = name_.readlines() changes['diff'] = \ ''.join(difflib.unified_diff(nlines, slines)) else: changes['sum'] = 'Checksum differs' if contents is not None: # Write a tempfile with the static contents tmp = salt.utils.mkstemp(text=True) with salt.utils.fopen(tmp, 'w') as tmp_: tmp_.write(str(contents)) # Compare the static contents with the named file with contextlib.nested( salt.utils.fopen(tmp, 'rb'), salt.utils.fopen(name, 'rb')) as (src, name_): slines = src.readlines() nlines = name_.readlines() if ''.join(nlines) != ''.join(slines): if __salt__['config.option']('obfuscate_templates'): changes['diff'] = '' else: if salt.utils.istextfile(name): changes['diff'] = \ ''.join(difflib.unified_diff(nlines, slines)) else: changes['diff'] = 'Replace binary file with text file' if user is not None and user != lstats['user']: changes['user'] = user if group is not None and group != lstats['group']: changes['group'] = group # Normalize the file mode smode = __salt__['config.manage_mode'](lstats['mode']) mode = __salt__['config.manage_mode'](mode) if mode is not None and mode != smode: changes['mode'] = mode return changes def get_diff( minionfile, masterfile, env='base'): ''' Return unified diff of file compared to file on master CLI Example: .. code-block:: bash salt '*' file.get_diff /home/fred/.vimrc salt://users/fred/.vimrc ''' ret = '' if not os.path.exists(minionfile): ret = 'File {0} does not exist on the minion'.format(minionfile) return ret sfn = __salt__['cp.cache_file'](masterfile, env) if sfn: with contextlib.nested(salt.utils.fopen(sfn, 'r'), salt.utils.fopen(minionfile, 'r')) \ as (src, name_): slines = src.readlines() nlines = name_.readlines() if ''.join(nlines) != ''.join(slines): bdiff = _binary_replace(minionfile, sfn) if bdiff: ret += bdiff else: ret += ''.join(difflib.unified_diff(nlines, slines, minionfile, masterfile)) else: ret = 'Failed to copy file from master' return ret def manage_file(name, sfn, ret, source, source_sum, user, group, mode, env, backup, template=None, show_diff=True, contents=None, dir_mode=None): ''' Checks the destination against what was retrieved with get_managed and makes the appropriate modifications (if necessary). CLI Example: .. code-block:: bash salt '*' file.manage_file /etc/httpd/conf.d/httpd.conf '{}' salt://http/httpd.conf '{hash_type: 'md5', 'hsum': }' root root '755' base '' ''' if not ret: ret = {'name': name, 'changes': {}, 'comment': '', 'result': True} # Check changes if the target file exists if os.path.isfile(name): # Only test the checksums on files with managed contents if source: name_sum = get_hash(name, source_sum['hash_type']) # Check if file needs to be replaced if source and source_sum['hsum'] != name_sum: if not sfn: sfn = __salt__['cp.cache_file'](source, env) if not sfn: return _error( ret, 'Source file {0} not found'.format(source)) # If the downloaded file came from a non salt server source verify # that it matches the intended sum value if salt._compat.urlparse(source).scheme != 'salt': dl_sum = get_hash(sfn, source_sum['hash_type']) if dl_sum != source_sum['hsum']: ret['comment'] = ('File sum set for file {0} of {1} does ' 'not match real sum of {2}' ).format(name, source_sum['hsum'], dl_sum) ret['result'] = False return ret # Print a diff equivalent to diff -u old new if __salt__['config.option']('obfuscate_templates'): ret['changes']['diff'] = '' elif not show_diff: ret['changes']['diff'] = '' else: # Check to see if the files are bins bdiff = _binary_replace(name, sfn) if bdiff: ret['changes']['diff'] = bdiff else: with contextlib.nested( salt.utils.fopen(sfn, 'rb'), salt.utils.fopen(name, 'rb')) as (src, name_): slines = src.readlines() nlines = name_.readlines() ret['changes']['diff'] = \ ''.join(difflib.unified_diff(nlines, slines)) # Pre requisites are met, and the file needs to be replaced, do it try: salt.utils.copyfile(sfn, name, __salt__['config.backup_mode'](backup), __opts__['cachedir']) except IOError: __clean_tmp(sfn) return _error( ret, 'Failed to commit change, permission error') if contents is not None: # Write the static contents to a temporary file tmp = salt.utils.mkstemp(text=True) with salt.utils.fopen(tmp, 'w') as tmp_: tmp_.write(str(contents)) # Compare contents of files to know if we need to replace with contextlib.nested( salt.utils.fopen(tmp, 'rb'), salt.utils.fopen(name, 'rb')) as (src, name_): slines = src.readlines() nlines = name_.readlines() different = ''.join(slines) != ''.join(nlines) if different: if __salt__['config.option']('obfuscate_templates'): ret['changes']['diff'] = '' elif not show_diff: ret['changes']['diff'] = '' else: if salt.utils.istextfile(name): ret['changes']['diff'] = \ ''.join(difflib.unified_diff(nlines, slines)) else: ret['changes']['diff'] = \ 'Replace binary file with text file' # Pre requisites are met, the file needs to be replaced, do it try: salt.utils.copyfile(tmp, name, __salt__['config.backup_mode'](backup), __opts__['cachedir']) except IOError: __clean_tmp(tmp) return _error( ret, 'Failed to commit change, permission error') __clean_tmp(tmp) ret, perms = check_perms(name, ret, user, group, mode) if ret['changes']: ret['comment'] = 'File {0} updated'.format(name) elif not ret['changes'] and ret['result']: ret['comment'] = 'File {0} is in the correct state'.format(name) __clean_tmp(sfn) return ret else: # Only set the diff if the file contents is managed if source: # It is a new file, set the diff accordingly ret['changes']['diff'] = 'New file' # Apply the new file if not sfn: sfn = __salt__['cp.cache_file'](source, env) if not sfn: return _error( ret, 'Source file {0} not found'.format(source)) # If the downloaded file came from a non salt server source verify # that it matches the intended sum value if salt._compat.urlparse(source).scheme != 'salt': dl_sum = get_hash(sfn, source_sum['hash_type']) if dl_sum != source_sum['hsum']: ret['comment'] = ('File sum set for file {0} of {1} does ' 'not match real sum of {2}' ).format(name, source_sum['hsum'], dl_sum) ret['result'] = False return ret if not os.path.isdir(os.path.dirname(name)): if makedirs: makedirs(name, user=user, group=group, mode=dir_mode or mode) else: __clean_tmp(sfn) return _error(ret, 'Parent directory not present') else: if not os.path.isdir(os.path.dirname(name)): if makedirs: makedirs(name, user=user, group=group, mode=mode) else: __clean_tmp(sfn) return _error(ret, 'Parent directory not present') # Create the file, user rw-only if mode will be set to prevent # a small security race problem before the permissions are set if mode: current_umask = os.umask(077) # Create a new file when test is False and source is None if contents is None: if not __opts__['test']: if touch(name): ret['changes']['new'] = 'file {0} created'.format(name) ret['comment'] = 'Empty file' else: return _error( ret, 'Empty file {0} not created'.format(name) ) else: if not __opts__['test']: if touch(name): ret['changes']['diff'] = 'New file' else: return _error( ret, 'File {0} not created'.format(name) ) if mode: os.umask(current_umask) if contents is not None: # Write the static contents to a temporary file tmp = salt.utils.mkstemp(text=True) with salt.utils.fopen(tmp, 'w') as tmp_: tmp_.write(str(contents)) # Copy into place salt.utils.copyfile(tmp, name, __salt__['config.backup_mode'](backup), __opts__['cachedir']) __clean_tmp(tmp) # Now copy the file contents if there is a source file elif sfn: salt.utils.copyfile(sfn, name, __salt__['config.backup_mode'](backup), __opts__['cachedir']) __clean_tmp(sfn) # This is a new file, if no mode specified, use the umask to figure # out what mode to use for the new file. if mode is None and not salt.utils.is_windows(): # Get current umask mask = os.umask(0) os.umask(mask) # Calculate the mode value that results from the umask mode = oct((0777 ^ mask) & 0666) ret, perms = check_perms(name, ret, user, group, mode) if not ret['comment']: ret['comment'] = 'File ' + name + ' updated' if __opts__['test']: ret['comment'] = 'File ' + name + ' not updated' elif not ret['changes'] and ret['result']: ret['comment'] = 'File ' + name + ' is in the correct state' __clean_tmp(sfn) return ret def mkdir(dir_path, user=None, group=None, mode=None): ''' Ensure that a directory is available. CLI Example: .. code-block:: bash salt '*' file.mkdir /opt/jetty/context ''' directory = os.path.normpath(dir_path) if not os.path.isdir(directory): # If a caller such as managed() is invoked with makedirs=True, make # sure that any created dirs are created with the same user and group # to follow the principal of least surprise method. makedirs_perms(directory, user, group, mode) def makedirs(path, user=None, group=None, mode=None): ''' Ensure that the directory containing this path is available. CLI Example: .. code-block:: bash salt '*' file.makedirs /opt/code ''' # walk up the directory structure until we find the first existing # directory dirname = os.path.normpath(os.path.dirname(path)) if os.path.isdir(dirname): # There's nothing for us to do return 'Directory {0!r} already exists'.format(path) if os.path.exists(dirname): return 'The path {0!r} already exists and is not a directory'.format( path ) directories_to_create = [] while True: if os.path.isdir(dirname): break directories_to_create.append(dirname) dirname = os.path.dirname(dirname) # create parent directories from the topmost to the most deeply nested one directories_to_create.reverse() for directory_to_create in directories_to_create: # all directories have the user, group and mode set!! mkdir(directory_to_create, user=user, group=group, mode=mode) def makedirs_perms(name, user=None, group=None, mode='0755'): ''' Taken and modified from os.makedirs to set user, group and mode for each directory created. CLI Example: .. code-block:: bash salt '*' file.makedirs_perms /opt/code ''' path = os.path head, tail = path.split(name) if not tail: head, tail = path.split(head) if head and tail and not path.exists(head): try: makedirs_perms(head, user, group, mode) except OSError as exc: # be happy if someone already created the path if exc.errno != errno.EEXIST: raise if tail == os.curdir: # xxx/newdir/. exists if xxx/newdir exists return os.mkdir(name) check_perms(name, None, user, group, int('{0}'.format(mode)) if mode else None) def get_devmm(name): ''' Get major/minor info from a device CLI Example: .. code-block:: bash salt '*' file.get_devmm /dev/chr ''' if is_chrdev(name) or is_blkdev(name): stat_structure = os.stat(name) return ( os.major(stat_structure.st_rdev), os.minor(stat_structure.st_rdev)) else: return (0, 0) def is_chrdev(name): ''' Check if a file exists and is a character device. CLI Example: .. code-block:: bash salt '*' file.is_chrdev /dev/chr ''' stat_structure = None try: stat_structure = os.stat(name) except OSError as exc: if exc.errno == errno.ENOENT: #If the character device does not exist in the first place return False else: raise return stat.S_ISCHR(stat_structure.st_mode) def mknod_chrdev(name, major, minor, user=None, group=None, mode='0660'): ''' Create a character device. CLI Example: .. code-block:: bash salt '*' file.mknod_chrdev /dev/chr 180 31 ''' ret = {'name': name, 'changes': {}, 'comment': '', 'result': False} log.debug("Creating character device name:{0} major:{1} minor:{2} mode:{3}".format(name, major, minor, mode)) try: if __opts__['test']: ret['changes'] = {'new': 'Character device {0} created.'.format(name)} ret['result'] = None else: if os.mknod(name, int(str(mode).lstrip('0'), 8) | stat.S_IFCHR, os.makedev(major, minor)) is None: ret['changes'] = {'new': 'Character device {0} created.'.format(name)} ret['result'] = True except OSError as exc: # be happy it is already there....however, if you are trying to change the # major/minor, you will need to unlink it first as os.mknod will not overwrite if exc.errno != errno.EEXIST: raise else: ret['comment'] = 'File {0} exists and cannot be overwritten'.format(name) #quick pass at verifying the permissions of the newly created character device check_perms(name, None, user, group, int('{0}'.format(mode)) if mode else None) return ret def is_blkdev(name): ''' Check if a file exists and is a block device. CLI Example: .. code-block:: bash salt '*' file.is_blkdev /dev/blk ''' stat_structure = None try: stat_structure = os.stat(name) except OSError as exc: if exc.errno == errno.ENOENT: #If the block device does not exist in the first place return False else: raise return stat.S_ISBLK(stat_structure.st_mode) def mknod_blkdev(name, major, minor, user=None, group=None, mode='0660'): ''' Create a block device. CLI Example: .. code-block:: bash salt '*' file.mknod_blkdev /dev/blk 8 999 ''' ret = {'name': name, 'changes': {}, 'comment': '', 'result': False} log.debug("Creating block device name:{0} major:{1} minor:{2} mode:{3}".format(name, major, minor, mode)) try: if __opts__['test']: ret['changes'] = {'new': 'Block device {0} created.'.format(name)} ret['result'] = None else: if os.mknod(name, int(str(mode).lstrip('0'), 8) | stat.S_IFBLK, os.makedev(major, minor)) is None: ret['changes'] = {'new': 'Block device {0} created.'.format(name)} ret['result'] = True except OSError as exc: # be happy it is already there....however, if you are trying to change the # major/minor, you will need to unlink it first as os.mknod will not overwrite if exc.errno != errno.EEXIST: raise else: ret['comment'] = 'File {0} exists and cannot be overwritten'.format(name) #quick pass at verifying the permissions of the newly created block device check_perms(name, None, user, group, int('{0}'.format(mode)) if mode else None) return ret def is_fifo(name): ''' Check if a file exists and is a FIFO. CLI Example: .. code-block:: bash salt '*' file.is_fifo /dev/fifo ''' stat_structure = None try: stat_structure = os.stat(name) except OSError as exc: if exc.errno == errno.ENOENT: #If the fifo does not exist in the first place return False else: raise return stat.S_ISFIFO(stat_structure.st_mode) def mknod_fifo(name, user=None, group=None, mode='0660'): ''' Create a FIFO pipe. CLI Example: .. code-block:: bash salt '*' file.mknod_fifo /dev/fifo ''' ret = {'name': name, 'changes': {}, 'comment': '', 'result': False} log.debug("Creating FIFO name:{0}".format(name)) try: if __opts__['test']: ret['changes'] = {'new': 'Fifo pipe {0} created.'.format(name)} ret['result'] = None else: if os.mkfifo(name, int(str(mode).lstrip('0'), 8)) is None: ret['changes'] = {'new': 'Fifo pipe {0} created.'.format(name)} ret['result'] = True except OSError as exc: #be happy it is already there if exc.errno != errno.EEXIST: raise else: ret['comment'] = 'File {0} exists and cannot be overwritten'.format(name) #quick pass at verifying the permissions of the newly created fifo check_perms(name, None, user, group, int('{0}'.format(mode)) if mode else None) return ret def mknod(name, ntype, major=0, minor=0, user=None, group=None, mode='0600'): ''' Create a block device, character device, or fifo pipe. Identical to the gnu mknod. CLI Examples: .. code-block:: bash salt '*' file.mknod /dev/chr c 180 31 salt '*' file.mknod /dev/blk b 8 999 salt '*' file.nknod /dev/fifo p ''' ret = False makedirs(name, user, group) if ntype == 'c': ret = mknod_chrdev(name, major, minor, user, group, mode) elif ntype == 'b': ret = mknod_blkdev(name, major, minor, user, group, mode) elif ntype == 'p': ret = mknod_fifo(name, user, group, mode) else: raise Exception("Node type unavailable: '{0}'. Available node types are character ('c'), block ('b'), and pipe ('p').".format(ntype)) return ret def list_backups(path, limit=None): ''' .. note:: This function will be available in version 0.17.0. Lists the previous versions of a file backed up using Salt's :doc:`file state backup ` system. path The path on the minion to check for backups limit Limit the number of results to the most recent N backups CLI Example: .. code-block:: bash salt '*' file.list_backups /foo/bar/baz.txt ''' try: limit = int(limit) except TypeError: pass except ValueError: log.error('file.list_backups: \'limit\' value must be numeric') limit = None bkroot = _get_bkroot() parent_dir, basename = os.path.split(path) # Figure out full path of location of backup file in minion cache bkdir = os.path.join(bkroot, parent_dir[1:]) files = {} for fn in [x for x in os.listdir(bkdir) if os.path.isfile(os.path.join(bkdir, x))]: strpfmt = '{0}_%a_%b_%d_%H:%M:%S_%f_%Y'.format(basename) try: timestamp = datetime.datetime.strptime(fn, strpfmt) except ValueError: # File didn't match the strp format string, so it's not a backup # for this file. Move on to the next one. continue files.setdefault(timestamp, {})['Backup Time'] = \ timestamp.strftime('%a %b %d %Y %H:%M:%S.%f') location = os.path.join(bkdir, fn) files[timestamp]['Size'] = os.stat(location).st_size files[timestamp]['Location'] = location return dict(zip( range(len(files)), [files[x] for x in sorted(files, reverse=True)[:limit]] )) list_backup = list_backups def restore_backup(path, backup_id): ''' .. note:: This function will be available in version 0.17.0. Restore a previous version of a file that was backed up using Salt's :doc:`file state backup ` system. path The path on the minion to check for backups backup_id The numeric id for the backup you wish to restore, as found using :mod:`file.list_backups ` CLI Example: .. code-block:: bash salt '*' file.restore_backup /foo/bar/baz.txt 0 ''' # Note: This only supports minion backups, so this function will need to be # modified if/when master backups are implemented. ret = {'result': False, 'comment': 'Invalid backup_id \'{0}\''.format(backup_id)} try: if len(str(backup_id)) == len(str(int(backup_id))): backup = list_backups(path)[int(backup_id)] else: return ret except ValueError: return ret except KeyError: ret['comment'] = 'backup_id \'{0}\' does not exist for ' \ '{1}'.format(backup_id, path) return ret salt.utils.backup_minion(path, _get_bkroot()) try: shutil.copyfile(backup['Location'], path) except IOError as exc: ret['comment'] = \ 'Unable to restore {0} to {1}: ' \ '{2}'.format(backup['Location'], path, exc) return ret else: ret['result'] = True ret['comment'] = 'Successfully restored {0} to ' \ '{1}'.format(backup['Location'], path) # Try to set proper ownership try: fstat = os.stat(path) except (OSError, IOError): ret['comment'] += ', but was unable to set ownership' else: os.chown(path, fstat.st_uid, fstat.st_gid) return ret def delete_backup(path, backup_id): ''' .. note:: This function will be available in version 0.17.0. Restore a previous version of a file that was backed up using Salt's :doc:`file state backup ` system. path The path on the minion to check for backups backup_id The numeric id for the backup you wish to delete, as found using :mod:`file.list_backups ` CLI Example: .. code-block:: bash salt '*' file.restore_backup /foo/bar/baz.txt 0 ''' ret = {'result': False, 'comment': 'Invalid backup_id \'{0}\''.format(backup_id)} try: if len(str(backup_id)) == len(str(int(backup_id))): backup = list_backups(path)[int(backup_id)] else: return ret except ValueError: return ret except KeyError: ret['comment'] = 'backup_id \'{0}\' does not exist for ' \ '{1}'.format(backup_id, path) return ret try: os.remove(backup['Location']) except IOError as exc: ret['comment'] = 'Unable to remove {0}: {1}'.format(backup['Location'], exc) else: ret['result'] = True ret['comment'] = 'Successfully removed {0}'.format(backup['Location']) return ret remove_backup = delete_backup salt-0.17.5+ds.orig/salt/modules/rabbitmq.py0000644000175000017500000002410712270576114017023 0ustar joejoe# -*- coding: utf-8 -*- ''' Module to provide RabbitMQ compatibility to Salt. Todo: A lot, need to add cluster support, logging, and minion configuration data. ''' # Import salt libs from salt import exceptions, utils # Import python libs import logging log = logging.getLogger(__name__) def __virtual__(): '''Verify RabbitMQ is installed. ''' name = 'rabbitmq' try: utils.check_or_die('rabbitmqctl') except exceptions.CommandNotFoundError: name = False return name def _format_response(response, msg): if 'Error' in response: msg = 'Error' return { msg: response.replace('\n', '') } def list_users(runas=None): ''' Return a list of users based off of rabbitmqctl user_list. CLI Example: .. code-block:: bash salt '*' rabbitmq.list_users ''' ret = {} res = __salt__['cmd.run']('rabbitmqctl list_users', runas=runas) for line in res.splitlines(): if '...' not in line or line == '\n': parts = line.split('\t') if len(parts) < 2: continue user, properties = parts[0], parts[1] ret[user] = properties return ret def list_vhosts(runas=None): ''' Return a list of vhost based on rabbitmqctl list_vhosts. CLI Example: .. code-block:: bash salt '*' rabbitmq.list_vhosts ''' res = __salt__['cmd.run']('rabbitmqctl list_vhosts', runas=runas) lines = res.splitlines() vhost_list = [line for line in lines if '...' not in line] return vhost_list def user_exists(name, runas=None): ''' Return whether the user exists based on rabbitmqctl list_users. CLI Example: .. code-block:: bash salt '*' rabbitmq.user_exists rabbit_user ''' user_list = list_users(runas=runas) log.debug(user_list) return name in user_list def vhost_exists(name, runas=None): ''' Return whether the vhost exists based on rabbitmqctl list_vhosts. CLI Example: .. code-block:: bash salt '*' rabbitmq.vhost_exists rabbit_host ''' return name in list_vhosts(runas=runas) def add_user(name, password, runas=None): ''' Add a rabbitMQ user via rabbitmqctl user_add CLI Example: .. code-block:: bash salt '*' rabbitmq.add_user rabbit_user password ''' res = __salt__['cmd.run']( 'rabbitmqctl add_user {0} \'{1}\''.format(name, password), runas=runas) msg = 'Added' return _format_response(res, msg) def delete_user(name, runas=None): ''' Deletes a user via rabbitmqctl delete_user. CLI Example: .. code-block:: bash salt '*' rabbitmq.delete_user rabbit_user ''' res = __salt__['cmd.run']('rabbitmqctl delete_user {0}'.format(name), runas=runas) msg = 'Deleted' return _format_response(res, msg) def change_password(name, password, runas=None): ''' Changes a user's password. CLI Example: .. code-block:: bash salt '*' rabbitmq.change_password rabbit_user password ''' res = __salt__['cmd.run']( 'rabbitmqctl change_password {0} \'{1}\''.format(name, password), runas=runas) msg = 'Password Changed' return _format_response(res, msg) def clear_password(name, runas=None): ''' Removes a user's password. CLI Example: .. code-block:: bash salt '*' rabbitmq.clear_password rabbit_user ''' res = __salt__['cmd.run']('rabbitmqctl clear_password {0}'.format(name), runas=runas) msg = 'Password Cleared' return _format_response(res, msg) def add_vhost(vhost, runas=None): ''' Adds a vhost via rabbitmqctl add_vhost. CLI Example: .. code-block:: bash salt '*' rabbitmq add_vhost '' ''' res = __salt__['cmd.run']('rabbitmqctl add_vhost {0}'.format(vhost), runas=runas) msg = 'Added' return _format_response(res, msg) def delete_vhost(vhost, runas=None): ''' Deletes a vhost rabbitmqctl delete_vhost. CLI Example: .. code-block:: bash salt '*' rabbitmq.delete_vhost '' ''' res = __salt__['cmd.run']('rabbitmqctl delete_vhost {0}'.format(vhost), runas=runas) msg = 'Deleted' return _format_response(res, msg) def set_permissions(vhost, user, conf='.*', write='.*', read='.*', runas=None): ''' Sets permissions for vhost via rabbitmqctl set_permissions CLI Example: .. code-block:: bash salt '*' rabbitmq.set_permissions 'myvhost' 'myuser' ''' res = __salt__['cmd.run']( 'rabbitmqctl set_permissions -p {0} {1} "{2}" "{3}" "{4}"'.format( vhost, user, conf, write, read), runas=runas) msg = 'Permissions Set' return _format_response(res, msg) def list_user_permissions(name, user=None): ''' List permissions for a user via rabbitmqctl list_user_permissions CLI Example: .. code-block:: bash salt '*' rabbitmq.list_user_permissions 'user'. ''' res = __salt__['cmd.run']( 'rabbitmqctl list_user_permissions {0}'.format(name), runas=user) return [r.split('\t') for r in res.splitlines()] def status(user=None): ''' return rabbitmq status CLI Example: .. code-block:: bash salt '*' rabbitmq.status ''' res = __salt__['cmd.run']( 'rabbitmqctl status', runas=user ) return res def cluster_status(user=None): ''' return rabbitmq cluster_status CLI Example: .. code-block:: bash salt '*' rabbitmq.cluster_status ''' ret = {} res = __salt__['cmd.run']( 'rabbitmqctl cluster_status', runas=user) return res def stop_app(runas=None): ''' Stops the RabbitMQ application, leaving the Erlang node running. CLI Example: .. code-block:: bash salt '*' rabbitmq.stop_app ''' res = __salt__['cmd.run']( 'rabbitmqctl stop_app', runas=runas) return res def start_app(runas=None): ''' Start the RabbitMQ application. CLI Example: .. code-block:: bash salt '*' rabbitmq.start_app ''' res = __salt__['cmd.run']( 'rabbitmqctl start_app', runas=runas) return res def reset(runas=None): ''' Return a RabbitMQ node to its virgin state CLI Example: .. code-block:: bash salt '*' rabbitmq.reset ''' res = __salt__['cmd.run']( 'rabbitmqctl reset', runas=runas) return res def force_reset(runas=None): ''' Forcefully Return a RabbitMQ node to its virgin state CLI Example: .. code-block:: bash salt '*' rabbitmq.force_reset ''' res = __salt__['cmd.run']( 'rabbitmqctl force_reset', runas=runas) return res def list_queues(*kwargs): ''' Returns queue details of the / virtual host CLI Example: .. code-block:: bash salt '*' rabbitmq.list_queues messages consumers ''' res = __salt__['cmd.run']( 'rabbitmqctl list_queues {0}'.format(' '.join(list(kwargs)))) return res def list_queues_vhost(vhost, *kwargs): ''' Returns queue details of specified virtual host. This command will consider first parameter as the vhost name and rest will be treated as queueinfoitem. For getting details on vhost ``/``, use :mod:`list_queues ` instead). CLI Example: .. code-block:: bash salt '*' rabbitmq.list_queues messages consumers ''' res = __salt__['cmd.run']( 'rabbitmqctl list_queues -p {0} {1}'.format(vhost, ' '.join(list(kwargs)))) return res def list_policies(runas=None): ''' Return a dictionary of policies nested by vhost and name based on the data returned from rabbitmqctl list_policies. Reference: http://www.rabbitmq.com/ha.html CLI Example: .. code-block:: bash salt '*' rabbitmq.list_policies' ''' ret = {} res = __salt__['cmd.run']('rabbitmqctl list_policies', runas=runas) for line in res.splitlines(): if '...' not in line and line != '\n': parts = line.split('\t') if len(parts) != 5: continue vhost, name = parts[0], parts[1] if vhost not in ret: ret[vhost] = {} ret[vhost][name] = { 'pattern': parts[2], 'definition': parts[3], 'priority': parts[4] } log.debug('Listing policies: {0}'.format(ret)) return ret def set_policy(vhost, name, pattern, definition, priority=0, runas=None): ''' Set a policy based on rabbitmqctl set_policy. Reference: http://www.rabbitmq.com/ha.html CLI Example: .. code-block:: bash salt '*' rabbitmq.set_policy / HA '.*' '{"ha-mode": "all"}' ''' res = __salt__['cmd.run']( "rabbitmqctl set_policy -p {0} {1} '{2}' '{3}' {4}".format( vhost, name, pattern, definition.replace("'", '"'), priority), runas=runas) log.debug('Set policy: {0}'.format(res)) return _format_response(res, 'Set') def delete_policy(vhost, name, runas=None): ''' Delete a policy based on rabbitmqctl clear_policy. Reference: http://www.rabbitmq.com/ha.html CLI Example: .. code-block:: bash salt '*' rabbitmq.delete_policy / HA' ''' res = __salt__['cmd.run']( 'rabbitmqctl clear_policy -p {0} {1}'.format( vhost, name), runas=runas) log.debug('Delete policy: {0}'.format(res)) return _format_response(res, 'Deleted') def policy_exists(vhost, name, runas=None): ''' Return whether the policy exists based on rabbitmqctl list_policies. Reference: http://www.rabbitmq.com/ha.html CLI Example: .. code-block:: bash salt '*' rabbitmq.policy_exists / HA ''' policies = list_policies(runas=runas) return bool(vhost in policies and name in policies[vhost]) salt-0.17.5+ds.orig/salt/modules/zfs.py0000644000175000017500000000664012263042153016017 0ustar joejoe# -*- coding: utf-8 -*- ''' Module for running ZFS command ''' # Import Python libs import logging # Some std libraries that are made # use of. import re import sys # Import Salt libs import salt.utils import salt.utils.decorators as decorators import salt.modules.cmdmod as salt_cmd log = logging.getLogger(__name__) # Function alias to set mapping. Filled # in later on. __func_alias__ = {} @decorators.memoize def _check_zfs(): ''' Looks to see if zfs is present on the system. ''' # Get the path to the zfs binary. return salt.utils.which('zfs') def _available_commands(): ''' List available commands based on 'zfs help'. Returns a dict. ''' zfs_path = _check_zfs() if not zfs_path: return False _return = {} # Note that we append '|| :' as a unix hack to force return code to be 0. res = salt_cmd.run_all('{0} help || :'.format(zfs_path)) # This bit is dependent on specific output from `zfs help` - any major changes # in how this works upstream will require a change. for line in res['stderr'].splitlines(): if re.match(' [a-zA-Z]', line): cmds = line.split(' ')[0].split('|') doc = ' '.join(line.split(' ')[1:]) for cmd in [cmd.strip() for cmd in cmds]: if cmd not in _return: _return[cmd] = doc return _return def _exit_status(retcode): ''' Translate exit status of zfs ''' ret = {0: 'Successful completion.', 1: 'An error occurred.', 2: 'Usage error.' }[retcode] return ret def __virtual__(): ''' Makes sure that ZFS is available. ''' if _check_zfs(): return 'zfs' return False def _add_doc(func, doc, prefix='\n\n '): if not func.__doc__: func.__doc__ = '' func.__doc__ += '{0}{1}'.format(prefix, doc) def _make_function(cmd_name, doc): ''' Returns a function based on the command name. ''' def _cmd(*args): # Define a return value. ret = {} # Run the command. res = salt_cmd.run_all( '{0} {1} {2}'.format( _check_zfs(), cmd_name, ' '.join(args) ) ) # Make a note of the error in the return object if retcode # not 0. if res['retcode'] != 0: ret['Error'] = _exit_status(res['retcode']) # Set the output to be splitlines for now. ret = res['stdout'].splitlines() return ret _add_doc(_cmd, 'This function is dynamically generated.', '\n ') _add_doc(_cmd, doc) _add_doc(_cmd, '\n CLI Example:\n\n') _add_doc(_cmd, '\n salt \'*\' zfs.{0} '.format(cmd_name)) # At this point return the function we've just defined. return _cmd # Run through all the available commands if _check_zfs(): available_cmds = _available_commands() for available_cmd in available_cmds: # Set the output from _make_function to be 'available_cmd_'. # ie 'list' becomes 'list_' in local module. setattr( sys.modules[__name__], '{0}_'.format(available_cmd), _make_function(available_cmd, available_cmds[available_cmd]) ) # Update the function alias so that salt finds the functions properly. __func_alias__['{0}_'.format(available_cmd)] = available_cmd salt-0.17.5+ds.orig/salt/modules/portage_config.py0000644000175000017500000003541112270576120020205 0ustar joejoe# -*- coding: utf-8 -*- ''' Configure ``portage(5)`` ''' # Import python libs import os import shutil # Import salt libs import salt.utils # Import third party libs try: import portage HAS_PORTAGE = True except ImportError: HAS_PORTAGE = False import sys if os.path.isdir('/usr/lib/portage/pym'): try: # In a virtualenv, the portage python path needs to be manually # added sys.path.insert(0, '/usr/lib/portage/pym') import portage HAS_PORTAGE = True except ImportError: pass BASE_PATH = '/etc/portage/package.{0}' SUPPORTED_CONFS = ('accept_keywords', 'env', 'license', 'mask', 'properties', 'unmask', 'use') def __virtual__(): ''' Confirm this module is on a Gentoo based system. ''' if HAS_PORTAGE and __grains__['os'] == 'Gentoo': return 'portage_config' return False def _porttree(): return portage.db[portage.root]['porttree'] def _p_to_cp(p): ''' Convert a package name or a DEPEND atom to category/package format. Raises an exception if program name is ambigous. ''' ret = _porttree().dbapi.xmatch("match-all", p) if ret: return portage.cpv_getkey(ret[0]) return None def enforce_nice_config(): ''' Enforce a nice tree structure for /etc/portage/package.* configuration files. .. seealso:: :py:func:`salt.modules.ebuild.ex_mod_init` for information on automatically running this when pkg is used. CLI Example: .. code-block:: bash salt '*' portage_config.enforce_nice_config ''' _convert_all_package_confs_to_dir() _order_all_package_confs() def _convert_all_package_confs_to_dir(): ''' Convert all /etc/portage/package.* configuration files to directories. ''' for conf_file in SUPPORTED_CONFS: _package_conf_file_to_dir(conf_file) def _order_all_package_confs(): ''' Place all entries in /etc/portage/package.* config dirs in the correct file. ''' for conf_file in SUPPORTED_CONFS: _package_conf_ordering(conf_file) _unify_keywords() def _unify_keywords(): ''' Merge /etc/portage/package.keywords and /etc/portage/package.accept_keywords. ''' old_path = BASE_PATH.format('keywords') if os.path.exists(old_path): if os.path.isdir(old_path): for triplet in os.walk(old_path): for file_name in triplet[2]: file_path = '{0}/{1}'.format(triplet[0], file_name) with salt.utils.fopen(file_path) as fh_: for line in fh_: line = line.strip() if line and not line.startswith('#'): append_to_package_conf( 'accept_keywords', string=line) shutil.rmtree(old_path) else: with salt.utils.fopen(old_path) as fh_: for line in fh_: line = line.strip() if line and not line.startswith('#'): append_to_package_conf('accept_keywords', string=line) os.remove(old_path) def _package_conf_file_to_dir(file_name): ''' Convert a config file to a config directory. ''' if file_name in SUPPORTED_CONFS: path = BASE_PATH.format(file_name) if os.path.exists(path): if os.path.isdir(path): return False else: os.rename(path, path + '.tmpbak') os.mkdir(path, 0755) with salt.utils.fopen(path + '.tmpbak') as fh_: for line in fh_: line = line.strip() if line and not line.startswith('#'): append_to_package_conf(file_name, string=line) os.remove(path + '.tmpbak') return True else: os.mkdir(path, 0755) return True def _package_conf_ordering(conf, clean=True, keep_backup=False): ''' Move entries in the correct file. ''' if conf in SUPPORTED_CONFS: rearrange = [] path = BASE_PATH.format(conf) backup_files = [] for triplet in os.walk(path): for file_name in triplet[2]: file_path = '{0}/{1}'.format(triplet[0], file_name) cp = triplet[0][len(path) + 1:] + '/' + file_name shutil.copy(file_path, file_path + '.bak') backup_files.append(file_path + '.bak') if cp[0] == '/' or cp.split('/') > 2: rearrange.extend(list(salt.utils.fopen(file_path))) os.remove(file_path) else: new_contents = '' with salt.utils.fopen(file_path, 'r+') as file_handler: for line in file_handler: try: atom = line.strip().split()[0] except IndexError: new_contents += line else: if atom[0] == '#' or \ portage.dep_getkey(atom) == cp: new_contents += line else: rearrange.append(line.strip()) if len(new_contents) != 0: file_handler.seek(0) file_handler.truncate(len(new_contents)) file_handler.write(new_contents) if len(new_contents) == 0: os.remove(file_path) for line in rearrange: append_to_package_conf(conf, string=line) if not keep_backup: for bfile in backup_files: try: os.remove(bfile) except OSError: pass if clean: for triplet in os.walk(path): if len(triplet[1]) == 0 and len(triplet[2]) == 0 and \ triplet[0] != path: shutil.rmtree(triplet[0]) def _merge_flags(*args): ''' Merges multiple lists of flags removing duplicates and resolving conflicts giving priority to lasts lists. ''' tmp = portage.flatten(args) flags = {} for flag in tmp: if flag[0] == '-': flags[flag[1:]] = False else: flags[flag] = True tmp = [] for k, v in flags.iteritems(): if v: tmp.append(k) else: tmp.append('-' + k) # Next sort is just aesthetic, can be commented for a small performance # boost tmp.sort(cmp=lambda x, y: cmp(x.lstrip('-'), y.lstrip('-'))) return tmp def append_to_package_conf(conf, atom='', flags=None, string='', overwrite=False): ''' Append a string or a list of flags for a given package or DEPEND atom to a given configuration file. CLI Example: .. code-block:: bash salt '*' portage_config.append_to_package_conf use string="app-admin/salt ldap -libvirt" salt '*' portage_config.append_to_package_conf use atom="> = app-admin/salt-0.14.1" flags="['ldap', '-libvirt']" ''' if flags is None: flags = [] if conf in SUPPORTED_CONFS: if not string: if '/' not in atom: atom = _p_to_cp(atom) if not atom: return string = '{0} {1}'.format(atom, ' '.join(flags)) new_flags = list(flags) else: atom = string.strip().split()[0] new_flags = portage.dep.strip_empty(string.strip().split(' '))[1:] if '/' not in atom: atom = _p_to_cp(atom) string = '{0} {1}'.format(atom, ' '.join(new_flags)) if not atom: return to_delete_if_empty = [] if conf == 'accept_keywords': if '-~ARCH' in new_flags: new_flags.remove('-~ARCH') to_delete_if_empty.append(atom) if '~ARCH' in new_flags: new_flags.remove('~ARCH') append_to_package_conf(conf, string=atom, overwrite=overwrite) if not new_flags: return # Next sort is just aesthetic, can be commented for a small performance # boost new_flags.sort(cmp=lambda x, y: cmp(x.lstrip('-'), y.lstrip('-'))) package_file = _p_to_cp(atom) if not package_file: return psplit = package_file.split('/') if len(psplit) == 2: pdir = BASE_PATH.format(conf) + '/' + psplit[0] if not os.path.exists(pdir): os.mkdir(pdir, 0755) complete_file_path = BASE_PATH.format(conf) + '/' + package_file try: shutil.copy(complete_file_path, complete_file_path + '.bak') except IOError: pass try: file_handler = salt.utils.fopen(complete_file_path, 'r+') except IOError: file_handler = salt.utils.fopen(complete_file_path, 'w+') new_contents = '' added = False for l in file_handler: l_strip = l.strip() if l_strip == '': new_contents += '\n' elif l_strip[0] == '#': new_contents += l elif l_strip.split()[0] == atom: if l_strip in to_delete_if_empty: continue if overwrite: new_contents += string.strip() + '\n' added = True else: old_flags = portage.dep.strip_empty(l_strip.split(' '))[1:] if conf == 'accept_keywords': if not old_flags: new_contents += l if not new_flags: added = True continue elif not new_flags: continue merged_flags = _merge_flags(old_flags, new_flags) if merged_flags: new_contents += '{0} {1}\n'.format( atom, ' '.join(merged_flags)) else: new_contents += '{0}\n'.format(atom) added = True else: new_contents += l if not added: new_contents += string.strip() + '\n' file_handler.seek(0) file_handler.truncate(len(new_contents)) file_handler.write(new_contents) file_handler.close() try: os.remove(complete_file_path + '.bak') except OSError: pass def append_use_flags(atom, uses=None, overwrite=False): ''' Append a list of use flags for a given package or DEPEND atom CLI Example: .. code-block:: bash salt '*' portage_config.append_use_flags "app-admin/salt[ldap, -libvirt]" salt '*' portage_config.append_use_flags ">=app-admin/salt-0.14.1" "['ldap', '-libvirt']" ''' if not uses: uses = portage.dep.dep_getusedeps(atom) if len(uses) == 0: return atom = atom[:atom.rfind('[')] append_to_package_conf('use', atom=atom, flags=uses, overwrite=overwrite) def get_flags_from_package_conf(conf, atom): ''' Get flags for a given package or DEPEND atom. Warning: This only works if the configuration files tree is in the correct format (the one enforced by enforce_nice_config) CLI Example: .. code-block:: bash salt '*' portage_config.get_flags_from_package_conf license salt ''' if conf in SUPPORTED_CONFS: package_file = '{0}/{1}'.format(BASE_PATH.format(conf), _p_to_cp(atom)) if '/' not in atom: atom = _p_to_cp(atom) match_list = set(_porttree().dbapi.xmatch("match-all", atom)) flags = [] try: file_handler = salt.utils.fopen(package_file) except IOError: return [] else: for line in file_handler: line = line.strip() line_package = line.split()[0] line_list = _porttree().dbapi.xmatch("match-all", line_package) if match_list.issubset(line_list): f_tmp = portage.dep.strip_empty(line.strip().split()[1:]) if f_tmp: flags.extend(f_tmp) else: flags.append('~ARCH') return _merge_flags(flags) def has_flag(conf, atom, flag): ''' Verify if the given package or DEPEND atom has the given flag. Warning: This only works if the configuration files tree is in the correct format (the one enforced by enforce_nice_config) CLI Example: .. code-block:: bash salt '*' portage_config.has_flag license salt Apache-2.0 ''' if flag in get_flags_from_package_conf(conf, atom): return True return False def get_missing_flags(conf, atom, flags): ''' Find out which of the given flags are currently not set. CLI Example: .. code-block:: bash salt '*' portage_config.get_missing_flags use salt "['ldap', '-libvirt', 'openssl']" ''' new_flags = [] for flag in flags: if not has_flag(conf, atom, flag): new_flags.append(flag) return new_flags def has_use(atom, use): ''' Verify if the given package or DEPEND atom has the given use flag. Warning: This only works if the configuration files tree is in the correct format (the one enforced by enforce_nice_config) CLI Example: .. code-block:: bash salt '*' portage_config.has_use salt libvirt ''' return has_flag('use', atom, use) def is_present(conf, atom): ''' Tell if a given package or DEPEND atom is present in the configuration files tree. Warning: This only works if the configuration files tree is in the correct format (the one enforced by enforce_nice_config) CLI Example: .. code-block:: bash salt '*' portage_config.is_present unmask salt ''' if conf in SUPPORTED_CONFS: package_file = '{0}/{1}'.format(BASE_PATH.format(conf), _p_to_cp(atom)) match_list = set(_porttree().dbapi.xmatch("match-all", atom)) try: file_handler = salt.utils.fopen(package_file) except IOError: return False else: for line in file_handler: line = line.strip() line_package = line.split()[0] line_list = _porttree().dbapi.xmatch("match-all", line_package) if match_list.issubset(line_list): return True return False salt-0.17.5+ds.orig/salt/modules/djangomod.py0000644000175000017500000001125312270576114017162 0ustar joejoe# -*- coding: utf-8 -*- ''' Manage Django sites ''' # Import python libs import os def __virtual__(): return 'django' def _get_django_admin(bin_env): ''' Return the django admin ''' if not bin_env: return 'django-admin.py' # try to get django-admin.py bin from env if os.path.exists(os.path.join(bin_env, 'bin', 'django-admin.py')): return os.path.join(bin_env, 'bin', 'django-admin.py') return bin_env def command(settings_module, command, bin_env=None, pythonpath=None, env=None, *args, **kwargs): ''' Run arbitrary django management command CLI Example: .. code-block:: bash salt '*' django.command ''' dja = _get_django_admin(bin_env) cmd = '{0} {1} --settings={2}'.format(dja, command, settings_module) if pythonpath: cmd = '{0} --pythonpath={1}'.format(cmd, pythonpath) for arg in args: cmd = '{0} --{1}'.format(cmd, arg) for key, value in kwargs.items(): if not key.startswith('__'): cmd = '{0} --{1}={2}'.format(cmd, key, value) return __salt__['cmd.run'](cmd, env=env) def syncdb(settings_module, bin_env=None, migrate=False, database=None, pythonpath=None, env=None, noinput=True): ''' Run syncdb Execute the Django-Admin syncdb command, if South is available on the minion the ``migrate`` option can be passed as ``True`` calling the migrations to run after the syncdb completes CLI Example: .. code-block:: bash salt '*' django.syncdb ''' args = [] kwargs = {} if migrate: args.append('migrate') if database: kwargs['database'] = database if noinput: args.append('noinput') return command(settings_module, 'syncdb', bin_env, pythonpath, env, *args, **kwargs) def createsuperuser(settings_module, username, email, bin_env=None, database=None, pythonpath=None, env=None): ''' Create a super user for the database. This function defaults to use the ``--noinput`` flag which prevents the creation of a password for the superuser. CLI Example: .. code-block:: bash salt '*' django.createsuperuser user user@example.com ''' args = ['noinput'] kwargs = dict( email=email, username=username, ) if database: kwargs['database'] = database return command(settings_module, 'createsuperuser', bin_env, pythonpath, env, *args, **kwargs) def loaddata(settings_module, fixtures, bin_env=None, database=None, pythonpath=None, env=None): ''' Load fixture data Fixtures: comma separated list of fixtures to load CLI Example: .. code-block:: bash salt '*' django.loaddata ''' kwargs = {} if database: kwargs['database'] = database return command(settings_module, 'loaddata', bin_env, pythonpath, env, *fixtures.split(','), **kwargs) def collectstatic(settings_module, bin_env=None, no_post_process=False, ignore=None, dry_run=False, clear=False, link=False, no_default_ignore=False, pythonpath=None, env=None): ''' Collect static files from each of your applications into a single location that can easily be served in production. CLI Example: .. code-block:: bash salt '*' django.collectstatic ''' args = ['noinput'] kwargs = {} if no_post_process: args.append('no-post-process') if ignore: kwargs['ignore'] = ignore if dry_run: args.append('dry-run') if clear: args.append('clear') if link: args.append('link') if no_default_ignore: args.append('no-default-ignore') return command(settings_module, 'collectstatic', bin_env, pythonpath, env, *args, **kwargs) salt-0.17.5+ds.orig/salt/modules/modjk.py0000644000175000017500000003046512270576114016332 0ustar joejoe# -*- coding: utf-8 -*- ''' Control Modjk via the Apache Tomcat "Status" worker (http://tomcat.apache.org/connectors-doc/reference/status.html) Below is an example of the configuration needed for this module. This configuration data can be placed either in :doc:`grains ` or :doc:`pillar `. If using grains, this can be accomplished :ref:`statically ` or via a :ref:`grain module `. If using pillar, the yaml configuration can be placed directly into a pillar SLS file, making this both the easier and more dynamic method of configuring this module. .. code-block:: yaml modjk: default: url: http://localhost/jkstatus user: modjk pass: secret realm: authentication realm for digest passwords timeout: 5 otherVhost: url: http://otherVhost/jkstatus user: modjk pass: secret2 realm: authentication realm2 for digest passwords timeout: 600 ''' # Python libs import urllib import urllib2 def __virtual__(): ''' Always load ''' return 'modjk' def _auth(url, user, passwd, realm): ''' returns a authentication handler. ''' basic = urllib2.HTTPBasicAuthHandler() basic.add_password(realm=realm, uri=url, user=user, passwd=passwd) digest = urllib2.HTTPDigestAuthHandler() digest.add_password(realm=realm, uri=url, user=user, passwd=passwd) return urllib2.build_opener(basic, digest) def _do_http(opts, profile='default'): ''' Make the http request and return the data ''' ret = {} url = __salt__['config.get']('modjk:{0}:url'.format(profile), '') user = __salt__['config.get']('modjk:{0}:user'.format(profile), '') passwd = __salt__['config.get']('modjk:{0}:pass'.format(profile), '') realm = __salt__['config.get']('modjk:{0}:realm'.format(profile), '') timeout = __salt__['config.get']('modjk:{0}:timeout'.format(profile), '') if not url: raise Exception('missing url in profile {0}'.format(profile)) if user and passwd: auth = _auth(url, realm, user, passwd) urllib2.install_opener(auth) url += '?{0}'.format(urllib.urlencode(opts)) for line in urllib2.urlopen(url, timeout=timeout).read().splitlines(): splt = line.split('=', 1) if splt[0] in ret: ret[splt[0]] += ',{0}'.format(splt[1]) else: ret[splt[0]] = splt[1] return ret def _worker_ctl(worker, lbn, vwa, profile='default'): ''' enable/disable/stop a worker ''' cmd = { 'cmd': 'update', 'mime': 'prop', 'w': lbn, 'sw': worker, 'vwa': vwa, } return _do_http(cmd, profile)['worker.result.type'] == 'OK' ############### ### General ### ############### def version(profile='default'): ''' Return the modjk version CLI Examples: .. code-block:: bash salt '*' modjk.version salt '*' modjk.version other-profile ''' cmd = { 'cmd': 'version', 'mime': 'prop', } return _do_http(cmd, profile)['worker.jk_version'].split('/')[-1] def get_running(profile='default'): ''' Get the current running config (not from disk) CLI Examples: .. code-block:: bash salt '*' modjk.get_running salt '*' modjk.get_running other-profile ''' cmd = { 'cmd': 'list', 'mime': 'prop', } return _do_http(cmd, profile) def dump_config(profile='default'): ''' Dump the original configuration that was loaded from disk CLI Examples: .. code-block:: bash salt '*' modjk.dump_config salt '*' modjk.dump_config other-profile ''' cmd = { 'cmd': 'dump', 'mime': 'prop', } return _do_http(cmd, profile) #################### ### LB Functions ### #################### def list_configured_members(lbn, profile='default'): ''' Return a list of member workers from the configuration files CLI Examples: .. code-block:: bash salt '*' modjk.list_configured_members loadbalancer1 salt '*' modjk.list_configured_members loadbalancer1 other-profile ''' config = dump_config(profile) try: ret = config['worker.{0}.balance_workers'.format(lbn)] except KeyError: return [] return filter(None, ret.strip().split(',')) def workers(profile='default'): ''' Return a list of member workers and their status CLI Examples: .. code-block:: bash salt '*' modjk.workers salt '*' modjk.workers other-profile ''' config = get_running(profile) lbn = config['worker.list'].split(',') worker_list = [] ret = {} for lb in lbn: try: worker_list.extend( config['worker.{0}.balance_workers'.format(lb)].split(',') ) except KeyError: pass worker_list = list(set(worker_list)) for worker in worker_list: ret[worker] = { 'activation': config['worker.{0}.activation'.format(worker)], 'state': config['worker.{0}.state'.format(worker)], } return ret def recover_all(lbn, profile='default'): ''' Set the all the workers in lbn to recover and activate them if they are not CLI Examples: .. code-block:: bash salt '*' modjk.recover_all loadbalancer1 salt '*' modjk.recover_all loadbalancer1 other-profile ''' ret = {} config = get_running(profile) try: workers_ = config['worker.{0}.balance_workers'.format(lbn)].split(',') except KeyError: return ret for worker in workers_: curr_state = worker_status(worker, profile) if curr_state['activation'] != 'ACT': worker_activate(worker, lbn, profile) if not curr_state['state'].startswith('OK'): worker_recover(worker, lbn, profile) ret[worker] = worker_status(worker, profile) return ret def reset_stats(lbn, profile='default'): ''' Reset all runtime statistics for the load balancer CLI Examples: .. code-block:: bash salt '*' modjk.reset_stats loadbalancer1 salt '*' modjk.reset_stats loadbalancer1 other-profile ''' cmd = { 'cmd': 'reset', 'mime': 'prop', 'w': lbn, } return _do_http(cmd, profile)['worker.result.type'] == 'OK' def lb_edit(lbn, settings, profile='default'): ''' Edit the loadbalancer settings Note: http://tomcat.apache.org/connectors-doc/reference/status.html Data Parameters for the standard Update Action CLI Examples: .. code-block:: bash salt '*' modjk.lb_edit loadbalancer1 "{'vlr': 1, 'vlt': 60}" salt '*' modjk.lb_edit loadbalancer1 "{'vlr': 1, 'vlt': 60}" other-profile ''' settings['cmd'] = 'update' settings['mime'] = 'prop' settings['w'] = lbn return _do_http(settings, profile)['worker.result.type'] == 'OK' ######################## ### Worker Functions ### ######################## def bulk_stop(workers, lbn, profile='default'): ''' Stop all the given workers in the specific load balancer CLI Examples: .. code-block:: bash salt '*' modjk.bulk_stop node1,node2,node3 salt '*' modjk.bulk_stop node1,node2,node3 other-profile salt '*' modjk.bulk_stop ["node1","node2","node3"] salt '*' modjk.bulk_stop ["node1","node2","node3"] other-profile ''' ret = {} if type(workers) == str: workers = workers.split(',') for worker in workers: try: ret[worker] = worker_stop(worker, lbn, profile) except Exception: ret[worker] = False return ret def bulk_activate(workers, lbn, profile='default'): ''' Activate all the given workers in the specific load balancer CLI Examples: .. code-block:: bash salt '*' modjk.bulk_activate node1,node2,node3 salt '*' modjk.bulk_activate node1,node2,node3 other-profile salt '*' modjk.bulk_activate ["node1","node2","node3"] salt '*' modjk.bulk_activate ["node1","node2","node3"] other-profile ''' ret = {} if type(workers) == str: workers = workers.split(',') for worker in workers: try: ret[worker] = worker_activate(worker, lbn, profile) except Exception: ret[worker] = False return ret def bulk_disable(workers, lbn, profile='default'): ''' Disable all the given workers in the specific load balancer CLI Examples: .. code-block:: bash salt '*' modjk.bulk_disable node1,node2,node3 salt '*' modjk.bulk_disable node1,node2,node3 other-profile salt '*' modjk.bulk_disable ["node1","node2","node3"] salt '*' modjk.bulk_disable ["node1","node2","node3"] other-profile ''' ret = {} if type(workers) == str: workers = workers.split(',') for worker in workers: try: ret[worker] = worker_disable(worker, lbn, profile) except Exception: ret[worker] = False return ret def bulk_recover(workers, lbn, profile='default'): ''' Recover all the given workers in the specific load balancer CLI Examples: .. code-block:: bash salt '*' modjk.bulk_recover node1,node2,node3 salt '*' modjk.bulk_recover node1,node2,node3 other-profile salt '*' modjk.bulk_recover ["node1","node2","node3"] salt '*' modjk.bulk_recover ["node1","node2","node3"] other-profile ''' ret = {} if type(workers) == str: workers = workers.split(',') for worker in workers: try: ret[worker] = worker_recover(worker, lbn, profile) except Exception: ret[worker] = False return ret def worker_status(worker, profile='default'): ''' Return the state of the worker CLI Examples: .. code-block:: bash salt '*' modjk.worker_status node1 salt '*' modjk.worker_status node1 other-profile ''' config = get_running(profile) try: return { 'activation': config['worker.{0}.activation'.format(worker)], 'state': config['worker.{0}.state'.format(worker)], } except KeyError: return False def worker_recover(worker, lbn, profile='default'): ''' Set the worker to recover this module will fail if it is in OK state CLI Examples: .. code-block:: bash salt '*' modjk.worker_recover node1 loadbalancer1 salt '*' modjk.worker_recover node1 loadbalancer1 other-profile ''' cmd = { 'cmd': 'recover', 'mime': 'prop', 'w': lbn, 'sw': worker, } return _do_http(cmd, profile) def worker_disable(worker, lbn, profile='default'): ''' Set the worker to disable state in the lbn load balancer CLI Examples: .. code-block:: bash salt '*' modjk.worker_disable node1 loadbalancer1 salt '*' modjk.worker_disable node1 loadbalancer1 other-profile ''' return _worker_ctl(worker, lbn, 'd', profile) def worker_activate(worker, lbn, profile='default'): ''' Set the worker to activate state in the lbn load balancer CLI Examples: .. code-block:: bash salt '*' modjk.worker_activate node1 loadbalancer1 salt '*' modjk.worker_activate node1 loadbalancer1 other-profile ''' return _worker_ctl(worker, lbn, 'a', profile) def worker_stop(worker, lbn, profile='default'): ''' Set the worker to stopped state in the lbn load balancer CLI Examples: .. code-block:: bash salt '*' modjk.worker_activate node1 loadbalancer1 salt '*' modjk.worker_activate node1 loadbalancer1 other-profile ''' return _worker_ctl(worker, lbn, 's', profile) def worker_edit(worker, lbn, settings, profile='default'): ''' Edit the worker settings Note: http://tomcat.apache.org/connectors-doc/reference/status.html Data Parameters for the standard Update Action CLI Examples: .. code-block:: bash salt '*' modjk.worker_edit node1 loadbalancer1 "{'vwf': 500, 'vwd': 60}" salt '*' modjk.worker_edit node1 loadbalancer1 "{'vwf': 500, 'vwd': 60}" other-profile ''' settings['cmd'] = 'update' settings['mime'] = 'prop' settings['w'] = lbn settings['sw'] = worker return _do_http(settings, profile)['worker.result.type'] == 'OK' salt-0.17.5+ds.orig/salt/modules/state.py0000644000175000017500000005011312270576114016336 0ustar joejoe# -*- coding: utf-8 -*- ''' Control the state system on the minion ''' # Import python libs import os import json import copy import shutil import time import logging import tarfile import datetime import tempfile # Import salt libs import salt.utils import salt.state import salt.payload from salt._compat import string_types __outputter__ = { 'sls': 'highstate', 'top': 'highstate', 'single': 'highstate', 'highstate': 'highstate', } log = logging.getLogger(__name__) def _filter_running(runnings): ''' Filter out the result: True + no changes data ''' ret = dict((tag, value) for tag, value in runnings.iteritems() if not value['result'] or value['changes']) return ret def _set_retcode(ret): ''' Set the return code based on the data back from the state system ''' if isinstance(ret, list): __context__['retcode'] = 1 return if not salt.utils.check_state_result(ret): __context__['retcode'] = 2 def _check_pillar(kwargs): ''' Check the pillar for errors, refuse to run the state if there are errors in the pillar and return the pillar errors ''' if kwargs.get('force'): return True if '_errors' in __pillar__: return False return True def _wait(jid): ''' Wait for all previously started state jobs to finish running ''' if jid is None: jid = '{0:%Y%m%d%H%M%S%f}'.format(datetime.datetime.now()) states = _prior_running_states(jid) while states: time.sleep(1) states = _prior_running_states(jid) def running(): ''' Return a dict of state return data if a state function is already running. This function is used to prevent multiple state calls from being run at the same time. CLI Example: .. code-block:: bash salt '*' state.running ''' ret = [] active = __salt__['saltutil.is_running']('state.*') for data in active: err = ( 'The function "{0}" is running as PID {1} and was started at ' '{2} with jid {3}' ).format( data['fun'], data['pid'], salt.utils.jid_to_time(data['jid']), data['jid'], ) ret.append(err) return ret def _prior_running_states(jid): """ Return a list of dicts of prior calls to state functions. This function is used to queue state calls so only one is run at a time. """ ret = [] active = __salt__['saltutil.is_running']('state.*') for data in active: if int(data['jid']) < int(jid): ret.append(data) return ret def low(data, queue=False, **kwargs): ''' Execute a single low data call This function is mostly intended for testing the state system CLI Example: .. code-block:: bash salt '*' state.low '{"state": "pkg", "fun": "installed", "name": "vi"}' ''' if queue: _wait(kwargs.get('__pub_jid')) else: conflict = running() if conflict: __context__['retcode'] = 1 return conflict st_ = salt.state.State(__opts__) err = st_.verify_data(data) if err: __context__['retcode'] = 1 return err ret = st_.call(data) if isinstance(ret, list): __context__['retcode'] = 1 if salt.utils.check_state_result(ret): __context__['retcode'] = 2 return ret def high(data, queue=False, **kwargs): ''' Execute the compound calls stored in a single set of high data This function is mostly intended for testing the state system CLI Example: .. code-block:: bash salt '*' state.high '{"vim": {"pkg": ["installed"]}}' ''' if queue: _wait(kwargs.get('__pub_jid')) else: conflict = running() if conflict: __context__['retcode'] = 1 return conflict st_ = salt.state.State(__opts__) ret = st_.call_high(data) _set_retcode(ret) return ret def template(tem, queue=False, **kwargs): ''' Execute the information stored in a template file on the minion CLI Example: .. code-block:: bash salt '*' state.template '' ''' if queue: _wait(kwargs.get('__pub_jid')) else: conflict = running() if conflict: __context__['retcode'] = 1 return conflict st_ = salt.state.State(__opts__) ret = st_.call_template(tem) _set_retcode(ret) return ret def template_str(tem, queue=False, **kwargs): ''' Execute the information stored in a string from an sls template CLI Example: .. code-block:: bash salt '*' state.template_str '