diff --git a/jasset/ansible_api.py b/jasset/ansible_api.py deleted file mode 100644 index 7041c7bf1..000000000 --- a/jasset/ansible_api.py +++ /dev/null @@ -1,459 +0,0 @@ -# -*- 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 -from passlib.hash import sha512_crypt - -# from utils import get_rand_pass - -import random -import os.path -API_DIR = os.path.dirname(os.path.abspath(__file__)) -ANSIBLE_DIR = os.path.join(API_DIR, 'playbooks') - -def get_rand_pass(): - """ - get a reandom password. - """ - lower = [chr(i) for i in range(97,123)] - upper = [chr(i).upper() for i in range(97,123)] - digit = [str(i) for i in range(10)] - password_pool = [] - password_pool.extend(lower) - password_pool.extend(upper) - password_pool.extend(digit) - pass_list = [random.choice(password_pool) for i in range(1,14)] - pass_list.insert(random.choice(range(1,14)), '@') - pass_list.insert(random.choice(range(1,14)), random.choice(digit)) - password = ''.join(pass_list) - return password - -class AnsibleError(StandardError): - """ - 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(object): - """ - this is my ansible inventory object. - """ - 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"}, ...] - """ - self.resource = resource - self.inventory = Inventory() - self.gen_inventory() - - def add_group(self, hosts, groupname, groupvars=None): - """ - add hosts to a group - """ - 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 - for host in hosts: - # set connection variables - hostname = host.get("hostname") - hostport = host.get("port") - username = host.get("username") - password = host.get("password") - my_host = Host(name=hostname, port=hostport) - my_host.set_variable('ansible_ssh_host', hostname) - my_host.set_variable('ansible_ssh_port', hostport) - my_host.set_variable('ansible_ssh_user', username) - my_host.set_variable('ansible_ssh_pass', password) - # 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 - my_group.add_host(my_host) - - self.inventory.add_group(my_group) - - def gen_inventory(self): - """ - add hosts to inventory. - """ - if isinstance(self.resource, list): - self.add_group(self.resource, 'my_group') - elif isinstance(self.resource, dict): - for groupname, hosts_and_vars in self.resource.iteritems(): - self.add_group(hosts_and_vars.get("hosts"), groupname, hosts_and_vars.get("vars")) - - -class Command(MyInventory): - """ - this is a command object for parallel execute command. - """ - def __init__(self, *args, **kwargs): - super(Command, self).__init__(*args, **kwargs) - self.results = '' - - def run(self, command, module_name="command", timeout=5, forks=10, group='my_group'): - """ - run command from andible ad-hoc. - command : 必须是一个需要执行的命令字符串, 比如 - 'uname -a' - """ - if module_name not in ["raw", "command", "shell"]: - raise CommandValueError("module_name", - "module_name must be of the 'raw, command, shell'") - hoc = Runner(module_name=module_name, - module_args=command, - timeout=timeout, - inventory=self.inventory, - subset=group, - forks=forks - ) - self.results = hoc.run() - - if self.stdout: - return {"ok": self.stdout} - else: - msg = [] - if self.stderr: - msg.append(self.stderr) - if self.dark: - msg.append(self.dark) - return {"failed": msg} - - @property - def raw_results(self): - """ - get the ansible raw results. - """ - return self.results - - @property - def exec_time(self): - """ - get the command execute time. - """ - result = {} - all = self.results.get("contacted") - 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): - """ - get the comamnd standard output. - """ - result = {} - all = self.results.get("contacted") - for key, value in all.iteritems(): - result[key] = value.get("stdout") - return result - - @property - def stderr(self): - """ - get the command standard error. - """ - result = {} - all = self.results.get("contacted") - for key, value in all.iteritems(): - result[key] = { - "stderr": value.get("stderr"), - "warnings": value.get("warnings"),} - return result - - @property - def dark(self): - """ - get the dark results. - """ - return self.results.get("dark") - - -class Tasks(Command): - """ - this is a tasks object for include the common command. - """ - def __init__(self, *args, **kwargs): - super(Tasks, self).__init__(*args, **kwargs) - - def __run(self, module_args, module_name="command", timeout=5, forks=10, group='my_group'): - """ - run command from andible ad-hoc. - command : 必须是一个需要执行的命令字符串, 比如 - 'uname -a' - """ - hoc = Runner(module_name=module_name, - module_args=module_args, - timeout=timeout, - inventory=self.inventory, - subset=group, - forks=forks - ) - - self.results = hoc.run() - - @property - def msg(self): - """ - get the contacted and dark msg - """ - msg = {} - for result in ["contacted", "dark"]: - all = self.results.get(result) - for key, value in all.iteritems(): - if value.get("msg"): - msg[key] = value.get("msg") - return msg - - def push_key(self, user, key_path): - """ - push the ssh authorized key to target. - """ - module_args = 'user="%s" key="{{ lookup("file", "%s") }}"' % (user, key_path) - self.__run(module_args, "authorized_key") - - return {"status": "failed","msg": self.msg} if self.msg else {"status": "ok"} - - 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) - self.__run(module_args, "authorized_key") - - return {"status": "failed","msg": self.msg} if self.msg else {"status": "ok"} - - def add_user(self, username, password): - """ - add a host user. - """ - encrypt_pass = sha512_crypt.encrypt(password) - module_args = 'name=%s shell=/bin/bash password=%s' % (username, encrypt_pass) - self.__run(module_args, "user") - - return {"status": "failed","msg": self.msg} if self.msg else {"status": "ok"} - - def add_multi_user(self, *args): - """ - add multi user - :param args: - user - :return: - """ - results = {} - users = {} - action = results["action_info"] = {} - for user in args: - users[user] = get_rand_pass() - for user, password in users.iteritems(): - ret = self.add_user(user, password) - action[user] = ret - results["user_info"] = users - - return results - - def del_user(self, username): - """ - delete a host user. - """ - module_args = 'name=%s state=absent remove=yes move_home=yes force=yes' % (username) - self.__run(module_args, "user") - - return {"status": "failed","msg": self.msg} if self.msg else {"status": "ok"} - - def add_init_users(self): - """ - add initail users: SA, DBA, DEV - """ - results = {} - action = results["action_info"] = {} - users = {"SA": get_rand_pass(), "DBA": get_rand_pass(), "DEV": get_rand_pass()} - for user, password in users.iteritems(): - ret = self.add_user(user, password) - action[user] = ret - results["user_info"] = users - - return results - - def del_init_users(self): - """ - delete initail users: SA, DBA, DEV - """ - results = {} - action = results["action_info"] = {} - for user in ["SA", "DBA", "DEV"]: - ret = self.del_user(user) - action[user] = ret - return results - - def get_host_info(self): - """ - use the setup module get host informations - :return: - all_ip is list - processor_count is int - system_dist_version is string - system_type is string - disk is dict (device_name: device_size} - system_dist is string - processor_type is string - default_ip is string - hostname is string - product_sn is string - memory_total is int (MB) - default_mac is string - product_name is string - """ - self.__run('', 'setup') - - result = {} - all = self.results.get("contacted") - for key, value in all.iteritems(): - setup =value.get("ansible_facts") - # get disk informations - disk_all = setup.get("ansible_devices") - disk_need = {} - for disk_name, disk_info in disk_all.iteritems(): - if disk_name.startswith('sd') or disk_name.startswith('hd') or disk_name.startswith('vd'): - disk_need[disk_name] = disk_info.get("size") - - result[key] = { - "other_ip": setup.get("ansible_all_ipv4_addresses"), - "hostname": setup.get("ansible_hostname" ), - "ip": setup.get("ansible_default_ipv4").get("address"), - "mac": setup.get("ansible_default_ipv4").get("macaddress"), - "brand": setup.get("ansible_product_name"), - "cpu_type": setup.get("ansible_processor"), - "cpu_cores": setup.get("ansible_processor_count"), - "memory": setup.get("ansible_memtotal_mb"), - "disk": disk_need, - "system_type": setup.get("ansible_distribution"), - "system_version": setup.get("ansible_distribution_version"), - "asset_type": setup.get("ansible_system"), - "sn": setup.get("ansible_product_serial") - } - - return {"status": "failed", "msg": self.msg} if self.msg else {"status": "ok", "result": result} - - -class CustomAggregateStats(callbacks.AggregateStats): - """ - Holds stats about per-host activity during playbook runs. - """ - def __init__(self): - super(CustomAggregateStats, self).__init__() - self.results = [] - - 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) - - self.results.append(runner_results) - - - def summarize(self, host): - """ - Return information about a particular host - """ - summarized_info = super(CustomAggregateStats, self).summarize(host) - - # Adding the info I need - summarized_info['result'] = self.results - - return summarized_info - - -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): - """ - run ansible playbook, - only surport relational path. - """ - stats = callbacks.AggregateStats() - playbook_cb = callbacks.PlaybookCallbacks(verbose=utils.VERBOSITY) - 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) - - self.results = pb.run() - - @property - def raw_results(self): - """ - get the raw results after playbook run. - """ - return self.results - - -class App(MyPlaybook): - """ - this is a app object for inclue the common playbook. - """ - def __init__(self, *args, **kwargs): - super(App, self).__init__(*args, **kwargs) - - -if __name__ == "__main__": - pass - - diff --git a/jasset/asset_api.py b/jasset/asset_api.py index a37386a72..9cc375509 100644 --- a/jasset/asset_api.py +++ b/jasset/asset_api.py @@ -4,6 +4,8 @@ import xlsxwriter from django.db.models import AutoField from jumpserver.api import * from jasset.models import ASSET_STATUS, ASSET_TYPE, ASSET_ENV, IDC, AssetRecord +from jperm.ansible_api import MyRunner +from jperm.perm_api import gen_resource def group_add_asset(group, asset_id=None, asset_ip=None): @@ -406,3 +408,64 @@ def excel_to_db(excel_file): asset.group = group_instance asset.save() return True + + +def get_ansible_asset_info(asset_ip, setup_info): + disk_all = setup_info.get("ansible_devices") + disk_need = {} + for disk_name, disk_info in disk_all.iteritems(): + if disk_name.startswith('sd') or disk_name.startswith('hd') or disk_name.startswith('vd'): + disk_need[disk_name] = disk_info.get("size") + + all_ip = setup_info.get("ansible_all_ipv4_addresses") + other_ip_list = all_ip.remove(asset_ip) if asset_ip in all_ip else [] + other_ip = ','.join(other_ip_list) if other_ip_list else '' + # hostname = setup_info.get("ansible_hostname") + # ip = setup_info.get("ansible_default_ipv4").get("address") + mac = setup_info.get("ansible_default_ipv4").get("macaddress") + brand = setup_info.get("ansible_product_name") + cpu_type = setup_info.get("ansible_processor")[1] + cpu_cores = setup_info.get("ansible_processor_count") + cpu = cpu_type + ' * ' + unicode(cpu_cores) + memory = setup_info.get("ansible_memtotal_mb") + disk = disk_need + system_type = setup_info.get("ansible_distribution") + system_version = setup_info.get("ansible_distribution_version") + # asset_type = setup_info.get("ansible_system") + sn = setup_info.get("ansible_product_serial") + asset_info = [other_ip, mac, cpu, memory, disk, sn, system_type, system_version, brand] + + return asset_info + + +def asset_ansible_update(obj_list, name=''): + resource = gen_resource(obj_list) + ansible_instance = MyRunner(resource) + ansible_asset_info = ansible_instance.run(module_name='setup', pattern='*') + for asset in obj_list: + try: + setup_info = ansible_asset_info['contacted'][asset.hostname]['ansible_facts'] + except KeyError: + continue + else: + asset_info = get_ansible_asset_info(asset.ip, setup_info) + other_ip, mac, cpu, memory, disk, sn, system_type, system_version, brand = asset_info + asset_dic = {"other_ip": other_ip, + "mac": mac, + "cpu": cpu, + "memory": memory, + "disk": disk, + "sn": sn, + "system_type": system_type, + "system_version": system_version, + "brand": brand + } + + ansible_record(asset, asset_dic, name) + + +def asset_ansible_update_all(): + name = u'定时更新' + asset_all = Asset.objects.all() + asset_ansible_update(asset_all, name) + diff --git a/jasset/models.py b/jasset/models.py index 159d72254..4f0ce5107 100644 --- a/jasset/models.py +++ b/jasset/models.py @@ -73,7 +73,7 @@ class Asset(models.Model): memory = models.CharField(max_length=128, blank=True, null=True, verbose_name=u'内存') disk = models.CharField(max_length=128, blank=True, null=True, verbose_name=u'硬盘') system_type = models.CharField(max_length=32, blank=True, null=True, verbose_name=u"系统类型") - system_version = models.CharField(max_length=8, blank=True, null=True, verbose_name=u"版本号") + system_version = models.CharField(max_length=8, blank=True, null=True, verbose_name=u"系统版本号") cabinet = models.CharField(max_length=32, blank=True, null=True, verbose_name=u'机柜号') position = models.IntegerField(blank=True, null=True, verbose_name=u'机器位置') number = models.CharField(max_length=32, blank=True, null=True, verbose_name=u'资产编号') diff --git a/jasset/urls.py b/jasset/urls.py index 10d6dafc7..8fcbcdea4 100644 --- a/jasset/urls.py +++ b/jasset/urls.py @@ -11,6 +11,7 @@ urlpatterns = patterns('', url(r"^asset_detail/$", asset_detail), url(r'^asset_edit/$', asset_edit), url(r'^asset_update/$', asset_update), + url(r'^asset_update_batch/$', asset_update_batch), # url(r'^search/$', host_search), # url(r"^show_all_ajax/$", show_all_ajax), url(r'^group_add/$', group_add), diff --git a/jasset/views.py b/jasset/views.py index 9bbff91de..54bc576a7 100644 --- a/jasset/views.py +++ b/jasset/views.py @@ -6,7 +6,8 @@ from jumpserver.api import * from jumpserver.models import Setting from jasset.forms import AssetForm, IdcForm from jasset.models import Asset, IDC, AssetGroup, ASSET_TYPE, ASSET_STATUS -from ansible_api import Tasks +from jperm.ansible_api import Tasks, MyRunner +from jperm.perm_api import gen_resource @require_role('admin') @@ -197,7 +198,7 @@ def asset_edit(request): header_title, path1, path2 = u'修改资产', u'资产管理', u'修改资产' asset_id = request.GET.get('id', '') - username = request.session.get('username', 'admin') + username = request.user.username asset = get_object(Asset, id=asset_id) if asset: password_old = asset.password @@ -311,7 +312,7 @@ def asset_list(request): @require_role('admin') def asset_edit_batch(request): af = AssetForm() - name = request.session.get('username', 'admin') + name = request.user.username asset_group_all = AssetGroup.objects.all() if request.method == 'POST': @@ -409,55 +410,30 @@ def asset_update(request): """ asset_id = request.GET.get('id', '') asset = get_object(Asset, id=asset_id) - name = request.session.get('username', 'admin') + name = request.user.username if not asset: return HttpResponseRedirect('/jasset/asset_detail/?id=%s' % asset_id) - if asset.use_default_auth: - default = Setting.objects.all() - if default: - default = default[0] - username = default.default_user - password = CRYPTOR.decrypt(default.default_password) - port = default.default_port - else: - return HttpResponse(u'没有设置默认用户名和密码!') else: - username = asset.username - password = CRYPTOR.decrypt(asset.password) - port = asset.port - - resource = [{"hostname": asset.ip, "port": port, - "username": username, "password": password}] - - ansible_instance = Tasks(resource) - ansible_asset_info = ansible_instance.get_host_info() - if ansible_asset_info['status'] == 'ok': - asset_info = ansible_asset_info['result'][asset.ip] - if asset_info: - hostname = asset_info.get('hostname') - all_ip = asset_info.get('other_ip') - other_ip_list = all_ip.remove(asset.ip) if asset.ip in all_ip else [] - other_ip = ','.join(other_ip_list) if other_ip_list else '' - cpu_type = asset_info.get('cpu_type')[1] - cpu_cores = asset_info.get('cpu_cores') - cpu = cpu_type + ' * ' + unicode(cpu_cores) - memory = asset_info.get('memory') - disk = asset_info.get('disk') - sn = asset_info.get('sn') - brand = asset_info.get('brand') - system_type = asset_info.get('system_type') - system_version = asset_info.get('system_version') - - asset_dic = {"hostname": hostname, "other_ip": other_ip, "cpu": cpu, - "memory": memory, "disk": disk, "system_type": system_type, - "system_version": system_version, "brand": brand, "sn": sn - } - - ansible_record(asset, asset_dic, name) - + asset_ansible_update(asset_list, name) return HttpResponseRedirect('/jasset/asset_detail/?id=%s' % asset_id) +@require_role('admin') +def asset_update_batch(request): + if request.method == 'POST': + asset_list = [] + name = unicode(request.user.username) + ' - ' + u'自动更新' + asset_id_all = unicode(request.POST.get('asset_id_all', '')) + asset_id_all = asset_id_all.split(',') + for asset_id in asset_id_all: + asset = get_object(Asset, id=asset_id) + if asset: + asset_list.append(asset) + asset_ansible_update(asset_list, name) + return HttpResponse(u'批量更新成功!') + return HttpResponse(u'批量更新成功!') + + @require_role('admin') def idc_add(request): """ @@ -478,9 +454,7 @@ def idc_add(request): return HttpResponseRedirect("/jasset/idc_list/") else: idc_form = IdcForm() - return render_to_response('jasset/idc_add.html', - locals(), - context_instance=RequestContext(request)) + return my_render('jasset/idc_add.html', locals(), request) @require_role('admin') @@ -496,9 +470,7 @@ def idc_list(request): else: posts = IDC.objects.exclude(name='ALL').order_by('id') contact_list, p, contacts, page_range, current_page, show_first, show_end = pages(posts, request) - return render_to_response('jasset/idc_list.html', - locals(), - context_instance=RequestContext(request)) + return my_render('jasset/idc_list.html', locals(), request) @require_role('admin') diff --git a/jumpserver/settings.py b/jumpserver/settings.py index be8464d38..fa8431272 100644 --- a/jumpserver/settings.py +++ b/jumpserver/settings.py @@ -66,6 +66,7 @@ INSTALLED_APPS = ( 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.humanize', + 'django_crontab', 'bootstrapform', 'jumpserver', 'juser', @@ -149,3 +150,7 @@ USE_TZ = False STATIC_URL = '/static/' BOOTSTRAP_COLUMN_COUNT = 10 + +CRONJOBS = [ + ('0 1 * * *', 'jasset.asset_api.asset_ansible_update_all') +] diff --git a/templates/jasset/asset_detail.html b/templates/jasset/asset_detail.html index 64b3e569f..bc38235ed 100644 --- a/templates/jasset/asset_detail.html +++ b/templates/jasset/asset_detail.html @@ -11,9 +11,15 @@