zoukankan      html  css  js  c++  java
  • ansible源码分析之程序入口

    首先我们先来看下装完ansible之后  ansible里面是些上面内容

    [root@node1 scripts]# cat /bin/ansible
    #!/usr/bin/python
    # EASY-INSTALL-SCRIPT: 'ansible==2.3.4.0','ansible'
    __requires__ = 'ansible==2.3.4.0'
    #指定库的版本 指定了要执行的脚本名字 __import__('pkg_resources').run_script('ansible==2.3.4.0', 'ansible')

    发现就简单的几行代码。关于这里可以去了解一下python pkg_resources用法。

    这几行代码最终会执行/usr/lib/python2.7/site-packages/ansible-2.3.4.0-py2.7.egg/EGG-INFO/scripts下面对应的脚本

    上面传来的参数是ansible所以会执行ansible脚本。

     所以我们直接看/usr/lib/python2.7/site-packages/ansible-2.3.4.0-py2.7.egg/EGG-INFO/scripts/ansible 文件吧

    if __name__ == '__main__':
        display = LastResort()
        cli = None
        me = os.path.basename(sys.argv[0])
    
        try:
            display = Display()
            display.debug("starting run")
            sub = None
            ###下面这一堆代码都是去找文件名字适配的,和兼容行适配的,无需大多关注
            target = me.split('-')
            ##这里是为了去小版本号的 比如ansible-1.4.2
            if target[-1][0].isdigit():
                target = target[:-1]
            if len(target) > 1:
                sub = target[1]
                myclass = "%sCLI" % sub.capitalize()
            elif target[0] == 'ansible':
                sub = 'adhoc'
                myclass = 'AdHocCLI'
            else:
                raise AnsibleError("Unknown Ansible alias: %s" % me)
    
            try:
                #反射导入ansible.cli.AdHocCLI类。
                mycli = getattr(__import__("ansible.cli.%s" % sub, fromlist=[myclass]), myclass)
            except ImportError as e:
                if 'msg' in dir(e):
                    msg = e.msg
                else:
                    msg = e.message
                if msg.endswith(' %s' % sub):
                    raise AnsibleError("Ansible sub-program not implemented: %s" % me)
                else:
                    raise
    
            try:
                #各种检查
                args = [to_text(a, errors='surrogate_or_strict') for a in sys.argv]
                ###
            except UnicodeError:
                display.error('Command line args are not in utf-8, unable to continue.  Ansible currently only understands utf-8')
                display.display(u"The full traceback was:
    
    %s" % to_text(traceback.format_exc()))
                exit_code = 6
            else:
                # 此时的args[u'/usr/bin/ansible', u'jack', u'-o']
                cli = mycli(args)
                cli.parse()
                exit_code = cli.run()
        except AnsibleOptionsError as e:
            cli.parser.print_help()
            display.error(to_text(e), wrap_text=False)
            exit_code = 5
        except AnsibleParserError as e:
            display.error(to_text(e), wrap_text=False)
            exit_code = 4
    # TQM takes care of these, but leaving comment to reserve the exit codes
    #    except AnsibleHostUnreachable as e:
    #        display.error(str(e))
    #        exit_code = 3
    #    except AnsibleHostFailed as e:
    #        display.error(str(e))
    #        exit_code = 2
        except AnsibleError as e:
            display.error(to_text(e), wrap_text=False)
            exit_code = 1
        except KeyboardInterrupt:
            display.error("User interrupted execution")
            exit_code = 99
        except Exception as e:
            have_cli_options = cli is not None and cli.options is not None
            display.error("Unexpected Exception: %s" % to_text(e), wrap_text=False)
            if not have_cli_options or have_cli_options and cli.options.verbosity > 2:
                log_only = False
            else:
                display.display("to see the full traceback, use -vvv")
                log_only = True
            display.display(u"the full traceback was:
    
    %s" % to_text(traceback.format_exc()), log_only=log_only)
            exit_code = 250
        finally:
            # Remove ansible tempdir
            shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
        sys.exit(exit_code)
      下面我们看下这个类mycli.parse的源代码:
    
    这个方法是做参数检测的,多了少了,参数不对都将会报错 
     
    def parse(self):
        ''' create an options parser for bin/ansible '''
    
        self.parser = CLI.base_parser(
            usage='%prog <host-pattern> [options]',
            runas_opts=True,
            inventory_opts=True,
            async_opts=True,
            output_opts=True,
            connect_opts=True,
            check_opts=True,
            runtask_opts=True,
            vault_opts=True,
            fork_opts=True,
            module_opts=True,
        )
    
        # options unique to ansible ad-hoc
        self.parser.add_option('-a', '--args', dest='module_args',
            help="module arguments", default=C.DEFAULT_MODULE_ARGS)
        self.parser.add_option('-m', '--module-name', dest='module_name',
            help="module name to execute (default=%s)" % C.DEFAULT_MODULE_NAME,
            default=C.DEFAULT_MODULE_NAME)
    
        super(AdHocCLI, self).parse()
        #没有输入要执行的组或者ip就会报这条语句
        if len(self.args) < 1:
           raise AnsibleOptionsError("Missing target hosts")
      #多输出了 参数也会报错,所以这个 self.args 只能等于一也就是。 
        elif len(self.args) > 1:
            raise AnsibleOptionsError("Extraneous options or arguments")
    
        display.verbosity = self.options.verbosity
        self.validate_conflicts(runas_opts=True, vault_opts=True, fork_opts=True)
      下面我们看下这个类mycli.run的源代码:
    
        def run(self):
            ''' use Runner lib to do SSH things '''
    ###这里其实是实现了接口类。如果当前类中没有run方法就会报错了。 super(AdHocCLI, self).run() # only thing left should be host pattern pattern = to_text(self.args[0], errors='surrogate_or_strict') # ignore connection password cause we are local #这里的if判断的是执行的命令的主机是否是 local 如果是local话就不会调用远程执行命令方法了。 if self.options.connection == "local": self.options.ask_pass = False sshpass = None becomepass = None b_vault_pass = None self.normalize_become_options() ###从配置文件里面获取账号密码 (sshpass, becomepass) = self.ask_passwords() passwords = { 'conn_pass': sshpass, 'become_pass': becomepass } loader = DataLoader() if self.options.vault_password_file: # read vault_pass from a file b_vault_pass = CLI.read_vault_password_file(self.options.vault_password_file, loader=loader) loader.set_vault_password(b_vault_pass) elif self.options.ask_vault_pass: b_vault_pass = self.ask_vault_passwords() loader.set_vault_password(b_vault_pass) variable_manager = VariableManager() variable_manager.extra_vars = load_extra_vars(loader=loader, options=self.options) variable_manager.options_vars = load_options_vars(self.options) inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list=self.options.inventory) variable_manager.set_inventory(inventory) no_hosts = False #判断需要执行命令的主机,如果为0的话表示配置文件里面定义的组里面没有主机 if len(inventory.list_hosts()) == 0: # Empty inventory display.warning("provided hosts list is empty, only localhost is available") no_hosts = True inventory.subset(self.options.subset) hosts = inventory.list_hosts(pattern) #下面这驼if就是各种环境参数判断,这里可以忽略不管。 if len(hosts) == 0: if no_hosts is False and self.options.subset: # Invalid limit raise AnsibleError("Specified --limit does not match any hosts") else: display.warning("No hosts matched, nothing to do") if self.options.listhosts: display.display(' hosts (%d):' % len(hosts)) for host in hosts: display.display(' %s' % host) return 0 if self.options.module_name in C.MODULE_REQUIRE_ARGS and not self.options.module_args: err = "No argument passed to %s module" % self.options.module_name if pattern.endswith(".yml"): err = err + ' (did you mean to run ansible-playbook?)' raise AnsibleOptionsError(err) # Avoid modules that don't work with ad-hoc if self.options.module_name in ('include', 'include_role'): raise AnsibleOptionsError("'%s' is not a valid action for ad-hoc commands" % self.options.module_name) # dynamically load any plugins from the playbook directory for name, obj in get_all_plugin_loaders(): if obj.subdir: plugin_path = os.path.join('.', obj.subdir) if os.path.isdir(plugin_path): obj.add_directory(plugin_path) play_ds = self._play_ds(pattern, self.options.seconds, self.options.poll_interval) play = Play().load(play_ds, variable_manager=variable_manager, loader=loader) if self.callback: cb = self.callback elif self.options.one_line: cb = 'oneline' # Respect custom 'stdout_callback' only with enabled 'bin_ansible_callbacks' elif C.DEFAULT_LOAD_CALLBACK_PLUGINS and C.DEFAULT_STDOUT_CALLBACK != 'default': cb = C.DEFAULT_STDOUT_CALLBACK else: cb = 'minimal' run_tree=False if self.options.tree: C.DEFAULT_CALLBACK_WHITELIST.append('tree') C.TREE_DIR = self.options.tree run_tree=True # now create a task queue manager to execute the play self._tqm = None try: self._tqm = TaskQueueManager( inventory=inventory, variable_manager=variable_manager, loader=loader, options=self.options, passwords=passwords, stdout_callback=cb, run_additional_callbacks=C.DEFAULT_LOAD_CALLBACK_PLUGINS, run_tree=run_tree, ) #这里的result 就是一个退出状态码,执命令的过程都在self._tqm.run里面 result = self._tqm.run(play) finally: if self._tqm: self._tqm.cleanup() if loader: loader.cleanup_all_tmp_files() return result

      

     

               

    Welcome to visit
  • 相关阅读:
    看雪-课程-加密与解密基础
    Windows API-Wininet&WinHTTP
    OS-Windows-bat-不等待当前命令返回继续执行后续指令
    Code-OPC DA- OPC Client Code Demo
    OS-Windows-Close Windows Error Reporting
    C-长度修饰符
    Code-Linux-time_t
    Windows-bat-Path
    Code-C++-CTime&ColeDateTime
    c++命名规范、代码规范和参数设置
  • 原文地址:https://www.cnblogs.com/Nolover/p/10979308.html
Copyright © 2011-2022 走看看