jumpserver/jperm/ansible_api.py

506 lines
16 KiB
Python
Raw Normal View History

2015-10-29 08:32:02 +00:00
# -*- coding: utf-8 -*-
from ansible.inventory.group import Group
from ansible.inventory.host import Host
from ansible.inventory import Inventory
from ansible.runner import Runner
from ansible.playbook import PlayBook
from ansible import callbacks
from ansible import utils
2015-10-31 03:55:19 +00:00
from passlib.hash import sha512_crypt
from utils import get_rand_pass
2015-12-08 11:25:11 +00:00
from jumpserver.api import logger
2015-12-08 11:25:11 +00:00
from tempfile import NamedTemporaryFile
from django.template.loader import get_template
from django.template import Context
2015-10-29 08:32:02 +00:00
import os.path
API_DIR = os.path.dirname(os.path.abspath(__file__))
ANSIBLE_DIR = os.path.join(API_DIR, 'playbooks')
2015-10-30 00:25:22 +00:00
class AnsibleError(StandardError):
2015-10-29 08:32:02 +00:00
"""
2015-10-30 00:25:22 +00:00
the base AnsibleError which contains error(required),
data(optional) and message(optional).
存储所有Ansible 异常对象
"""
def __init__(self, error, data='', message=''):
super(AnsibleError, self).__init__(message)
self.error = error
self.data = data
self.message = message
class CommandValueError(AnsibleError):
"""
indicate the input value has error or invalid.
the data specifies the error field of input form.
输入不合法 异常对象
"""
def __init__(self, field, message=''):
super(CommandValueError, self).__init__('value:invalid', field, message)
class MyInventory(Inventory):
2015-10-30 00:25:22 +00:00
"""
this is my ansible inventory object.
2015-10-29 08:32:02 +00:00
"""
def __init__(self, resource):
"""
resource的数据格式是一个列表字典比如
{
"group1": {
"hosts": [{"hostname": "10.10.10.10", "port": "22", "username": "test", "password": "mypass"}, ...],
"vars": {"var1": value1, "var2": value2, ...}
}
}
如果你只传入1个列表这默认该列表内的所有主机属于my_group组,比如
[{"hostname": "10.10.10.10", "port": "22", "username": "test", "password": "mypass"}, ...]
2015-10-29 08:32:02 +00:00
"""
self.resource = resource
self.inventory = Inventory(host_list=[])
2015-10-31 03:55:19 +00:00
self.gen_inventory()
2015-10-29 08:32:02 +00:00
def my_add_group(self, hosts, groupname, groupvars=None):
2015-10-29 08:32:02 +00:00
"""
2015-10-31 03:55:19 +00:00
add hosts to a group
2015-10-29 08:32:02 +00:00
"""
2015-10-31 03:55:19 +00:00
my_group = Group(name=groupname)
# if group variables exists, add them to group
if groupvars:
for key, value in groupvars.iteritems():
my_group.set_variable(key, value)
# add hosts to group
2015-10-31 03:55:19 +00:00
for host in hosts:
# set connection variables
hostname = host.get("hostname")
hostip = host.get('ip', hostname)
hostport = host.get("port")
username = host.get("username")
password = host.get("password")
2015-11-20 13:30:57 +00:00
ssh_key = host.get("ssh_key")
2015-10-29 08:32:02 +00:00
my_host = Host(name=hostname, port=hostport)
my_host.set_variable('ansible_ssh_host', hostip)
2015-10-29 08:32:02 +00:00
my_host.set_variable('ansible_ssh_port', hostport)
my_host.set_variable('ansible_ssh_user', username)
2015-10-29 08:32:02 +00:00
my_host.set_variable('ansible_ssh_pass', password)
2015-11-20 13:30:57 +00:00
my_host.set_variable('ansible_ssh_private_key_file', ssh_key)
# set other variables
for key, value in host.iteritems():
if key not in ["hostname", "port", "username", "password"]:
my_host.set_variable(key, value)
# add to group
2015-10-29 08:32:02 +00:00
my_group.add_host(my_host)
2015-10-31 03:55:19 +00:00
self.inventory.add_group(my_group)
2015-10-29 08:32:02 +00:00
2015-10-31 03:55:19 +00:00
def gen_inventory(self):
"""
add hosts to inventory.
"""
if isinstance(self.resource, list):
self.my_add_group(self.resource, 'default_group')
2015-10-31 03:55:19 +00:00
elif isinstance(self.resource, dict):
for groupname, hosts_and_vars in self.resource.iteritems():
self.my_add_group(hosts_and_vars.get("hosts"), groupname, hosts_and_vars.get("vars"))
2015-10-29 08:32:02 +00:00
2015-10-30 00:25:22 +00:00
2015-11-20 10:42:44 +00:00
class MyRunner(MyInventory):
"""
This is a General object for parallel execute modules.
"""
def __init__(self, *args, **kwargs):
super(MyRunner, self).__init__(*args, **kwargs)
2015-12-03 08:53:39 +00:00
self.results_raw = {}
2015-11-20 10:42:44 +00:00
2015-12-05 04:03:18 +00:00
def run(self, module_name='shell', module_args='', timeout=10, forks=10, pattern='*',
become=False, become_method='sudo', become_user='root', become_pass=''):
2015-11-20 10:42:44 +00:00
"""
run module from andible ad-hoc.
module_name: ansible module_name
module_args: ansible module args
"""
hoc = Runner(module_name=module_name,
module_args=module_args,
timeout=timeout,
inventory=self.inventory,
pattern=pattern,
forks=forks,
2015-12-05 04:03:18 +00:00
become=become,
become_method=become_method,
become_user=become_user,
become_pass=become_pass
2015-11-20 10:42:44 +00:00
)
2015-12-03 08:53:39 +00:00
self.results_raw = hoc.run()
2015-12-08 11:25:11 +00:00
logger.debug(self.results_raw)
2015-12-03 08:53:39 +00:00
return self.results_raw
2015-11-20 10:42:44 +00:00
2015-12-03 08:53:39 +00:00
@property
def results(self):
"""
{'failed': {'localhost': ''}, 'ok': {'jumpserver': ''}}
"""
result = {'failed': {}, 'ok': {}}
dark = self.results_raw.get('dark')
contacted = self.results_raw.get('contacted')
2015-12-02 05:35:06 +00:00
if dark:
for host, info in dark.items():
result['failed'][host] = info.get('msg')
if contacted:
for host, info in contacted.items():
2015-12-09 06:50:48 +00:00
if info.get('invocation').get('module_name') in ['raw', 'shell', 'command', 'script']:
if info.get('rc') == 0:
result['ok'][host] = info.get('stdout') + info.get('stderr')
else:
result['failed'][host] = info.get('stdout') + info.get('stderr')
2015-12-02 05:35:06 +00:00
else:
2015-12-09 06:50:48 +00:00
if info.get('failed'):
result['failed'][host] = info.get('msg')
else:
result['ok'][host] = info.get('changed')
2015-12-02 05:35:06 +00:00
return result
2015-11-20 10:42:44 +00:00
2015-10-30 00:25:22 +00:00
class Command(MyInventory):
"""
this is a command object for parallel execute command.
"""
def __init__(self, *args, **kwargs):
super(Command, self).__init__(*args, **kwargs)
2015-12-03 08:53:39 +00:00
self.results_raw = {}
2015-10-30 00:25:22 +00:00
2015-12-03 08:53:39 +00:00
def run(self, command, module_name="command", timeout=10, forks=10, pattern=''):
2015-10-29 08:32:02 +00:00
"""
2015-10-30 00:25:22 +00:00
run command from andible ad-hoc.
2015-10-29 08:32:02 +00:00
command : 必须是一个需要执行的命令字符串 比如
'uname -a'
"""
2015-11-20 10:42:44 +00:00
data = {}
2015-10-30 00:25:22 +00:00
if module_name not in ["raw", "command", "shell"]:
raise CommandValueError("module_name",
2015-11-20 10:42:44 +00:00
"module_name must be of the 'raw, command, shell'")
2015-11-20 13:30:57 +00:00
hoc = Runner(module_name=module_name,
module_args=command,
timeout=timeout,
inventory=self.inventory,
pattern=pattern,
forks=forks,
2015-10-29 08:32:02 +00:00
)
2015-12-03 08:53:39 +00:00
self.results_raw = hoc.run()
2015-12-03 08:53:39 +00:00
@property
def result(self):
result = {}
for k, v in self.results_raw.items():
if k == 'dark':
for host, info in v.items():
result[host] = {'dark': info.get('msg')}
elif k == 'contacted':
for host, info in v.items():
result[host] = {}
if info.get('stdout'):
result[host]['stdout'] = info.get('stdout')
elif info.get('stderr'):
result[host]['stderr'] = info.get('stderr')
return result
2015-12-03 08:53:39 +00:00
@property
def state(self):
result = {}
if self.stdout:
2015-12-03 08:53:39 +00:00
result['ok'] = self.stdout
2015-11-20 10:42:44 +00:00
if self.stderr:
2015-12-03 08:53:39 +00:00
result['err'] = self.stderr
2015-11-20 10:42:44 +00:00
if self.dark:
2015-12-03 08:53:39 +00:00
result['dark'] = self.dark
return result
2015-10-29 08:32:02 +00:00
@property
def exec_time(self):
"""
2015-10-30 00:25:22 +00:00
get the command execute time.
2015-10-29 08:32:02 +00:00
"""
result = {}
2015-12-03 08:53:39 +00:00
all = self.results_raw.get("contacted")
2015-10-29 08:32:02 +00:00
for key, value in all.iteritems():
result[key] = {
"start": value.get("start"),
"end" : value.get("end"),
"delta": value.get("delta"),}
return result
@property
def stdout(self):
"""
2015-10-30 00:25:22 +00:00
get the comamnd standard output.
2015-10-29 08:32:02 +00:00
"""
result = {}
2015-12-03 08:53:39 +00:00
all = self.results_raw.get("contacted")
2015-10-29 08:32:02 +00:00
for key, value in all.iteritems():
2015-11-20 10:42:44 +00:00
result[key] = value.get("stdout")
2015-10-29 08:32:02 +00:00
return result
@property
def stderr(self):
"""
2015-10-30 00:25:22 +00:00
get the command standard error.
2015-10-29 08:32:02 +00:00
"""
result = {}
2015-12-03 08:53:39 +00:00
all = self.results_raw.get("contacted")
2015-10-29 08:32:02 +00:00
for key, value in all.iteritems():
2015-11-20 10:42:44 +00:00
if value.get("stderr") or value.get("warnings"):
result[key] = {
2015-10-29 08:32:02 +00:00
"stderr": value.get("stderr"),
"warnings": value.get("warnings"),}
return result
@property
def dark(self):
"""
2015-10-30 00:25:22 +00:00
get the dark results.
2015-10-29 08:32:02 +00:00
"""
2015-12-03 08:53:39 +00:00
return self.results_raw.get("dark")
2015-10-29 08:32:02 +00:00
2015-10-30 00:25:22 +00:00
2015-12-05 04:03:18 +00:00
class MyTask(MyRunner):
2015-10-30 00:25:22 +00:00
"""
this is a tasks object for include the common command.
"""
def __init__(self, *args, **kwargs):
2015-12-05 04:03:18 +00:00
super(MyTask, self).__init__(*args, **kwargs)
2015-10-30 15:08:45 +00:00
2015-10-31 03:55:19 +00:00
def push_key(self, user, key_path):
"""
push the ssh authorized key to target.
"""
2015-11-29 11:02:13 +00:00
module_args = 'user="%s" key="{{ lookup("file", "%s") }}" state=present' % (user, key_path)
2015-12-05 04:03:18 +00:00
self.run("authorized_key", module_args, become=True)
2015-10-30 15:08:45 +00:00
2015-12-05 04:03:18 +00:00
return self.results
2015-10-30 15:08:45 +00:00
def push_multi_key(self, **user_info):
"""
push multi key
:param user_info:
:return:
"""
ret_failed = []
ret_success = []
for user, key_path in user_info.iteritems():
ret = self.push_key(user, key_path)
if ret.get("status") == "ok":
ret_success.append(ret)
if ret.get("status") == "failed":
ret_failed.append(ret)
if ret_failed:
return {"status": "failed", "msg": ret_failed}
else:
return {"status": "success", "msg": ret_success}
def del_key(self, user, key_path):
"""
push the ssh authorized key to target.
"""
module_args = 'user="%s" key="{{ lookup("file", "%s") }}" state="absent"' % (user, key_path)
2015-12-05 04:03:18 +00:00
self.run("authorized_key", module_args, become=True)
2015-12-05 04:03:18 +00:00
return self.results
2015-11-29 11:02:13 +00:00
def add_user(self, username, password=''):
2015-10-30 15:08:45 +00:00
"""
add a host user.
"""
2015-11-30 15:08:30 +00:00
2015-11-29 11:02:13 +00:00
if password:
encrypt_pass = sha512_crypt.encrypt(password)
module_args = 'name=%s shell=/bin/bash password=%s' % (username, encrypt_pass)
else:
module_args = 'name=%s shell=/bin/bash' % username
2015-10-30 15:08:45 +00:00
2015-12-05 04:03:18 +00:00
self.run("user", module_args, become=True)
2015-10-30 15:08:45 +00:00
2015-12-05 04:03:18 +00:00
return self.results
2015-10-30 15:08:45 +00:00
def add_multi_user(self, **user_info):
"""
add multi user
:param user_info: keyword args
{username: password}
:return:
"""
ret_success = []
ret_failed = []
for user, password in user_info.iteritems():
ret = self.add_user(user, password)
if ret.get("status") == "ok":
ret_success.append(ret)
if ret.get("status") == "failed":
ret_failed.append(ret)
if ret_failed:
return {"status": "failed", "msg": ret_failed}
else:
return {"status": "success", "msg": ret_success}
2015-10-31 03:55:19 +00:00
def del_user(self, username):
2015-10-30 15:08:45 +00:00
"""
delete a host user.
"""
2015-12-05 04:03:18 +00:00
module_args = 'name=%s state=absent remove=yes move_home=yes force=yes' % username
self.run("user", module_args, become=True)
return self.results
2015-10-30 15:08:45 +00:00
2015-12-08 11:25:11 +00:00
@staticmethod
def gen_sudo_script(role_list, sudo_list):
# receive role_list = [role1, role2] sudo_list = [sudo1, sudo2]
# return sudo_alias={'NETWORK': '/sbin/ifconfig, /ls'} sudo_user={'user1': ['NETWORK', 'SYSTEM']}
sudo_alias = {}
sudo_user = {}
for sudo in sudo_list:
sudo_alias[sudo.name] = sudo.commands
2015-12-08 11:25:11 +00:00
for role in role_list:
sudo_user[role.name] = ','.join(sudo_alias.keys())
2015-12-08 11:25:11 +00:00
sudo_j2 = get_template('jperm/role_sudo.j2')
sudo_content = sudo_j2.render(Context({"sudo_alias": sudo_alias, "sudo_user": sudo_user}))
sudo_file = NamedTemporaryFile(delete=False)
sudo_file.write(sudo_content)
sudo_file.close()
return sudo_file.name
2015-12-08 11:25:11 +00:00
def push_sudo_file(self, role_list, sudo_list):
"""
use template to render pushed sudoers file
:return:
"""
2015-12-08 11:25:11 +00:00
module_args1 = self.gen_sudo_script(role_list, sudo_list)
2015-12-05 04:03:18 +00:00
self.run("script", module_args1, become=True)
return self.results
2015-11-29 07:18:05 +00:00
2015-10-31 03:55:19 +00:00
class CustomAggregateStats(callbacks.AggregateStats):
"""
Holds stats about per-host activity during playbook runs.
"""
def __init__(self):
super(CustomAggregateStats, self).__init__()
self.results = []
2015-10-31 03:55:19 +00:00
def compute(self, runner_results, setup=False, poll=False,
ignore_errors=False):
"""
Walk through all results and increment stats.
"""
super(CustomAggregateStats, self).compute(runner_results, setup, poll,
ignore_errors)
2015-10-31 03:55:19 +00:00
self.results.append(runner_results)
2015-10-31 03:55:19 +00:00
def summarize(self, host):
"""
Return information about a particular host
"""
summarized_info = super(CustomAggregateStats, self).summarize(host)
2015-10-31 03:55:19 +00:00
# Adding the info I need
summarized_info['result'] = self.results
2015-10-31 03:55:19 +00:00
return summarized_info
2015-10-31 03:55:19 +00:00
2015-10-30 00:25:22 +00:00
class MyPlaybook(MyInventory):
"""
this is my playbook object for execute playbook.
"""
def __init__(self, *args, **kwargs):
super(MyPlaybook, self).__init__(*args, **kwargs)
def run(self, playbook_relational_path, extra_vars=None):
2015-10-31 03:55:19 +00:00
"""
run ansible playbook,
only surport relational path.
"""
stats = callbacks.AggregateStats()
playbook_cb = callbacks.PlaybookCallbacks(verbose=utils.VERBOSITY)
2015-10-31 03:55:19 +00:00
runner_cb = callbacks.PlaybookRunnerCallbacks(stats, verbose=utils.VERBOSITY)
playbook_path = os.path.join(ANSIBLE_DIR, playbook_relational_path)
pb = PlayBook(
playbook=playbook_path,
stats=stats,
callbacks=playbook_cb,
runner_callbacks=runner_cb,
inventory=self.inventory,
extra_vars=extra_vars,
check=False)
2015-10-31 03:55:19 +00:00
self.results = pb.run()
@property
def raw_results(self):
2015-10-30 00:25:22 +00:00
"""
2015-10-31 03:55:19 +00:00
get the raw results after playbook run.
2015-10-30 00:25:22 +00:00
"""
2015-10-31 03:55:19 +00:00
return self.results
2015-10-30 00:25:22 +00:00
class App(MyPlaybook):
"""
this is a app object for inclue the common playbook.
"""
def __init__(self, *args, **kwargs):
super(App, self).__init__(*args, **kwargs)
2015-10-29 08:32:02 +00:00
if __name__ == "__main__":
# resource = {
# "group1": {
# "hosts": [{"hostname": "127.0.0.1", "port": "22", "username": "root", "password": "xxx"},],
# "vars" : {"var1": "value1", "var2": "value2"},
# },
# }
resource = [{"hostname": "127.0.0.1", "port": "22", "username": "yumaojun", "password": "yusky0902",
# "ansible_become": "yes",
# "ansible_become_method": "sudo",
# # "ansible_become_user": "root",
# "ansible_become_pass": "yusky0902",
}]
cmd = Command(resource)
print cmd.run('ls')
# resource = [{"hostname": "192.168.10.148", "port": "22", "username": "root", "password": "xxx"}]
# task = Tasks(resource)
# print task.get_host_info()
# playbook = MyPlaybook(resource)
# playbook.run('test.yml')
# print playbook.raw_results
# task = Tasks(resource)
# print task.add_user('test', 'mypass')
2015-10-31 03:55:19 +00:00
# print task.del_user('test')
# print task.push_key('root', '/root/.ssh/id_rsa.pub')
# print task.del_key('root', '/root/.ssh/id_rsa.pub')
# task = Tasks(resource)
# print task.add_init_users()
# print task.del_init_users()