ansible api 实现本地、动态加载Inventory
支持ad_hoc 执行,本地playbook执行
import shutil
import json
from ansible.parsing.dataloader import DataLoader
from ansible.playbook.play import Play
from ansible.executor.task_queue_manager import TaskQueueManager
from ansible.executor.playbook_executor import PlaybookExecutor
from ansible.plugins.callback import CallbackBase
from ansible.inventory.manager import InventoryManager
from ansible.vars.manager import VariableManager
from ansible.utils.path import unfrackpath
from ansible import constants as C
from ansible.errors import AnsibleError
from ansible import context
from optparse import Values
from multiprocessing import cpu_count
import time
class Inventory_var:
class My_InventoryManager(InventoryManager):
def parse_sources(self, cache=False):
parsed = False
for source in self._sources:
if source:
if ',' not in source:
source = unfrackpath(source, follow=False)
parse = self.parse_source(source, cache=cache)
if parse and not parsed:
parsed = True
if parsed:
self._inventory.reconcile_inventory()
else:
if C.INVENTORY_UNPARSED_IS_FAILED:
raise AnsibleError("No inventory was parsed, please check your configuration and options.")
def __init__(self, loader,):
self.loader = loader
self.inventory = self.My_InventoryManager(loader=self.loader, sources=None)
def local_inventory(self, local_resource):
self.inventory = InventoryManager(loader=self.loader, sources=local_resource)
self.variable_manager = VariableManager(loader=self.loader, inventory=self.inventory)
return self
def __add_dynamic_inventory(self, hosts, groupname, ):
self.inventory.add_group(group=groupname)
for host in hosts:
ip = host.get("ip")
hostport = host.get("port")
self.inventory.add_host(host=ip, group=groupname, port=hostport)
self.inventory.add_host(host=ip, group='all', port=hostport)
def __set_variable(self, hosts,):
for host in hosts:
my_host = self.inventory.get_host(hostname=host.get("ip"))
hostname = host.get("hostname")
self.variable_manager.set_host_variable(host=my_host,varname='ansible_ssh_host',value= host.get('ip', hostname))
self.variable_manager.set_host_variable(host=my_host, varname='ansible_ssh_port', value=host.get("port"))
self.variable_manager.set_host_variable(host=my_host, varname='ansible_ssh_user', value=host.get("username"))
self.variable_manager.set_host_variable(host=my_host, varname='ansible_ssh_pass', value=host.get("password"))
self.variable_manager.set_host_variable(host=my_host, varname='ansible_ssh_private_key_file', value= host.get("ssh_key"))
for key, value in host.items():
if key not in ["hostname", "port", "username", "password"]:
self.variable_manager.set_host_variable(host=my_host, varname=key, value=value,)
def dynamic_inventory(self, dynamic_resource: dict):
if isinstance(dynamic_resource, dict):
for groupname, hosts_and_vars in dynamic_resource.items():
self.__add_dynamic_inventory(hosts=hosts_and_vars.get("hosts"), groupname=groupname,)
self.variable_manager = VariableManager(loader=self.loader, inventory=self.inventory)
for groupname, hosts_and_vars in dynamic_resource.items():
self.__set_variable(hosts=hosts_and_vars.get("hosts"))
return self
class ResultCallback(CallbackBase):
def __init__(self,log:list, *args, **kwargs,):
super(ResultCallback, self).__init__(*args, **kwargs)
self.log = log
def logmanager(self,result,):
self.result_json = ({ result._host.name: result._result})
self.log.append(self.result_json)
def v2_runner_on_ok(self, result, **kwargs):
self.logmanager(result)
def v2_runner_on_failed(self, result, ignore_errors=False):
self.logmanager(result)
def v2_runner_on_skipped(self, result):
self.logmanager(result)
def v2_runner_on_unreachable(self, result):
self.logmanager(result)
class Play_source:
def __init__(self,name:str,hosts,tasks:list,gather_facts='no'):
self.source = dict(
name=name,
hosts=hosts,
gather_facts=gather_facts,
tasks=[
dict(action=dict(module=task['module'], args=task['args']),register='shell_out',)
for task in tasks
]
)
class Ansible_scheduler:
def __init__(self,loader,inventory_var:Inventory_var ,options,password=None,extra_vars= dict()):
self.loader = loader
self.inventory_var = inventory_var
self.options = Values(options)
context._init_global_context(self.options)
self.password = password
def run_moudle(self,play_source:Play_source,log=[]):
play = Play().load(play_source.source, variable_manager=self.inventory_var.variable_manager, loader=self.loader,)
tqm = None
try:
tqm = TaskQueueManager(
inventory=self.inventory_var.inventory,
variable_manager=self.inventory_var.variable_manager,
loader=self.loader,
passwords=self.password,
stdout_callback=ResultCallback(log=log),
)
result = tqm.run(play)
finally:
if tqm is not None:
tqm.cleanup()
shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
return log
def run_playbook(self, playbook_path:list,log=[]):
C.DEFAULT_STDOUT_CALLBACK = ResultCallback(log=log)
playbook = PlaybookExecutor(playbooks=playbook_path,
inventory=self.inventory_var.inventory,
variable_manager=self.inventory_var.variable_manager,
loader=self.loader,
passwords=self.password,
)
playbook.run()
return log
class Options:
def __init__(self,options:dict,):
self.options = options
self.options.setdefault('verbosity', options.get('verbosity', 0))
self.options.setdefault('check', options.get('check', False))
self.options.setdefault('diff', options.get('diff', False))
self.options.setdefault('extra_vars', options.get('extra_vars', {}))
self.options.setdefault('fork', options.get('fork', cpu_count()))
self.options.setdefault('inventory', options.get('inventory', None))
self.options.setdefault('connection', options.get('connection', 'smart'))
self.options.setdefault('timeout', options.get('timeout', 10))
self.options.setdefault('remote_user', options.get('remote_user', None))
self.options.setdefault('ask_pass', options.get('ask_pass', False))
self.options.setdefault('private_key_file', options.get('private_key_file', None))
self.options.setdefault('ask_vault_pass', options.get('ask_vault_pass', False))
self.options.setdefault('vault_password_files', options.get('vault_password_files', []))
self.options.setdefault('vault_ids', options.get('vault_ids', []))
self.options.setdefault('module_path', options.get('module_path', None))
self.options.setdefault('listtasks', options.get('listtasks', None))
self.options.setdefault('listtags', options.get('listtags', None))
self.options.setdefault('step', options.get('step', None))
self.options.setdefault('start_at_task', options.get('start_at_task', None))
self.options.setdefault('args', options.get('args', None))
self.options.setdefault('force_handlers', options.get('force_handlers', False))
self.options.setdefault('flush_cache', options.get('flush_cache', None))
self.options.setdefault('tags', options.get('tags', ['all']))
self.options.setdefault('skip_tags', options.get('skip_tags', []))
self.options.setdefault('syntax', options.get('syntax', None))
self.options.setdefault('listhosts', options.get('listhosts', None))
self.options.setdefault('subset', options.get('subset', None))
self.options.setdefault('listtasks', options.get('listtasks', None))
self.options.setdefault('listtags', options.get('listtags', None))
self.options.setdefault('sftp_extra_args', options.get('sftp_extra_args', ''))
self.options.setdefault('scp_extra_args', options.get('scp_extra_args', ''))
self.options.setdefault('ssh_extra_args', options.get('ssh_extra_args', ''))
self.options.setdefault('ssh_common_args', options.get('ssh_common_args', ''))
self.options.setdefault('become', options.get('become', False))
self.options.setdefault('become_method', options.get('become_method', 'sudo'))
self.options.setdefault('become_user', options.get('become_user', None))
self.options.setdefault('become_ask_pass', options.get('become_ask_pass', False))
def lock(task_func):
def run(self, *args, **kwargs):
try:
if self.locked:
self.log_manager({'logid':'task','log':'task locking'})
else:
self.locked = True
self.log_manager({'logid':'task','log':'task running'})
task_func(self, *args, **kwargs)
self.locked = False
except Exception as e:
self.locked = False
self.log_manager({'logid':'task','log':'task erro','erro':str(e)})
return run
class Ansible_manager:
def __init__(self,local_resource:list,dynamic_esource:list,):
self.loader = DataLoader()
self.Inventory_var = Inventory_var(self.loader)
[ self.Inventory_var.local_inventory(source) for source in local_resource ]
[ self.Inventory_var.dynamic_inventory(source) for source in dynamic_esource ]
self.options = {}
self.Ansible_scheduler = Ansible_scheduler(loader=self.loader,inventory_var=self.Inventory_var,
options=Options(self.options).options)
self.locked = False
pass
def set_inventory_var(self,local_resource:list,dynamic_esource:list,):
self.Inventory_var = Inventory_var(self.loader)
[self.Inventory_var.local_inventory(source) for source in local_resource]
[self.Inventory_var.dynamic_inventory(source) for source in dynamic_esource]
self.Ansible_scheduler = Ansible_scheduler(loader=self.loader, inventory_var=self.Inventory_var,
options=Options(self.options).options)
def set_options(self,options:dict):
self.options = options
self.Ansible_scheduler = Ansible_scheduler(loader=self.loader, inventory_var=self.Inventory_var,
options=Options(self.options).options)
@lock
def ad_hoc(self,name:str,hostlist:list,tasks:list):
if not len(name) or not len(hostlist) or not len(tasks):
print('The variable cannot be empty')
raise Exception
play_source = Play_source(name=name, hosts=hostlist, tasks=tasks)
result = self.Ansible_scheduler.run_moudle(play_source)
self.log_manager({'logid':str(time.time()).replace('.',''),'taskname':name,'log':result})
@lock
def run_playbook_from_file(self,name:str,playbook_path):
if not len(name) or not len(playbook_path):
print('The variable cannot be empty')
raise Exception
result = self.Ansible_scheduler.run_playbook(playbook_path=playbook_path)
self.log_manager({'logid':str(time.time()).replace('.',''),'taskname':name,'log':result})
def log_manager(self, log):
print(json.dumps(log, indent=4))
pass
if __name__ == '__main__':
resource = [{
"dynamic_host": {
"hosts": [
{'username': u'root', 'password': '123456', 'ip': '192.168.0.105', 'hostname': '192', 'port': '22'},
{'username': u'root', 'password': '123465', 'ip': 'localhost', 'hostname': '150', 'port': '22'},
],
}
},
]
local_resource = ['/home/lozone/pycharm_project/plugins/ansible/hostslist', ]
playbook_path = ['/home/lozone/pycharm_project/plugins/ansible/f1.yml','/home/lozone/pycharm_project/plugins/ansible/f2.yml']
Ansible_manager = Ansible_manager(local_resource=local_resource,dynamic_esource=resource)
Ansible_manager.set_options({})
tasks = [{'module': 'shell', 'args': 'touch /tmp/x1'},{'module': 'shell', 'args': 'touch /tmp/x2;'}]
Ansible_manager.ad_hoc(name='test',hostlist=['localhost','192.168.0.105'],tasks=tasks)
Ansible_manager.run_playbook_from_file(name='test1',playbook_path=playbook_path)