qq524061227 发表于 2018-8-1 07:43:53

SaltStack源码分析之file状态模块

def managed(name,  
            source=None,
  
            source_hash='',
  
            user=None,
  
            group=None,
  
            mode=None,
  
            template=None,
  
            makedirs=False,
  
            dir_mode=None,
  
            context=None,
  
            replace=True,
  
            defaults=None,
  
            env=None,
  
            backup='',
  
            show_diff=True,
  
            create=True,
  
            contents=None,
  
            contents_pillar=None,
  
            contents_grains=None,
  
            contents_newline=True,
  
            follow_symlinks=True,
  
            check_cmd=None,
  
            **kwargs):
  

  

  

  
            '''
  
    Manage a given file, this function allows for a file to be downloaded from
  
    the salt master and potentially run through a templating system.
  

  
    name
  
      The location of the file to manage
  

  
    source
  
      The source file to download to the minion, this source file can be
  
      hosted on either the salt master server, or on an HTTP or FTP server.
  
      Both HTTPS and HTTP are supported as well as downloading directly
  
      from Amazon S3 compatible URLs with both pre-configured and automatic
  
      IAM credentials. (see s3.get state documentation)
  
      File retrieval from Openstack Swift object storage is supported via
  
      swift://container/object_path URLs, see swift.get documentation.
  
      For files hosted on the salt file server, if the file is located on
  
      the master in the directory named spam, and is called eggs, the source
  
      string is salt://spam/eggs. If source is left blank or None
  
      (use ~ in YAML), the file will be created as an empty file and
  
      the content will not be managed
  

  
      If the file is hosted on a HTTP or FTP server then the source_hash
  
      argument is also required
  

  
      A list of sources can also be passed in to provide a default source and
  
      a set of fallbacks. The first source in the list that is found to exist
  
      will be used and subsequent entries in the list will be ignored.
  

  
      .. code-block:: yaml
  

  
            file_override_example:
  
            file.managed:
  
                - source:
  
                  - salt://file_that_does_not_exist
  
                  - salt://file_that_exists
  

  
    source_hash
  
      This can be one of the following:
  
            1. a source hash string
  
            2. the URI of a file that contains source hash strings
  

  
      The function accepts the first encountered long unbroken alphanumeric
  
      string of correct length as a valid hash, in order from most secure to
  
      least secure:
  

  
      .. code-block:: text
  

  
            Type    Length
  
            ============
  
            sha512   128
  
            sha384      96
  
            sha256      64
  
            sha224      56
  
            sha1      40
  
            md5         32
  

  
      The file can contain several checksums for several files. Each line
  
      must contain both the file name and the hash.If no file name is
  
      matched, the first hash encountered will be used, otherwise the most
  
      secure hash with the correct source file name will be used.
  

  
      Debian file type ``*.dsc`` is supported.
  

  
      Examples:
  

  
      .. code-block:: text
  

  
            /etc/rc.conf ef6e82e4006dee563d98ada2a2a80a27
  
            sha254c8525aee419eb649f0233be91c151178b30f0dff8ebbdcc8de71b1d5c8bcc06a/etc/resolv.conf
  
            ead48423703509d37c4a90e6a0d53e143b6fc268
  

  
      Known issues:
  
            If the remote server URL has the hash file as an apparent
  
            sub-directory of the source file, the module will discover that it
  
            has already cached a directory where a file should be cached. For
  
            example:
  

  
            .. code-block:: yaml
  

  
                tomdroid-src-0.7.3.tar.gz:
  
                  file.managed:
  
                  - name: /tmp/tomdroid-src-0.7.3.tar.gz
  
                  - source: https://launchpad.net/tomdroid/beta/0.7.3/+download/tomdroid-src-0.7.3.tar.gz
  
                  - source_hash: https://launchpad.net/tomdroid/beta/0.7.3/+download/tomdroid-src-0.7.3.tar.gz/+md5
  

  

  
    user
  
      The user to own the file, this defaults to the user salt is running as
  
      on the minion
  

  
    group
  
      The group ownership set for the file, this defaults to the group salt
  
      is running as on the minion On Windows, this is ignored
  

  
    mode
  
      The permissions to set on this file, aka 644, 0775, 4664. Not supported
  
      on Windows
  

  
    template
  
      If this setting is applied then the named templating engine will be
  
      used to render the downloaded file, currently jinja, mako, and wempy
  
      are supported
  

  
    makedirs
  
      If the file is located in a path without a parent directory, then
  
      the state will fail. If makedirs is set to True, then the parent
  
      directories will be created to facilitate the creation of the named
  
      file.
  

  
    dir_mode
  
      If directories are to be created, passing this option specifies the
  
      permissions for those directories. If this is not set, directories
  
      will be assigned permissions from the 'mode' argument.
  

  
    replace
  
      If this file should be replaced.If false, this command will
  
      not overwrite file contents but will enforce permissions if the file
  
      exists already.Default is True.
  

  
    context
  
      Overrides default context variables passed to the template.
  

  
    defaults
  
      Default context passed to the template.
  

  
    backup
  
      Overrides the default backup mode for this specific file.
  

  
    show_diff
  
      If set to False, the diff will not be shown.
  

  
    create
  
      Default is True, if create is set to False then the file will only be
  
      managed if the file already exists on the system.
  

  
    contents
  
      Default is None.If specified, will use the given string as the
  
      contents of the file.Should not be used in conjunction with a source
  
      file of any kind.Ignores hashes and does not use a templating engine.
  

  
    contents_pillar
  
      .. versionadded:: 0.17.0
  

  
      Operates like ``contents``, but draws from a value stored in pillar,
  
      using the pillar path syntax used in :mod:`pillar.get
  
      <salt.modules.pillar.get>`. This is useful when the pillar value
  
      contains newlines, as referencing a pillar variable using a jinja/mako
  
      template can result in YAML formatting issues due to the newlines
  
      causing indentation mismatches.
  

  
      For example, the following could be used to deploy an SSH private key:
  

  
      .. code-block:: yaml
  

  
            /home/deployer/.ssh/id_rsa:
  
            file.managed:
  
                - user: deployer
  
                - group: deployer
  
                - mode: 600
  
                - contents_pillar: userdata:deployer:id_rsa
  

  
      This would populate ``/home/deployer/.ssh/id_rsa`` with the contents of
  
      ``pillar['userdata']['deployer']['id_rsa']``. An example of this pillar
  
      setup would be like so:
  

  
      .. code-block:: yaml
  

  
            userdata:
  
            deployer:
  
                id_rsa: |
  
                  -----BEGIN RSA PRIVATE KEY-----
  
                  MIIEowIBAAKCAQEAoQiwO3JhBquPAalQF9qP1lLZNXVjYMIswrMe2HcWUVBgh+vY
  
                  U7sCwx/dH6+VvNwmCoqmNnP+8gTPKGl1vgAObJAnMT623dMXjVKwnEagZPRJIxDy
  
                  B/HaAre9euNiY3LvIzBTWRSeMfT+rWvIKVBpvwlgGrfgz70m0pqxu+UyFbAGLin+
  
                  GpxzZAMaFpZw4sSbIlRuissXZj/sHpQb8p9M5IeO4Z3rjkCP1cxI
  
                  -----END RSA PRIVATE KEY-----
  

  
      .. note::
  

  
            The private key above is shortened to keep the example brief, but
  
            shows how to do multiline string in YAML. The key is followed by a
  
            pipe character, and the mutliline string is indented two more
  
            spaces.
  

  
    contents_grains
  
      .. versionadded:: 2014.7.0
  

  
      Same as contents_pillar, but with grains
  

  
    contents_newline
  
      .. versionadded:: 2014.7.0
  

  
      When using contents, contents_pillar, or contents_grains, this option
  
      ensures the file will have a newline at the end.
  
      When loading some data this newline is better left off. Setting
  
      contents_newline to False will omit this final newline.
  

  
    follow_symlinks : True
  
      .. versionadded:: 2014.7.0
  

  
      If the desired path is a symlink follow it and make changes to the
  
      file to which the symlink points.
  

  
    check_cmd
  
      .. versionadded:: 2014.7.0
  

  
      The specified command will be run with the managed file as an argument.
  
      If the command exits with a nonzero exit code, the command will not be
  
      run.
  
    '''
  
    # Make sure that leading zeros stripped by YAML loader are added back
  
    mode = __salt__['config.manage_mode'](mode)
  

  
    # If no source is specified, set replace to False, as there is nothing
  
    # to replace the file with.
  
    src_defined = source or contents or contents_pillar or contents_grains
  
    if not src_defined and replace:
  
      replace = False
  
      log.warning(
  
            'Neither \'source\' nor \'contents\' nor \'contents_pillar\' nor \'contents_grains\' '
  
            'was defined, yet \'replace\' was set to \'True\'. As there is '
  
            'no source to replace the file with, \'replace\' has been set '
  
            'to \'False\' to avoid reading the file unnecessarily'
  
      )
  

  
    user = _test_owner(kwargs, user=user)
  
    if salt.utils.is_windows():
  
      if group is not None:
  
            log.warning(
  
                'The group argument for {0} has been ignored as this '
  
                'is a Windows system.'.format(name)
  
            )
  
      group = user
  
    ret = {'changes': {},
  
         'comment': '',
  
         'name': name,
  
         'result': True}
  
    if not create:
  
      if not os.path.isfile(name):
  
            # Don't create a file that is not already present
  
            ret['comment'] = ('File {0} is not present and is not set for '
  
                              'creation').format(name)
  
            return ret
  
    u_check = _check_user(user, group)
  
    if u_check:
  
      # The specified user or group do not exist
  
      return _error(ret, u_check)
  
    if not os.path.isabs(name):
  
      return _error(
  
            ret, 'Specified file {0} is not an absolute path'.format(name))
  

  
    if isinstance(env, salt._compat.string_types):
  
      msg = (
  
            'Passing a salt environment should be done using \'saltenv\' not '
  
            '\'env\'. This warning will go away in Salt Boron and this '
  
            'will be the default and expected behavior. Please update your '
  
            'state files.'
  
      )
  
      salt.utils.warn_until('Boron', msg)
  
      ret.setdefault('warnings', []).append(msg)
  
      # No need to set __env__ = env since that's done in the state machinery
  

  
    if os.path.isdir(name):
  
      ret['comment'] = 'Specified target {0} is a directory'.format(name)
  
      ret['result'] = False
  
      return ret
  

  
    if context is None:
  
      context = {}
  
    elif not isinstance(context, dict):
  
      return _error(
  
            ret, 'Context must be formed as a dict')
  
    if defaults and not isinstance(defaults, dict):
  
      return _error(
  
            ret, 'Defaults must be formed as a dict')
  

  
    if len(filter(None, )) > 1:
  
      return _error(
  
            ret, 'Only one of contents, contents_pillar, and contents_grains is permitted')
  

  
    # If contents_pillar was used, get the pillar data
  
    if contents_pillar:
  
      contents = __salt__['pillar.get'](contents_pillar)
  

  
    if contents_grains:
  
      contents = __salt__['grains.get'](contents_grains)
  

  
    if contents_newline:
  
      # Make sure file ends in newline
  
      if contents and not contents.endswith('\n'):
  
            contents += '\n'
  

  
    if not replace and os.path.exists(name):
  
      # Check and set the permissions if necessary
  
      ret, _ = __salt__['file.check_perms'](name, ret, user, group, mode, follow_symlinks)
  
      if __opts__['test']:
  
            ret['comment'] = 'File {0} not updated'.format(name)
  
      elif not ret['changes'] and ret['result']:
  
            ret['comment'] = ('File {0} exists with proper permissions. '
  
                              'No changes made.'.format(name))
  
      return ret
  

  
    if name in _ACCUMULATORS:
  
      if not context:
  
            context = {}
  
      context['accumulator'] = _ACCUMULATORS
  

  
    try:
  
      if __opts__['test']:
  
            ret['result'], ret['comment'] = __salt__['file.check_managed'](
  
                name,
  
                source,
  
                source_hash,
  
                user,
  
                group,
  
                mode,
  
                template,
  
                context,
  
                defaults,
  
                __env__,
  
                contents,
  
                **kwargs
  
            )
  
            return ret
  

  
      # If the source is a list then find which file exists
  
      source, source_hash = __salt__['file.source_list'](
  
            source,
  
            source_hash,
  
            __env__
  
      )
  
    except CommandExecutionError as exc:
  
      ret['result'] = False
  
      ret['comment'] = 'Unable to manage file: {0}'.format(exc)
  
      return ret
  

  
    # Gather the source file from the server
  
    try:
  
      sfn, source_sum, comment_ = __salt__['file.get_managed'](
  
            name,
  
            template,
  
            source,
  
            source_hash,
  
            user,
  
            group,
  
            mode,
  
            __env__,
  
            context,
  
            defaults,
  
            **kwargs
  
      )
  
    except Exception as exc:
  
      ret['changes'] = {}
  
      log.debug(traceback.format_exc())
  
      return _error(ret, 'Unable to manage file: {0}'.format(exc))
  

  
    if check_cmd:
  
      tmp_filename = salt.utils.mkstemp()
  

  
      # if exists copy existing file to tmp to compare
  
      if __salt__['file.file_exists'](name):
  
            try:
  
                __salt__['file.copy'](name, tmp_filename)
  
            except Exception as exc:
  
                return _error(ret, 'Unable to copy file {0} to {1}: {2}'.format(name, tmp_filename, exc))
  

  
      try:
  
            ret = __salt__['file.manage_file'](
  
                tmp_filename,
  
                sfn,
  
                ret,
  
                source,
  
                source_sum,
  
                user,
  
                group,
  
                mode,
  
                __env__,
  
                backup,
  
                makedirs,
  
                template,
  
                show_diff,
  
                contents,
  
                dir_mode,
  
                follow_symlinks)
  
      except Exception as exc:
  
            ret['changes'] = {}
  
            log.debug(traceback.format_exc())
  
            return _error(ret, 'Unable to check_cmd file: {0}'.format(exc))
  

  
      # file being updated to verify using check_cmd
  
      if ret['changes']:
  
            # Reset ret
  
            ret = {'changes': {},
  
                   'comment': '',
  
                   'name': name,
  
                   'result': True}
  

  
            check_cmd_opts = {}
  
            if 'shell' in __grains__:
  
                check_cmd_opts['shell'] = __grains__['shell']
  

  
            cret = mod_run_check_cmd(check_cmd, tmp_filename, **check_cmd_opts)
  
            if isinstance(cret, dict):
  
                ret.update(cret)
  
                return ret
  
      else:
  
            ret = {'changes': {},
  
                   'comment': '',
  
                   'name': name,
  
                   'result': True}
  

  
    if comment_ and contents is None:
  
      return _error(ret, comment_)
  
    else:
  
      try:
  
            return __salt__['file.manage_file'](
  
                name,
  
                sfn,
  
                ret,
  
                source,
  
                source_sum,
  
                user,
  
                group,
  
                mode,
  
                __env__,
  
                backup,
  
                makedirs,
  
                template,
  
                show_diff,
  
                contents,
  
                dir_mode,
  
                follow_symlinks)
  
      except Exception as exc:
  
            ret['changes'] = {}
  
            log.debug(traceback.format_exc())
  
            return _error(ret, 'Unable to manage file: {0}'.format(exc))
页: [1]
查看完整版本: SaltStack源码分析之file状态模块