• [自动化]浅聊ansible的幂等


    描述#

      幂等性是在实际应用中经常需要考虑的概念,尤其是运维中。相较于将幂等性理解为各种异常情况的综合处理,将其理解为执行时需要考虑到在前次执行产生的影响的情况下能够正常执行则会更加容易接近业务需求。
      ansible包含众多的模块,大部分内置模块都能够保证操作的幂等性,即相关操作的多次执行能够达到相同结果这一特性,不会出现多次执行带来副作用的影响。但是也有不满足幂等原则的,比如shell模块、raw模块、command模块。

    幂等操作和非幂等操作的对比

    场景说明:
    比如实现删除一个临时性的文件/root/testfile的操作,如果希望其在相同的条件下,多次执行能够保持相同的结果和不会带来其它副作用,至少需要保证此操作在/root/testfile文件存在和不存在的情况下都能正常动作。

    # 当采用raw模块执行shell命令删除文件,第一次删除是成功,当执行第二次删除也是成功的,但是在生产环境这结果是不理想的,比如重启一个服务,你会随便重启服务吗?

    [root@Server-1~]# touch /root/testfile
    [root@Server-1~]# ansible localhost -m shell -a "rm -rf testfile"
    localhost | CHANGED | rc=0 >>
    [root@Server-1~]# ansible localhost -m shell -a "rm -rf testfile"
    localhost | CHANGED | rc=0 >>
    

    # 当采用file 模块执行删除文件,第一次执行删除文件成功changed: true,多次执行删除文件都是同一样的结果,不会带来副作用的影响changed: Fasle

    [root@Server-1~]# touch /root/testfile
    [root@Server-1~]# ansible localhost -m file -a "path=/root/testfile state=absent"
    localhost | CHANGED => {
        "changed": true, 
        "path": "/root/testfile", 
        "state": "absent"
    }
    [root@Server-1~]# ansible localhost -m file -a "path=/root/testfile state=absent"
    localhost | SUCCESS => {
        "changed": false, 
        "path": "/root/testfile", 
        "state": "absent"
    }
    
    那file模块是如何实现幂等的呢?如下是file模块执行absent文件时的代码(有中文注释)
    
    vim /usr/lib/python2.7/site-packages/ansible/modules/files/file.py
    .....
    def get_state(path):
        ''' Find out current state '''
    
        b_path = to_bytes(path, errors='surrogate_or_strict')
        try:
            if os.path.lexists(b_path): # 如果文件存在返回file,文件不存在返回absent
                if os.path.islink(b_path):
                    return 'link'
                elif os.path.isdir(b_path):
                    return 'directory'
                elif os.stat(b_path).st_nlink > 1:
                    return 'hard'
    
                # could be many other things, but defaulting to file
                return 'file' 
    
            return 'absent'
        except OSError as e:
            if e.errno == errno.ENOENT:  # It may already have been removed
                return 'absent'
            else:
                raise
    
    
    def ensure_absent(path):
        b_path = to_bytes(path, errors='surrogate_or_strict')
        prev_state = get_state(b_path) # 获取文件的状态
        result = {}
    
        if prev_state != 'absent': # 当prev_state='directory' or 'file' 为真
            diff = initial_diff(path, 'absent', prev_state)
    
            if not module.check_mode:
                if prev_state == 'directory': # 如果prev_state='directory', 则删除目录
                    try:
                        shutil.rmtree(b_path, ignore_errors=False)
                    except Exception as e:
                        raise AnsibleModuleError(results={'msg': "rmtree failed: %s" % to_native(e)})
                else:
                    try:
                        os.unlink(b_path) # 如果prev_state='file', 则删除文件
                    except OSError as e:
                        if e.errno != errno.ENOENT:  # It may already have been removed
                            raise AnsibleModuleError(results={'msg': "unlinking failed: %s " % to_native(e),
                                                              'path': path})
    
            result.update({'path': path, 'changed': True, 'diff': diff, 'state': 'absent'}) # 删除文件成功,动作有改变,changed=True
        else:
            result.update({'path': path, 'changed': False, 'state': 'absent'}) # 如果prev_state='absent', 动作没有改变,changed=False, 实现多次操作执行不会有任何改变。
    
        return result
    
    
    def main():
    
        global module
    
        module = AnsibleModule(
            argument_spec=dict(
                state=dict(type='str', choices=['absent', 'directory', 'file', 'hard', 'link', 'touch']),
                path=dict(type='path', required=True, aliases=['dest', 'name']),
                _original_basename=dict(type='str'),  # Internal use only, for recursive ops
                recurse=dict(type='bool', default=False),
                force=dict(type='bool', default=False),  # Note: Should not be in file_common_args in future
                follow=dict(type='bool', default=True),  # Note: Different default than file_common_args
                _diff_peek=dict(type='bool'),  # Internal use only, for internal checks in the action plugins
                src=dict(type='path'),  # Note: Should not be in file_common_args in future
                modification_time=dict(type='str'),
                modification_time_format=dict(type='str', default='%Y%m%d%H%M.%S'),
                access_time=dict(type='str'),
                access_time_format=dict(type='str', default='%Y%m%d%H%M.%S'),
            ),
            add_file_common_args=True,
            supports_check_mode=True,
        )
    
        # When we rewrite basic.py, we will do something similar to this on instantiating an AnsibleModule
        sys.excepthook = _ansible_excepthook
        additional_parameter_handling(module.params)
        params = module.params
    
        state = params['state']
        recurse = params['recurse']
        force = params['force']
        follow = params['follow']
        path = params['path']
        src = params['src']
    
        timestamps = {}
        timestamps['modification_time'] = keep_backward_compatibility_on_timestamps(params['modification_time'], state)
        timestamps['modification_time_format'] = params['modification_time_format']
        timestamps['access_time'] = keep_backward_compatibility_on_timestamps(params['access_time'], state)
        timestamps['access_time_format'] = params['access_time_format']
    
        # short-circuit for diff_peek
        if params['_diff_peek'] is not None:
            appears_binary = execute_diff_peek(to_bytes(path, errors='surrogate_or_strict'))
            module.exit_json(path=path, changed=False, appears_binary=appears_binary)
    
        if state == 'file':
            result = ensure_file_attributes(path, follow, timestamps)
        elif state == 'directory':
            result = ensure_directory(path, follow, recurse, timestamps)
        elif state == 'link':
            result = ensure_symlink(path, src, follow, force, timestamps)
        elif state == 'hard':
            result = ensure_hardlink(path, src, follow, force, timestamps)
        elif state == 'touch':
            result = execute_touch(path, follow, timestamps)
        elif state == 'absent': 
            result = ensure_absent(path) # 执行删除文件时,调用方法 def ensure_absent
    
        module.exit_json(**result)
    
    
    if __name__ == '__main__':
        main()
    
  • 相关阅读:
    FFmpeg内存管理
    学生党适合什么蓝牙耳机?推荐四款学生党蓝牙耳机
    PP-Tracking之C++部署
    亚马逊云科技AI For Good - 2022赛事答疑 | 分享4种大型目标检测算法框架
    自然语言处理历史史诗:NLP的范式演变与Python全实现
    内存管理(五)——内存回收
    Flask框架:运用Ajax轮询动态绘图
    cf Educational Codeforces Round 133 C. Robot in a Hallway
    增删改查模块测试用例设计
    linux上部署python环境
  • 原文地址:https://www.cnblogs.com/acommoners/p/15951352.html