%PDF- %PDF-
Direktori : /proc/self/root/usr/lib/python2.7/site-packages/salt/modules/ |
Current File : //proc/self/root/usr/lib/python2.7/site-packages/salt/modules/puppet.py |
# -*- coding: utf-8 -*- ''' Execute puppet routines ''' # Import python libs from __future__ import absolute_import, print_function, unicode_literals from distutils import version # pylint: disable=no-name-in-module import logging import os import datetime # Import salt libs import salt.utils.args import salt.utils.files import salt.utils.path import salt.utils.platform import salt.utils.yaml import salt.utils.stringutils from salt.exceptions import CommandExecutionError # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import range log = logging.getLogger(__name__) def __virtual__(): ''' Only load if puppet is installed ''' unavailable_exes = ', '.join(exe for exe in ('facter', 'puppet') if salt.utils.path.which(exe) is None) if unavailable_exes: return (False, ('The puppet execution module cannot be loaded: ' '{0} unavailable.'.format(unavailable_exes))) else: return 'puppet' 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 = [] # e.g. /a/b/manifest.pp self.kwargs = {'color': 'false'} # e.g. --tags=apache::server self.args = [] # e.g. --noop if salt.utils.platform.is_windows(): self.vardir = 'C:\\ProgramData\\PuppetLabs\\puppet\\var' self.rundir = 'C:\\ProgramData\\PuppetLabs\\puppet\\run' self.confdir = 'C:\\ProgramData\\PuppetLabs\\puppet\\etc' else: self.puppet_version = __salt__['cmd.run']('puppet --version') if 'Enterprise' in self.puppet_version: self.vardir = '/var/opt/lib/pe-puppet' self.rundir = '/var/opt/run/pe-puppet' self.confdir = '/etc/puppetlabs/puppet' elif self.puppet_version != [] and version.StrictVersion(self.puppet_version) >= version.StrictVersion('4.0.0'): self.vardir = '/opt/puppetlabs/puppet/cache' self.rundir = '/var/run/puppetlabs' self.confdir = '/etc/puppetlabs/puppet' else: self.vardir = '/var/lib/puppet' self.rundir = '/var/run/puppet' self.confdir = '/etc/puppet' self.disabled_lockfile = self.vardir + '/state/agent_disabled.lock' self.run_lockfile = self.vardir + '/state/agent_catalog_run.lock' self.agent_pidfile = self.rundir + '/agent.pid' self.lastrunfile = self.vardir + '/state/last_run_summary.yaml' 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 six.iteritems(self.kwargs)] ) # Ensure that the puppet call will return 0 in case of exit code 2 if salt.utils.platform.is_windows(): return 'cmd /V:ON /c {0} {1} ^& if !ERRORLEVEL! EQU 2 (EXIT 0) ELSE (EXIT /B)'.format(cmd, args) return '({0} {1}) || test $? -eq 2'.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([ 'test' ]) # 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-keyword arguments. Tags are specified by a tag keyword and comma separated list of values. -- http://docs.puppetlabs.com/puppet/latest/reference/lang_tags.html 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 ''' puppet = _Puppet() # new args tuple to filter out agent/apply for _Puppet.arguments() buildargs = () for arg in range(len(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[arg] in ['agent', 'apply']: puppet.subcmd = args[arg] else: buildargs += (args[arg],) # args will exist as an empty list even if none have been provided puppet.arguments(buildargs) puppet.kwargs.update(salt.utils.args.clean_kwargs(**kwargs)) ret = __salt__['cmd.run_all'](repr(puppet), python_shell=True) return ret 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 enable(): ''' .. versionadded:: 2014.7.0 Enable the puppet agent CLI Example: .. code-block:: bash salt '*' puppet.enable ''' puppet = _Puppet() if os.path.isfile(puppet.disabled_lockfile): try: os.remove(puppet.disabled_lockfile) except (IOError, OSError) as exc: msg = 'Failed to enable: {0}'.format(exc) log.error(msg) raise CommandExecutionError(msg) else: return True return False def disable(message=None): ''' .. versionadded:: 2014.7.0 Disable the puppet agent message .. versionadded:: 2015.5.2 Disable message to send to puppet CLI Example: .. code-block:: bash salt '*' puppet.disable salt '*' puppet.disable 'Disabled, contact XYZ before enabling' ''' puppet = _Puppet() if os.path.isfile(puppet.disabled_lockfile): return False else: with salt.utils.files.fopen(puppet.disabled_lockfile, 'w') as lockfile: try: # Puppet chokes when no valid json is found msg = '{{"disabled_message":"{0}"}}'.format(message) if message is not None else '{}' lockfile.write(salt.utils.stringutils.to_str(msg)) lockfile.close() return True except (IOError, OSError) as exc: msg = 'Failed to disable: {0}'.format(exc) log.error(msg) raise CommandExecutionError(msg) def status(): ''' .. versionadded:: 2014.7.0 Display puppet agent status CLI Example: .. code-block:: bash salt '*' puppet.status ''' puppet = _Puppet() if os.path.isfile(puppet.disabled_lockfile): return 'Administratively disabled' if os.path.isfile(puppet.run_lockfile): try: with salt.utils.files.fopen(puppet.run_lockfile, 'r') as fp_: pid = int(salt.utils.stringutils.to_unicode(fp_.read())) os.kill(pid, 0) # raise an OSError if process doesn't exist except (OSError, ValueError): return 'Stale lockfile' else: return 'Applying a catalog' if os.path.isfile(puppet.agent_pidfile): try: with salt.utils.files.fopen(puppet.agent_pidfile, 'r') as fp_: pid = int(salt.utils.stringutils.to_unicode(fp_.read())) os.kill(pid, 0) # raise an OSError if process doesn't exist except (OSError, ValueError): return 'Stale pidfile' else: return 'Idle daemon' return 'Stopped' def summary(): ''' .. versionadded:: 2014.7.0 Show a summary of the last puppet agent run CLI Example: .. code-block:: bash salt '*' puppet.summary ''' puppet = _Puppet() try: with salt.utils.files.fopen(puppet.lastrunfile, 'r') as fp_: report = salt.utils.yaml.safe_load(fp_) result = {} if 'time' in report: try: result['last_run'] = datetime.datetime.fromtimestamp( int(report['time']['last_run'])).isoformat() except (TypeError, ValueError, KeyError): result['last_run'] = 'invalid or missing timestamp' result['time'] = {} for key in ('total', 'config_retrieval'): if key in report['time']: result['time'][key] = report['time'][key] if 'resources' in report: result['resources'] = report['resources'] except salt.utils.yaml.YAMLError as exc: raise CommandExecutionError( 'YAML error parsing puppet run summary: {0}'.format(exc) ) except IOError as exc: raise CommandExecutionError( 'Unable to read puppet run summary: {0}'.format(exc) ) return result def plugin_sync(): ''' Runs a plugin sync between the puppet master and agent CLI Example: .. code-block:: bash salt '*' puppet.plugin_sync ''' ret = __salt__['cmd.run']('puppet plugin download') if not ret: return '' return ret def facts(puppet=False): ''' Run facter and return the results CLI Example: .. code-block:: bash salt '*' puppet.facts ''' ret = {} opt_puppet = '--puppet' if puppet else '' cmd_ret = __salt__['cmd.run_all']('facter {0}'.format(opt_puppet)) if cmd_ret['retcode'] != 0: raise CommandExecutionError(cmd_ret['stderr']) output = cmd_ret['stdout'] # 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, puppet=False): ''' Run facter for a specific fact CLI Example: .. code-block:: bash salt '*' puppet.fact kernel ''' opt_puppet = '--puppet' if puppet else '' ret = __salt__['cmd.run_all']( 'facter {0} {1}'.format(opt_puppet, name), python_shell=False) if ret['retcode'] != 0: raise CommandExecutionError(ret['stderr']) if not ret['stdout']: return '' return ret['stdout']