From cda8eb1232e88ae0e771e2d7fc3ca6676ea7d6e0 Mon Sep 17 00:00:00 2001 From: Antonio de la Rosa Date: Mon, 7 Feb 2022 00:35:21 +0100 Subject: [PATCH] First files to the project --- .gitignore | 108 +++++ __init__.py | 3 + admin/dashboard.py | 541 +++++++++++++++++++++++ admin/groups.py | 43 ++ app.py | 0 libraries/configtask.py | 83 ++++ libraries/task.py | 630 +++++++++++++++++++++++++++ media/css/jquery-ui.min.css | 7 + media/css/jquery-ui.theme.min.css | 5 + media/js/jquery-ui.min.js | 6 + models/pastafari2.py | 31 ++ models/tasks.py | 306 +++++++++++++ scripts/system/alive.sh | 10 + scripts/system/install_git.sh | 29 ++ scripts/system/install_psutil.sh | 19 + scripts/system/install_python.sh | 27 ++ scripts/system/install_pzoo_stats.py | 192 ++++++++ scripts/system/sudoers.d/spanel | 1 + settings/config_admin.py | 10 + tasks/system/ping.py | 14 + tasks/system/task.py | 38 ++ templates/admin/add_server.phtml | 42 ++ templates/admin/dash_pastafari.phtml | 135 ++++++ templates/admin/multiprogress.phtml | 104 +++++ templates/admin/progress.phtml | 224 ++++++++++ templates/admin/settings.phtml | 49 +++ 26 files changed, 2657 insertions(+) create mode 100644 .gitignore create mode 100644 __init__.py create mode 100644 admin/dashboard.py create mode 100644 admin/groups.py create mode 100644 app.py create mode 100644 libraries/configtask.py create mode 100644 libraries/task.py create mode 100644 media/css/jquery-ui.min.css create mode 100644 media/css/jquery-ui.theme.min.css create mode 100644 media/js/jquery-ui.min.js create mode 100644 models/pastafari2.py create mode 100644 models/tasks.py create mode 100644 scripts/system/alive.sh create mode 100644 scripts/system/install_git.sh create mode 100644 scripts/system/install_psutil.sh create mode 100644 scripts/system/install_python.sh create mode 100644 scripts/system/install_pzoo_stats.py create mode 100644 scripts/system/sudoers.d/spanel create mode 100644 settings/config_admin.py create mode 100644 tasks/system/ping.py create mode 100644 tasks/system/task.py create mode 100644 templates/admin/add_server.phtml create mode 100644 templates/admin/dash_pastafari.phtml create mode 100644 templates/admin/multiprogress.phtml create mode 100644 templates/admin/progress.phtml create mode 100644 templates/admin/settings.phtml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..17c45be --- /dev/null +++ b/.gitignore @@ -0,0 +1,108 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +.static_storage/ +.media/ +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +/backups +/settings/config.py +/media/js/jsutils diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..48c4ab3 --- /dev/null +++ b/__init__.py @@ -0,0 +1,3 @@ +from flask import Blueprint, g + +pastafari_app=Blueprint('pastafari_app', __name__) diff --git a/admin/dashboard.py b/admin/dashboard.py new file mode 100644 index 0000000..30e55d1 --- /dev/null +++ b/admin/dashboard.py @@ -0,0 +1,541 @@ +from settings import config +from flask import g, url_for, request, session, make_response +from paramecio2.libraries.generate_admin_class import GenerateAdminClass +from paramecio2.libraries.i18n import I18n +from paramecio2.modules.admin import admin_app, t as admin_t +from paramecio2.libraries.db.coreforms import SelectForm, SelectModelForm +from paramecio2.libraries.mtemplates import PTemplate, env_theme +from paramecio2.libraries import datetime +from paramecio2.libraries.urls import make_media_url +from modules.monit.models.monit import Server, ServerData, Alerts +from paramecio2.libraries.db.webmodel import WebModel +from paramecio2.libraries.lists import AjaxList +from paramecio2.libraries.db.extraforms.fileform import FileForm +from paramecio2.libraries.formsutils import show_form +from modules.pastafari2.libraries.task import Task as SSHTask +from modules.pastafari2.models.tasks import Task, LogTask +from modules.pastafari2.libraries.configtask import config_task +from modules.pastafari2.models.pastafari2 import ServerGroup +from pathlib import Path +import paramiko +import socket +import os +try: + import ujson as json +except: + import json + +env=env_theme(__file__) + +t=PTemplate(env) + +t.env.directories=admin_t.env.directories + +t.env.directories.insert(1, os.path.dirname(__file__).replace('/admin', '')+'/templates/admin') + +system_path='./ssh/' + +@admin_app.route('/pastafari2/dashboard/') +def pastafari2_dashboard(): + + return t.load_template('dash_pastafari.phtml', title=I18n.lang('pastafari2', 'servers_dashboard', 'Servers Dashboard'), path_module='admin_app.pastafari2_dashboard') + +@admin_app.route('/pastafari2/settings/') +def pastafari2_settings(): + """ + forms={} + + forms['ssh_key_priv']=FileForm('ssh_key_priv', '', '') + forms['ssh_key_pub']=FileForm('ssh_key_pub', '', '') + + forms['ssh_key_priv'].help=I18n.lang('pastafari2', 'ssh_key_priv_file_help', 'Global private SSH key used for enter in servers') + forms['ssh_key_pub'].help=I18n.lang('pastafari2', 'ssh_key_pub_file_help', 'Global public SSH key used for enter in servers') + + forms['ssh_key_priv'].label=I18n.lang('pastafari2', 'ssh_key_priv_file', 'Global private SSH') + forms['ssh_key_pub'].label=I18n.lang('pastafari2', 'ssh_key_pub_file', 'Global public SSH') + + forms['ssh_key_priv'].required=True + forms['ssh_key_pub'].required=True + + html_forms=show_form({}, forms, t, yes_error=False, pass_values=True, modelform_tpl='forms/modelform.phtml') + """ + + txt_error='' + txt_generate_key='

You have created your ssh keys

' + txt_generate_key_button='Regenerate SSH keys' + regenerate=True + + if not os.path.isdir(system_path): + + try: + Path(system_path).mkdir(mode=511) + + except: + + txt_error='

You need create ssh directory for save ssh keys

' + + #Get ssh key + + if not os.path.isfile(system_path+'id_rsa'): + + txt_generate_key='

You need generate a global private ssh key, because doesn\'t exists

' + txt_generate_key_button='Generate SSH keys' + regenerate=False + pass + + return t.load_template('settings.phtml', title=I18n.lang('pastafari2', 'settings', 'Settings'), path_module='admin_app.pastafari2_settings', txt_error=txt_error, txt_generate_key=txt_generate_key, txt_generate_key_button=txt_generate_key_button, regenerate=regenerate) + +@admin_app.route('/pastafari2/edit_global_ssh_keys/', methods=['POST']) +def pastafari2_edit_global_ssh_keys(): + + form={} + + error=0 + + if not os.path.isfile(system_path+'id_rsa'): + + try: + + key=paramiko.RSAKey.generate(2048) + key.write_private_key_file(system_path+'id_rsa') + + with open(system_path+'id_rsa.pub',"w") as pub_key: + pub_key.write("%s %s" % (key.get_name(), key.get_base64())) + + except: + + error=1 + + """ + if not 'ssh_key_priv_file' in request.files: + error=1 + form['ssh_key_priv']='ssh private key file required' + + if not 'ssh_key_pub_file' in request.files: + error=1 + form['ssh_key_pub']='ssh public key file required' + + if not error: + ssh_key_priv=request.files['ssh_key_priv_file'] + ssh_key_pub=request.files['ssh_key_pub_file'] + + if ssh_key_priv.filename=='': + error=1 + form['ssh_key_priv']='ssh private key file required' + + if ssh_key_pub.filename=='': + error=1 + form['ssh_key_pub']='ssh private key file required' + + if not os.path.isdir(system_path): + + try: + Path(system_path).mkdir(mode=511) + + except: + + error=1 + form['ssh_key_priv']='You need create ssh directory for save ssh keys' + + if not error: + + #Load keys + + #Check keys + + + + pass + """ + + #return {'form': form, 'error': error} + return {'error': error} + +@admin_app.route('/pastafari2/add_server/') +def pastafari2_add_server(): + + db=g.connection + + group_form=SelectModelForm('group_id', '', ServerGroup(db), 'group', 'id', field_parent=None) + + return t.load_template('add_server.phtml', title=I18n.lang('pastafari2', 'add_server', 'Add server'), path_module='admin_app.pastafari2_dashboard', group_form=group_form) + + +@admin_app.route('/pastafari2/add_server_task/', methods=['POST']) +def pastafari2_add_server_task(): + + db=g.connection + + #task=Task(db) + + #task.safe_query() + + #logtask=LogTask(db) + + #server=ServerTask(db) + #(self, server, conn, remote_user='root', remote_password='', private_key='./ssh/id_rsa', password_key='', remote_path='leviathan', task_id=0, data={}): + error_form={} + + server_host=request.form.get('server_host', '') + server_username=request.form.get('server_username', '') + server_password=request.form.get('server_password', '') + repeat_server_password=request.form.get('repeat_server_password', '') + group_id=request.form.get('group_id', '') + private_key='./ssh/id_rsa' + public_key='./ssh/id_rsa.pub' + remote_path='pastafari2' + task_id=0 + ip='' + error=0 + data={} + ssh_user=config_task.remote_user + + #make ping to server + + if server_host=='': + error=1 + error_form['#server_host_error']=I18n.lang('pastafari2', 'error_hostname', 'Error: you need enter a valid hostname') + + txt_error='' + + try: + #print("IP address of %s: %s" %(remote_host, socket.gethostbyname(remote_host))) + ip=socket.gethostbyname(server_host) + pass + except socket.error as err_msg: + error=1 + error_form['#server_host_error']=I18n.lang('pastafari2', 'error_getting_ip_host', 'Error: '+err_msg) + + if not error: + + group_name='' + + if group_id!='': + server_group=ServerGroup(db) + arr_group=server_group.set_conditions('WHERE id=%s', [group_id]).select_a_row_where() + group_name=arr_group['group'] + + data={'ssh_user': ssh_user, 'pub_key': public_key, 'url_stats': config.domain_url+url_for('monit_app.monit_get_data', api_key=config.monit_api_key), 'hostname': server_host, 'ip': ip, 'group_id': group_id, 'group_name': group_name} + + with SSHTask(server_host, db, remote_user=server_username, remote_password=server_password, private_key=private_key, remote_path=remote_path, task_id=task_id, data=data) as ssh_task: + if not ssh_task.prepare_connection(): + error=1 + txt_error=I18n.lang('pastafari2', 'error_connection', 'Error: cannot connect to server') + error_form['#server_host_error']=I18n.lang('pastafari2', 'error_connection', 'Error: cannot connect to server') + else: + # Prepare task for install monit + pass + + task=Task(db) + + task_id=0 + + path_task='modules.pastafari2.tasks.system.task' + + if not task.run_task(server_host, path_task, 'Add new server', 'add_new_server', 'Task for add a new server', user=server_username, password=server_password, where_sql_server='', url='', data=data, send_task=True): + + error=1 + error_form['#server_host_error']=I18n.lang('pastafari2', 'error_exec_task', 'Error: cannot execute the task') + + task_id=task.task_id + + return {'error': error, 'txt_error': txt_error, 'error_form': error_form, 'task_id': task_id} + +# Maybe translate to api + +@admin_app.route('/pastafari2/progress/') +def pastafari2_progress(): + + db=g.connection + + task_id=request.args.get('task_id', '0') + + position=request.args.get('position', '0') + + task=Task(db) + + arr_task=task.set_conditions('WHERE id=%s', [task_id]).select_a_row_where() + + #print(arr_task) + + return t.load_template('progress.phtml', title=I18n.lang('pastafari2', 'task_progress', 'Task progress'), path_module='admin_app.pastafari2_dashboard', name_task=arr_task['name_task'], description_task=arr_task['description_task'], position=position, task_id=task_id, server=arr_task['server']) + + + +@admin_app.route('/pastafari2/getprogress/', methods=['POST']) +def pastafari2_getprogress(): + + s=session + + db=g.connection + + if request.args.get('task_id', '0')=='0': + + s['get_progress']=s.get('get_progress', 0) + + try: + + task_id=int(request.args.get('task_id', '0')) + except: + task_id=0 + + try: + position=int(request.args.get('position', '0')) + except: + position=0 + + task=Task(db) + logtask=LogTask(db) + + arr_task=task.select_a_row(task_id) + + arr_rows={'wait': 1} + + if arr_task: + + logtask.set_limit([position, 5]) + + logtask.set_order({'id': 0}) + + logtask.conditions=['WHERE task_id=%s', [task_id]] + + server=request.args.get('server', '') + + if server!='': + logtask.conditions=['WHERE task_id=%s and logtask.server=%s', [task_id, server]] + + logtask.yes_reset_conditions=False + + c=0 + + arr_rows=[] + + with logtask.select([], True) as cursor: + for arr_row in cursor: + arr_rows.append(arr_row) + c+=1 + + if c==0: + arr_rows=[] + + c_error=logtask.set_conditions('WHERE task_id=%s and logtask.error=%s and logtask.status=%s', [task_id, 1, 1]).select_count() + + if c_error==0: + + arr_rows={'wait': 1} + else: + logtask.limit='' + + with logtask.set_conditions('WHERE task_id=%s and logtask.error=%s and logtask.status=%s', [task_id, 1, 1]).select([], True) as cursor: + for arr_row in cursor: + arr_rows.append(arr_row) + + else: + + arr_rows=[{'task_id': task_id, 'progress': 100, 'message': 'Error: no exists task', 'error': 1, 'status': 1}] + + db.close() + + #header=response.headers + + resp=make_response(json.dumps(arr_rows)) + + resp.headers['Content-Type']='application/json' + + return resp + +@admin_app.route('/pastafari2/get_servers/', methods=['POST']) +def get_servers_task(): + + db=g.connection + + group_sql='' + + count_data=[] + sql_data=[] + + group_id=request.form.get('group_id', '') + + group_sql_count='' + group_sql='' + + if group_id!='': + group_sql_count=' WHERE `group_id`=%s' + count_data=[group_id] + sql_data=[group_id] + group_sql=' WHERE `group_id`=%s' + + + + fields=[[I18n.lang('pastafari2', 'hostname', 'Hostname'), True], ['IP', True], [I18n.lang('pastafari2', 'selected', 'Selected'), False], [I18n.lang('pastafari2', 'options', 'Options'), False]] + arr_order_fields=['hostname', 'ip'] + + count_query=['select count(serverdbtask.id) as num_elements from serverdbtask'+group_sql_count, count_data] + + # server.id as select_id, + + # select hostname, ip, date, num_updates, id from serverofuser where user_id=%s; + + str_query=['select serverdbtask.hostname, serverdbtask.ip, serverdbtask.id as select_id, serverdbtask.id from serverdbtask'+group_sql, sql_data] + + ajax=AjaxList(db, fields, arr_order_fields, count_query, str_query) + + #ajax.func_fields['id']=options_server + #ajax.func_fields['ip']=options_ip + ajax.func_fields['select_id']=options_selected + ajax.func_fields['id']=options_options + ajax.limit=0 + + #{'fields': [['Hostname', True], ['IP', True], ['Status', True], ['Options', False]], 'rows': [{'hostname': 'debian-pc.localdomain', 'ip': '192.168.122.125', 'date': '', 'id': 'View stats'}, {'hostname': 'DESKTOP-HLHPSSO', 'ip': '192.168.122.81', 'date': '', 'id': 'View stats'}], 'html_pages': ''} + + return ajax.show() + +""" +def options_server(row_id, row): + + #'{}'.format(url_for('.services', server_id=row_id), I18n.lang('monit', 'services', 'Services')) + + arr_options=['{}'.format("", I18n.lang('pastafari2', 'edit', 'Edit'))] + arr_options.append('{}'.format("", I18n.lang('pastafari2', 'make_task', 'Make task'))) + arr_options.append('{}'.format("", I18n.lang('pastafari2', 'delete', 'Delete'))) +""" + +def options_selected(row_id, row): + + return ''.format(row_id, row_id) + +def options_options(row_id, row): + + arr_options=['{}'.format("", I18n.lang('pastafari2', 'edit', 'Edit'))] + #arr_options.append('{}'.format("", I18n.lang('pastafari2', 'make_task', 'Make task'))) + arr_options.append('{}'.format("", I18n.lang('pastafari2', 'delete', 'Delete'))) + + return '
'.join(arr_options) + + +@admin_app.route('/pastafari2/get_groups/', methods=['GET']) +def get_groups_task(): + + db=g.connection + + arr_groups=[] + + with db.query('select * from servergroup', []) as cursor: + for data in cursor: + arr_groups.append(data) + + return json.dumps(arr_groups) + + +@admin_app.route('/pastafari2/update_task/', methods=['POST']) +def pastafari2_update_task(): + + db=g.connection + + ids=json.loads(request.form.get('ids', '[]')) + arr_ids=[] + + error=0 + txt_error='' + error_form={} + + #print(ids) + for v in ids: + arr_ids.append(str(int(v))) + + where_sql='WHERE id IN ('+",".join(arr_ids)+') order by hostname ASC' + + #print(where_sql) + + task=Task(db) + + task_id=0 + + path_task='modules.pastafari2.tasks.system.ping' + + data={} + + server_host='' + + user=config_task.remote_user + + ssh_key_priv='./ssh/id_rsa' + + if not task.run_task(server_host, path_task, 'Update server', 'update_server', 'Task for update servers', user=user, password='', where_sql_server=where_sql, ssh_key_priv=ssh_key_priv, url='', data=data, send_task=True): + + error=1 + error_form['#server_host_error']=I18n.lang('pastafari2', 'error_exec_task', 'Error: cannot execute the task') + + task_id=task.task_id + + return {'error': error, 'txt_error': txt_error, 'error_form': error_form, 'task_id': task_id} + + return {} + +@admin_app.route('/pastafari2/multiprogress/') +def pastafari2_multiprogress(): + + db=g.connection + + task_id=request.args.get('task_id', '') + + task=Task(db) + + arr_task=task.select_a_row(task_id) + + num_servers=task.set_conditions('WHERE parent_id=%s', [task_id]).select_count() + + if arr_task: + + return t.load_template('multiprogress.phtml', title=I18n.lang('pastafari2', 'task_progress', 'Task progress'), path_module='admin_app.pastafari2_dashboard', name_task=arr_task['name_task'], description_task=arr_task['description_task'], task_id=task_id, server=arr_task['server'], num_servers=num_servers) + + else: + + return "" + +@admin_app.route('/pastafari2/get_servers_task/') +def pastafari2_get_servers_task(): + + db=g.connection + + task_id=request.args.get('task_id', '') + + task=Task(db) + + arr_ids=task.set_conditions('WHERE parent_id=%s', [task_id]).select_to_array(['id', 'hostname', 'server']) + + resp=make_response(json.dumps(arr_ids)) + + resp.headers['Content-Type']='application/json' + + return resp + +@admin_app.route('/pastafari2/get_multiprogress/') +def pastafari2_get_multiprogress(): + + s=session + + db=g.connection + + ids=request.args.get('ids', '[]') + + final_ids=[str(i) for i in json.loads(ids)] + + final_str=",".join(['%s']*len(final_ids)) + + #print(final_str) + #for i in final_ids: + + + task=Task(db) + + logtask=LogTask(db) + + arr_log=logtask.set_limit([20]).set_conditions('WHERE task_id IN ({})'.format(final_str), final_ids).select_to_array([], True) + + resp=make_response(json.dumps(arr_log)) + + resp.headers['Content-Type']='application/json' + + return resp + diff --git a/admin/groups.py b/admin/groups.py new file mode 100644 index 0000000..c98262f --- /dev/null +++ b/admin/groups.py @@ -0,0 +1,43 @@ +from settings import config +from flask import g, url_for, request, session, make_response +from paramecio2.libraries.generate_admin_class import GenerateAdminClass +from paramecio2.libraries.i18n import I18n +from paramecio2.modules.admin import admin_app, t as admin_t +from paramecio2.libraries.db.coreforms import SelectForm +from paramecio2.libraries.mtemplates import PTemplate, env_theme +from paramecio2.libraries import datetime +from paramecio2.libraries.urls import make_media_url +from paramecio2.libraries.db.webmodel import WebModel +from paramecio2.libraries.lists import AjaxList +from paramecio2.libraries.db.extraforms.fileform import FileForm +from paramecio2.libraries.formsutils import show_form +from modules.pastafari2.libraries.task import Task as SSHTask +from modules.pastafari2.libraries.configtask import config_task +from modules.pastafari2.models.tasks import Task, LogTask +from modules.pastafari2.models.pastafari2 import ServerGroup + +@admin_app.route('/pastafari2/groups/', methods=['GET', 'POST']) +def pastafari2_groups(): + + t=admin_t + + db=g.connection + + groups=ServerGroup(db) + + url=url_for('admin_app.pastafari2_groups') + + admin=GenerateAdminClass(groups, url, t) + + admin.list.raw_query=False + + form_admin=admin.show() + + if type(form_admin).__name__=='str': + + return t.load_template('content.phtml', title=I18n.lang('admin', 'groups_edit', 'Groups edit'), contents=form_admin, path_module='admin_app.pastafari2_groups') + else: + + return form_admin + + return "" diff --git a/app.py b/app.py new file mode 100644 index 0000000..e69de29 diff --git a/libraries/configtask.py b/libraries/configtask.py new file mode 100644 index 0000000..776f73e --- /dev/null +++ b/libraries/configtask.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 + +import os + +class ConfigTask: + + def __init__(self): + + #Local paths + + # Scheme: code task: {description task, module task} + + self.libraries={} + + self.base_path=os.path.dirname(os.path.dirname(__file__)) + + self.tasks_path=['tasks', 'modules/pastafari2/tasks'] + + self.scripts_path=['scripts', 'modules/pastafari2/scripts'] + + self.logs_path='logs' + + #Remote paths + + self.remote_path='/home/spanel' + + #Relative to remote_path + + self.tmp_sftp_path='tmp' + + self.remote_user='spanel' + + self.remote_password=None + + #Local home + + self.home='' + + #SSH configuration + + self.user_key='' + + self.public_key='' + + self.private_key='' + + self.password_key=None + + self.port=22 + + self.deny_missing_host_key=True + + #Internal tasks + + self.num_of_forks=10 + + self.stop_if_error=False + + self.num_errors=0 + + self.num_success=0 + + self.num_total=0 + + self.num_servers=0 + + self.file_resume=None + + #Used if in next versions, pastafari try use others tasks servers. + + self.server_task='http://127.0.0.1:1337' + + self.url_host='' + + self.url_monit='' + + self.api_key='' + + self.ssh_directory=os.path.expanduser('~')+'/.ssh' + +# Class for global configuration + +config_task=ConfigTask() diff --git a/libraries/task.py b/libraries/task.py new file mode 100644 index 0000000..eb7dd37 --- /dev/null +++ b/libraries/task.py @@ -0,0 +1,630 @@ +#!/usr/bin/env python3 + +import paramiko, json +import os, sys,traceback +from stat import S_ISDIR +from modules.pastafari2.models import tasks +#from modules.pastafari2.models.servers import ServerGroupTask +from modules.pastafari2.libraries.configtask import config_task +from paramecio2.libraries.i18n import I18n +from paramecio2.libraries.db.webmodel import WebModel + +class Task: + + #($server='', $ssh_user='root', $ssh_key_priv='./ssh/id_rsa', $ssh_key_password='', $ssh_path='leviathan', $mysql_conn=false) + + def __init__(self, server, conn, remote_user='root', remote_password='', private_key='./ssh/id_rsa', password_key='', remote_path='leviathan', task_id=0, data={}): + + self.config=config_task + + self.server=server + + self.name_task='' + + self.codename_task='' + + self.description_task='' + + self.txt_error='' + + self.os_server='' + + self.files=[] + + # Format first array element is command with the interpreter, the task is agnostic, the files in os directory. The commands are setted with 750 permission. + # First element is the file, next elements are the arguments + + self.commands_to_execute=[]; + + #THe files to delete + + self.delete_files=[]; + + self.delete_directories=[]; + + #The id of the task in db + + self.id=task_id + + self.user='' + self.password='' + + self.connection=conn + + self.logtask=tasks.LogTask(self.connection) + self.task=tasks.Task(self.connection) + self.taskdone=tasks.TaskDone(self.connection) + + self.ssh=paramiko.SSHClient() + + self.logtask.reset_require() + self.task.reset_require() + + self.one_time=False + + self.version='1.0' + + self.simultaneous=False + + self.error_post_task='' + + self.data=data + + self.remote_user=remote_user + + self.remote_password=remote_password + + self.private_key=private_key + + self.password_key=password_key + + self.remote_path=remote_path + + """ + self.pre_task=None + + self.error_task=None + + self.post_task=None + """ + + def prepare_connection(self): + + #self.ssh.load_system_host_keys() + + #Check if the unknown host keys are rejected or not + + #if self.config.deny_missing_host_key == False: + + #self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + # Check if in known_hosts + check_ssh_host= paramiko.hostkeys.HostKeys() + + if not os.path.isfile(self.config.ssh_directory+'/known_hosts'): + f=open(self.config.ssh_directory+'/known_hosts', 'w') + f.write('') + f.close() + + self.ssh.load_system_host_keys(self.config.ssh_directory+'/known_hosts') + + check_ssh_host.load(self.config.ssh_directory+'/known_hosts') + + host_key=self.ssh.get_host_keys() + + add_host=False + + rsa=None + if self.private_key!='': + rsa=paramiko.RSAKey.from_private_key_file(self.private_key, self.password_key) + + if check_ssh_host.lookup(self.server)==None: + + # Be tolerant for the first connect with hostkey policy + + self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + add_host=True + + try: + + self.ssh.connect(self.server, port=self.config.port, username=self.remote_user, password=self.remote_password, pkey=rsa, key_filename=None, timeout=None, allow_agent=True, look_for_keys=True, compress=False, sock=None, gss_auth=False, gss_kex=False, gss_deleg_creds=True, gss_host=None, banner_timeout=None) + + if add_host: + host_key.save(self.config.ssh_directory+'/known_hosts') + + except paramiko.SSHException as e: + self.txt_error="Error: cannot connect to the server SSHException\n"+str(e) + return False + + except paramiko.AuthenticationException as e: + self.txt_error="Error: cannot connect to the server AuthenticationException \n"+str(e) + return False + + except paramiko.BadHostKeyException as e: + self.txt_error="Error: cannot connect to the server BadHostKeyException\n"+str(e) + return False + + except OSError as e: + self.txt_error="Error: cannot connect to the server OsError \n"+str(e) + return False + + except: + self.txt_error="Error: cannot connect to the server Generic\n"+traceback.format_exc() + return False + + #finally: + # self.ssh.close() + + return True + + + def upload_files(self): + + try: + + sftp=self.ssh.open_sftp() + + except: + + self.txt_error='Sorry, error connecting to sftp: '+traceback.format_exc() + return False + + c=len(self.files) + + if c>0: + + percent=100/c + + progress=0 + + for f in self.files: + + source_file=f[0] + + source_file=source_file.replace('${os_server}', self.os_server) + + permissions=f[1] + #dest_file=self.remote_path+'/'+source_file + dest_file=source_file + try: + + if not os.path.isfile(source_file): + self.txt_error="Sorry, you don't have source file to upload "+source_file + return False + + dir_file=os.path.dirname(dest_file) + + parts_dir_file=dir_file.split('/') + + # Create remote directory + + try: + + f_stat=sftp.stat(dir_file) + + except IOError: + + try: + + final_path='' + + for d in parts_dir_file: + + final_path+=d+'/' + + #print(self.remote_path+'/'+final_path) + + try: + + f_stat=sftp.stat(final_path) + + except IOError: + + sftp.mkdir(final_path) + + except: + + self.txt_error='Sorry, error creating the directories for the files: '+traceback.format_exc() + return False + + # Upload file + + try: + + sftp.put(source_file, dest_file, callback=None, confirm=True) + + sftp.chmod(dest_file, permissions) + + progress+=percent + + self.logtask.insert({'task_id': self.id, 'progress': progress, 'message': I18n.lang('pastafari', 'uploading_files', 'Uploading file: ')+source_file, 'error': 0, 'server': self.server}) + + except: + + self.txt_error='Sorry, cannot upload file '+source_file+': '+traceback.format_exc() + return False + + # Create directory recursively if not exists + + except: + self.txt_error='Error: '+traceback.format_exc() + return False + + self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': I18n.lang('pastafari', 'upload_successful', 'Files uploaded successfully...'), 'error': 0, 'server': self.server}) + + return True + + def delete_files_and_dirs(self): + + sftp=self.ssh.open_sftp() + + c=len(self.delete_files) + + if c>0: + + percent=100/c + + progress=0 + + for filepath in self.delete_files: + + filepath=filepath.replace('${os_server}', self.os_server) + + try: + + #print(self.remote_path+'/'+filepath) + + sftp.remove(filepath) + + progress+=percent + + self.logtask.insert({'task_id': self.id, 'progress': progress, 'message': I18n.lang('pastafari', 'cleaning_files', 'Cleaning file: ')+filepath, 'error': 0, 'server': self.server}) + + except IOError: + + self.txt_error="Sorry, cannot remove file "+filepath+" from server." + + return False + + + c=len(self.delete_directories) + + if c>0: + + percent=100/c + + progress=0 + + for path in self.delete_directories: + + try: + + path=path.replace('${os_server}', self.os_server) + + self.delete_dir(path, sftp) + + progress+=percent + + self.logtask.insert({'task_id': self.id, 'progress': progress, 'message': I18n.lang('pastafari', 'cleaning_directories', 'Cleaning directory: ')+path, 'error': 0, 'server': self.server}) + + + except IOError: + + self.txt_error+="Sorry, cannot remove directory "+path+" from server." + + return False + + + return True + + + def isdir(self, path, sftp): + try: + return S_ISDIR(sftp.stat(path).st_mode) + except IOError: + return False + + def delete_dir(self, path, sftp): + + #path=self.config.remote_path+'/'+path + + if path != "/": + + files = sftp.listdir(path) + + for f in files: + filepath = os.path.join(path, f) + try: + if self.isdir(filepath, sftp): + self.delete_dir(filepath, sftp) + else: + sftp.remove(filepath) + except IOError: + self.txt_error="Sorry, cannot remove "+filepath+" from server." + return False + + sftp.rmdir(path) + + return True + + def exec(self): + + # Get task + + #self.id=task_id + + self.task.reset_require() + + self.task.valid_fields=['name_task', 'description_task', 'error', 'status', 'server'] + + self.logtask.valid_fields=self.logtask.fields.keys() + + if self.id==0: + + # Insert the task + + self.task.reset_require() + + self.task.insert({'name_task': self.name_task, 'description_task': self.description_task, 'server': self.server}) + + self.id=self.task.insert_id() + + if not self.prepare_connection(): + #self.task.conditions=['WHERE id=%s', [self.id]] + #self.task.update({'error': 1, 'status': 1}) + + self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': self.txt_error, 'error': 1, 'status': 1, 'server': self.server}) + + self.make_error_task() + + return False + + # Pre task + + if hasattr(self, 'pre_task'): + self.logtask.insert({'task_id': self.id, 'progress': 0, 'message': I18n.lang('pastafari', 'pre_tasks', 'Begin pre tasks execution...'), 'error': 0, 'status': 0, 'server': self.server}) + + if self.pre_task(): + self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': I18n.lang('pastafari', 'pre_tasks_executed', 'Pre tasks executed successfully...'), 'error': 0, 'status': 0, 'server': self.server}) + else: + #self.logtask.set_conditions('where id=%s', [last_log_id]) + + self.logtask.insert({'progress': 100, 'error': 1, 'message': "Error executing pre task "+self.txt_error, 'status': 1, 'server': self.server, 'task_id': self.id}) + + self.make_error_task() + + return False + + #Check if script was executed + + if self.codename_task!='': + + if self.one_time==True: + + with self.ssh.open_sftp() as sftp: + + try: + + with sftp.file(self.remote_path+'/tasks/'+self.codename_task) as f: + version=f.read() + version=version.decode('utf-8').strip() + + if version==self.version: + #self.task.conditions=['WHERE id=%s', [self.id]] + #self.task.update({'error': 0, 'status': 1}) + + self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': 'This script was executed correctly in this server', 'error': 0, 'status': 1, 'server': self.server}) + + return True + + except IOError: + # It was not executed + pass + + if not self.upload_files(): + #self.task.conditions=['WHERE id=%s', [self.id]] + #self.task.update({'error': 1, 'status': 1}) + + self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': self.txt_error, 'error': 1, 'status': 1, 'server': self.server}) + + self.make_error_task() + + return False + + self.logtask.insert({'task_id': self.id, 'progress': 0, 'message': 'Executing commands...', 'error': 0, 'status': 0, 'server': self.server}) + + # Execute commands + + json_code=[] + + for c in self.commands_to_execute: + + try: + command=c[0] + + command=command.replace('${os_server}', self.os_server) + + arguments=c[1] + + sudo_str='' + + if len(c)==3: + sudo_str='sudo ' + + #, get_pty=True + stdin, stdout, stderr = self.ssh.exec_command(sudo_str+command+' '+arguments) + + for line in stdout: + + if line==None: + + line="[]" + + line=line.strip() + + try: + json_code=json.loads(line) + + if not 'progress' in json_code or not 'message' in json_code or not 'error' in json_code: + #self.task.conditions=['WHERE id=%s', [self.id]] + #self.task.update({'error': 1, 'status': 1}) + + self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': 'Malformed json code: '+str(line), 'error': 1, 'status': 1, 'server': self.server}) + self.make_error_task() + return False + + else: + + json_code['task_id']=self.id + + json_code['server']=self.server + + self.logtask.insert(json_code) + + if json_code['error']==1: + self.make_error_task() + return False + + + + except: + + #self.task.conditions=['WHERE id=%s', [self.id]] + #self.task.update({'error': 0, 'status':0}) + + self.logtask.insert({'task_id': self.id, 'progress': 0, 'no_progress': 1, 'message': str(line), 'error': 0, 'status': 0, 'server': self.server}) + + #return False + + last_log_id=self.logtask.insert_id() + + if stdout.channel.recv_exit_status()>0: + #line=stdout.readlines() + #logging.warning(action.codename+" WARNING: "+line) + final_text='Error executing the command: %s' % command + + #self.task.conditions=['WHERE id=%s', [self.id]] + #self.task.update({'error': 1, 'status': 1}) + + for line in stdout: + final_text+=' '+line + + for line in stderr: + final_text+=' '+line + + self.logtask.set_conditions('where id=%s', [last_log_id]) + + self.logtask.update({'progress': 100, 'error': 1, 'message': final_text, 'status': 1, 'server': self.server}) + self.make_error_task() + return False + + except: + + #self.task.conditions=['WHERE id=%s', [self.id]] + #self.task.update({'error': 1, 'status': 1}) + + self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': traceback.format_exc(), 'error': 1, 'status': 1, 'server': self.server}) + self.make_error_task() + return False + + # Clean files + + if not self.delete_files_and_dirs(): + + #self.task.conditions=['WHERE id=%s', [self.id]] + #self.task.update({'error': 1, 'status': 1}) + + self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': self.txt_error, 'error': 1, 'status': 1, 'server': self.server}) + self.make_error_task() + return False + + #Upload files + + #self.ssh.close() + + # FInish task + + #Put this version how executed + + if self.codename_task!='': + + if self.one_time==True: + + with self.ssh.open_sftp() as sftp: + + try: + path_check=self.remote_path+'/tasks/' + + f_stat=sftp.stat(path_check) + + except IOError: + + sftp.mkdir(path_check) + + with sftp.file(path_check+self.codename_task, 'w') as f: + f.write(self.version) + + if hasattr(self, 'post_task'): + self.logtask.insert({'task_id': self.id, 'progress': 0, 'message': I18n.lang('pastafari', 'post_tasks', 'Post tasks executing...'), 'error': 0, 'status': 0, 'server': self.server}) + + if self.post_task(): + self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': I18n.lang('pastafari', 'post_tasks_executed', 'Post tasks executed successfully...'), 'error': 0, 'status': 0, 'server': self.server}) + else: + #self.logtask.set_conditions('where id=%s', [last_log_id]) + + #self.logtask.update({'progress': 100, 'error': 1, 'message': "Error executing post task", 'status': 1, 'server': self.server}) + + self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': I18n.lang('pastafari', 'error_post_tasks_executed', 'Error executing post task -> '+self.error_post_task), 'error': 1, 'status': 1, 'server': self.server}) + self.make_error_task() + return False + + + if 'progress' in json_code: + #if json_code['progress']!=100: + self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': I18n.lang('pastafari', 'finished_successfully', 'All tasks done successfully...'), 'error': 0, 'status': 1, 'server': self.server}) + else: + self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': I18n.lang('pastafari', 'finished_successfully', 'All tasks done successfully...'), 'error': 0, 'status': 1, 'server': self.server}) + + # Add + self.taskdone.create_forms() + + self.taskdone.insert({'name_task': self.codename_task, 'ip': self.server}) + + #self.task.conditions=['WHERE id=%s', [self.id]] + #self.task.update({'error': 0, 'status': 1}) + + #connection.close() + return True + + def make_error_task(self): + + if hasattr(self, 'error_task'): + self.logtask.insert({'task_id': self.id, 'progress': 0, 'message': I18n.lang('pastafari', 'error_tasks', 'Error tasks executing...'), 'error': 0, 'status': 0, 'server': self.server}) + + if self.error_task(): + self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': I18n.lang('pastafari', 'error_tasks_executed', 'Sorry, you have an error, please, review the log...'), 'error': 1, 'status': 1, 'server': self.server}) + else: + self.logtask.insert({'task_id': self.id, 'progress': 100, 'message': I18n.lang('pastafari', 'error_tasks_executed', 'Error Post task cannot be executed...'), 'error': 1, 'status': 1, 'server': self.server}) + + return False + + def __del__(self): + + #self.connection.close() + + if self.ssh!=None: + self.ssh.close() + + def __enter__(self): + + return self + + def __exit__(self, exc_type, exc_value, tb): + + if exc_type is not None: + traceback.print_exception(exc_type, exc_value, tb) + + return True + + + diff --git a/media/css/jquery-ui.min.css b/media/css/jquery-ui.min.css new file mode 100644 index 0000000..34414b6 --- /dev/null +++ b/media/css/jquery-ui.min.css @@ -0,0 +1,7 @@ +/*! jQuery UI - v1.11.4 - 2016-05-07 +* http://jqueryui.com +* Includes: core.css, progressbar.css, theme.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=5px&bgColorHeader=2191c0&bgTextureHeader=gloss_wave&bgImgOpacityHeader=75&borderColorHeader=4297d7&fcHeader=eaf5f7&iconColorHeader=d8e7f3&bgColorContent=fcfdfd&bgTextureContent=inset_hard&bgImgOpacityContent=100&borderColorContent=a6c9e2&fcContent=222222&iconColorContent=0078ae&bgColorDefault=0078ae&bgTextureDefault=glass&bgImgOpacityDefault=45&borderColorDefault=77d5f7&fcDefault=ffffff&iconColorDefault=e0fdff&bgColorHover=79c9ec&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=448dae&fcHover=026890&iconColorHover=056b93&bgColorActive=6eac2c&bgTextureActive=gloss_wave&bgImgOpacityActive=50&borderColorActive=acdd4a&fcActive=ffffff&iconColorActive=f5e175&bgColorHighlight=f8da4e&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcd113&fcHighlight=915608&iconColorHighlight=f7a50d&bgColorError=e14f1c&bgTextureError=gloss_wave&bgImgOpacityError=45&borderColorError=cd0a0a&fcError=ffffff&iconColorError=fcd113&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=75&opacityOverlay=30&bgColorShadow=999999&bgTextureShadow=flat&bgImgOpacityShadow=55&opacityShadow=45&thicknessShadow=0px&offsetTopShadow=5px&offsetLeftShadow=5px&cornerRadiusShadow=5px +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");height:100%;filter:alpha(opacity=25);opacity:0.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-widget{font-family:Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #a6c9e2;background:#fcfdfd url("images/ui-bg_inset-hard_100_fcfdfd_1x100.png") 50% bottom repeat-x;color:#222}.ui-widget-content a{color:#222}.ui-widget-header{border:1px solid #4297d7;background:#2191c0 url("images/ui-bg_gloss-wave_75_2191c0_500x100.png") 50% 50% repeat-x;color:#eaf5f7;font-weight:bold}.ui-widget-header a{color:#eaf5f7}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #77d5f7;background:#0078ae url("images/ui-bg_glass_45_0078ae_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#fff}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#fff;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #448dae;background:#79c9ec url("images/ui-bg_glass_75_79c9ec_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#026890}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited{color:#026890;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #acdd4a;background:#6eac2c url("images/ui-bg_gloss-wave_50_6eac2c_500x100.png") 50% 50% repeat-x;font-weight:normal;color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fcd113;background:#f8da4e url("images/ui-bg_glass_55_f8da4e_1x400.png") 50% 50% repeat-x;color:#915608}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#915608}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#e14f1c url("images/ui-bg_gloss-wave_45_e14f1c_500x100.png") 50% top repeat-x;color:#fff}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#fff}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#fff}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_0078ae_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_d8e7f3_256x240.png")}.ui-state-default .ui-icon{background-image:url("images/ui-icons_e0fdff_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url("images/ui-icons_056b93_256x240.png")}.ui-state-active .ui-icon{background-image:url("images/ui-icons_f5e175_256x240.png")}.ui-state-highlight .ui-icon{background-image:url("images/ui-icons_f7a50d_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_fcd113_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:5px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:5px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:5px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:5px}.ui-widget-overlay{background:#aaa;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{margin:5px 0 0 5px;padding:0;background:#999;opacity:.45;filter:Alpha(Opacity=45);border-radius:5px} \ No newline at end of file diff --git a/media/css/jquery-ui.theme.min.css b/media/css/jquery-ui.theme.min.css new file mode 100644 index 0000000..deba0ad --- /dev/null +++ b/media/css/jquery-ui.theme.min.css @@ -0,0 +1,5 @@ +/*! jQuery UI - v1.11.4 - 2016-05-07 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +.ui-widget{font-family:Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #a6c9e2;background:#fcfdfd url("images/ui-bg_inset-hard_100_fcfdfd_1x100.png") 50% bottom repeat-x;color:#222}.ui-widget-content a{color:#222}.ui-widget-header{border:1px solid #4297d7;background:#2191c0 url("images/ui-bg_gloss-wave_75_2191c0_500x100.png") 50% 50% repeat-x;color:#eaf5f7;font-weight:bold}.ui-widget-header a{color:#eaf5f7}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #77d5f7;background:#0078ae url("images/ui-bg_glass_45_0078ae_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#fff}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#fff;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #448dae;background:#79c9ec url("images/ui-bg_glass_75_79c9ec_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#026890}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited{color:#026890;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #acdd4a;background:#6eac2c url("images/ui-bg_gloss-wave_50_6eac2c_500x100.png") 50% 50% repeat-x;font-weight:normal;color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fcd113;background:#f8da4e url("images/ui-bg_glass_55_f8da4e_1x400.png") 50% 50% repeat-x;color:#915608}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#915608}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#e14f1c url("images/ui-bg_gloss-wave_45_e14f1c_500x100.png") 50% top repeat-x;color:#fff}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#fff}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#fff}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_0078ae_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_d8e7f3_256x240.png")}.ui-state-default .ui-icon{background-image:url("images/ui-icons_e0fdff_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url("images/ui-icons_056b93_256x240.png")}.ui-state-active .ui-icon{background-image:url("images/ui-icons_f5e175_256x240.png")}.ui-state-highlight .ui-icon{background-image:url("images/ui-icons_f7a50d_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_fcd113_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:5px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:5px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:5px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:5px}.ui-widget-overlay{background:#aaa;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{margin:5px 0 0 5px;padding:0;background:#999;opacity:.45;filter:Alpha(Opacity=45);border-radius:5px} \ No newline at end of file diff --git a/media/js/jquery-ui.min.js b/media/js/jquery-ui.min.js new file mode 100644 index 0000000..bdee536 --- /dev/null +++ b/media/js/jquery-ui.min.js @@ -0,0 +1,6 @@ +/*! jQuery UI - v1.11.4 - 2016-05-07 +* http://jqueryui.com +* Includes: core.js, widget.js, progressbar.js +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +(function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)})(function(t){function e(e,s){var n,a,o,r=e.nodeName.toLowerCase();return"area"===r?(n=e.parentNode,a=n.name,e.href&&a&&"map"===n.nodeName.toLowerCase()?(o=t("img[usemap='#"+a+"']")[0],!!o&&i(o)):!1):(/^(input|select|textarea|button|object)$/.test(r)?!e.disabled:"a"===r?e.href||s:s)&&i(e)}function i(e){return t.expr.filters.visible(e)&&!t(e).parents().addBack().filter(function(){return"hidden"===t.css(this,"visibility")}).length}t.ui=t.ui||{},t.extend(t.ui,{version:"1.11.4",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),t.fn.extend({scrollParent:function(e){var i=this.css("position"),s="absolute"===i,n=e?/(auto|scroll|hidden)/:/(auto|scroll)/,a=this.parents().filter(function(){var e=t(this);return s&&"static"===e.css("position")?!1:n.test(e.css("overflow")+e.css("overflow-y")+e.css("overflow-x"))}).eq(0);return"fixed"!==i&&a.length?a:t(this[0].ownerDocument||document)},uniqueId:function(){var t=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++t)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&t(this).removeAttr("id")})}}),t.extend(t.expr[":"],{data:t.expr.createPseudo?t.expr.createPseudo(function(e){return function(i){return!!t.data(i,e)}}):function(e,i,s){return!!t.data(e,s[3])},focusable:function(i){return e(i,!isNaN(t.attr(i,"tabindex")))},tabbable:function(i){var s=t.attr(i,"tabindex"),n=isNaN(s);return(n||s>=0)&&e(i,!n)}}),t("").outerWidth(1).jquery||t.each(["Width","Height"],function(e,i){function s(e,i,s,a){return t.each(n,function(){i-=parseFloat(t.css(e,"padding"+this))||0,s&&(i-=parseFloat(t.css(e,"border"+this+"Width"))||0),a&&(i-=parseFloat(t.css(e,"margin"+this))||0)}),i}var n="Width"===i?["Left","Right"]:["Top","Bottom"],a=i.toLowerCase(),o={innerWidth:t.fn.innerWidth,innerHeight:t.fn.innerHeight,outerWidth:t.fn.outerWidth,outerHeight:t.fn.outerHeight};t.fn["inner"+i]=function(e){return void 0===e?o["inner"+i].call(this):this.each(function(){t(this).css(a,s(this,e)+"px")})},t.fn["outer"+i]=function(e,n){return"number"!=typeof e?o["outer"+i].call(this,e):this.each(function(){t(this).css(a,s(this,e,!0,n)+"px")})}}),t.fn.addBack||(t.fn.addBack=function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}),t("").data("a-b","a").removeData("a-b").data("a-b")&&(t.fn.removeData=function(e){return function(i){return arguments.length?e.call(this,t.camelCase(i)):e.call(this)}}(t.fn.removeData)),t.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),t.fn.extend({focus:function(e){return function(i,s){return"number"==typeof i?this.each(function(){var e=this;setTimeout(function(){t(e).focus(),s&&s.call(e)},i)}):e.apply(this,arguments)}}(t.fn.focus),disableSelection:function(){var t="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.bind(t+".ui-disableSelection",function(t){t.preventDefault()})}}(),enableSelection:function(){return this.unbind(".ui-disableSelection")},zIndex:function(e){if(void 0!==e)return this.css("zIndex",e);if(this.length)for(var i,s,n=t(this[0]);n.length&&n[0]!==document;){if(i=n.css("position"),("absolute"===i||"relative"===i||"fixed"===i)&&(s=parseInt(n.css("zIndex"),10),!isNaN(s)&&0!==s))return s;n=n.parent()}return 0}}),t.ui.plugin={add:function(e,i,s){var n,a=t.ui[e].prototype;for(n in s)a.plugins[n]=a.plugins[n]||[],a.plugins[n].push([i,s[n]])},call:function(t,e,i,s){var n,a=t.plugins[e];if(a&&(s||t.element[0].parentNode&&11!==t.element[0].parentNode.nodeType))for(n=0;a.length>n;n++)t.options[a[n][0]]&&a[n][1].apply(t.element,i)}};var s=0,n=Array.prototype.slice;t.cleanData=function(e){return function(i){var s,n,a;for(a=0;null!=(n=i[a]);a++)try{s=t._data(n,"events"),s&&s.remove&&t(n).triggerHandler("remove")}catch(o){}e(i)}}(t.cleanData),t.widget=function(e,i,s){var n,a,o,r,h={},l=e.split(".")[0];return e=e.split(".")[1],n=l+"-"+e,s||(s=i,i=t.Widget),t.expr[":"][n.toLowerCase()]=function(e){return!!t.data(e,n)},t[l]=t[l]||{},a=t[l][e],o=t[l][e]=function(t,e){return this._createWidget?(arguments.length&&this._createWidget(t,e),void 0):new o(t,e)},t.extend(o,a,{version:s.version,_proto:t.extend({},s),_childConstructors:[]}),r=new i,r.options=t.widget.extend({},r.options),t.each(s,function(e,s){return t.isFunction(s)?(h[e]=function(){var t=function(){return i.prototype[e].apply(this,arguments)},n=function(t){return i.prototype[e].apply(this,t)};return function(){var e,i=this._super,a=this._superApply;return this._super=t,this._superApply=n,e=s.apply(this,arguments),this._super=i,this._superApply=a,e}}(),void 0):(h[e]=s,void 0)}),o.prototype=t.widget.extend(r,{widgetEventPrefix:a?r.widgetEventPrefix||e:e},h,{constructor:o,namespace:l,widgetName:e,widgetFullName:n}),a?(t.each(a._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete a._childConstructors):i._childConstructors.push(o),t.widget.bridge(e,o),o},t.widget.extend=function(e){for(var i,s,a=n.call(arguments,1),o=0,r=a.length;r>o;o++)for(i in a[o])s=a[o][i],a[o].hasOwnProperty(i)&&void 0!==s&&(e[i]=t.isPlainObject(s)?t.isPlainObject(e[i])?t.widget.extend({},e[i],s):t.widget.extend({},s):s);return e},t.widget.bridge=function(e,i){var s=i.prototype.widgetFullName||e;t.fn[e]=function(a){var o="string"==typeof a,r=n.call(arguments,1),h=this;return o?this.each(function(){var i,n=t.data(this,s);return"instance"===a?(h=n,!1):n?t.isFunction(n[a])&&"_"!==a.charAt(0)?(i=n[a].apply(n,r),i!==n&&void 0!==i?(h=i&&i.jquery?h.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+a+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; "+"attempted to call method '"+a+"'")}):(r.length&&(a=t.widget.extend.apply(null,[a].concat(r))),this.each(function(){var e=t.data(this,s);e?(e.option(a||{}),e._init&&e._init()):t.data(this,s,new i(a,this))})),h}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
",options:{disabled:!1,create:null},_createWidget:function(e,i){i=t(i||this.defaultElement||this)[0],this.element=t(i),this.uuid=s++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),i!==this&&(t.data(i,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===i&&this.destroy()}}),this.document=t(i.style?i.ownerDocument:i.document||i),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),e),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:t.noop,_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetFullName).removeData(t.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,a,o=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(o={},s=e.split("."),e=s.shift(),s.length){for(n=o[e]=t.widget.extend({},this.options[e]),a=0;s.length-1>a;a++)n[s[a]]=n[s[a]]||{},n=n[s[a]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];o[e]=i}return this._setOptions(o),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return this.options[t]=e,"disabled"===t&&(this.widget().toggleClass(this.widgetFullName+"-disabled",!!e),e&&(this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus"))),this},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_on:function(e,i,s){var n,a=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,o){function r(){return e||a.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof o?a[o]:o).apply(a,arguments):void 0}"string"!=typeof o&&(r.guid=o.guid=o.guid||r.guid||t.guid++);var h=s.match(/^([\w:-]*)\s*(.*)$/),l=h[1]+a.eventNamespace,u=h[2];u?n.delegate(u,l,r):i.bind(l,r)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.unbind(i).undelegate(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){t(e.currentTarget).addClass("ui-state-hover")},mouseleave:function(e){t(e.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){t(e.currentTarget).addClass("ui-state-focus")},focusout:function(e){t(e.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(e,i,s){var n,a,o=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],a=i.originalEvent)for(n in a)n in i||(i[n]=a[n]);return this.element.trigger(i,s),!(t.isFunction(o)&&o.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,a){"string"==typeof n&&(n={effect:n});var o,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),o=!t.isEmptyObject(n),n.complete=a,n.delay&&s.delay(n.delay),o&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,a):s.queue(function(i){t(this)[e](),a&&a.call(s[0]),i()})}}),t.widget,t.widget("ui.progressbar",{version:"1.11.4",options:{max:100,value:0,change:null,complete:null},min:0,_create:function(){this.oldValue=this.options.value=this._constrainedValue(),this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min}),this.valueDiv=t("
").appendTo(this.element),this._refreshValue()},_destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.valueDiv.remove()},value:function(t){return void 0===t?this.options.value:(this.options.value=this._constrainedValue(t),this._refreshValue(),void 0)},_constrainedValue:function(t){return void 0===t&&(t=this.options.value),this.indeterminate=t===!1,"number"!=typeof t&&(t=0),this.indeterminate?!1:Math.min(this.options.max,Math.max(this.min,t))},_setOptions:function(t){var e=t.value;delete t.value,this._super(t),this.options.value=this._constrainedValue(e),this._refreshValue()},_setOption:function(t,e){"max"===t&&(e=Math.max(this.min,e)),"disabled"===t&&this.element.toggleClass("ui-state-disabled",!!e).attr("aria-disabled",e),this._super(t,e)},_percentage:function(){return this.indeterminate?100:100*(this.options.value-this.min)/(this.options.max-this.min)},_refreshValue:function(){var e=this.options.value,i=this._percentage();this.valueDiv.toggle(this.indeterminate||e>this.min).toggleClass("ui-corner-right",e===this.options.max).width(i.toFixed(0)+"%"),this.element.toggleClass("ui-progressbar-indeterminate",this.indeterminate),this.indeterminate?(this.element.removeAttr("aria-valuenow"),this.overlayDiv||(this.overlayDiv=t("
").appendTo(this.valueDiv))):(this.element.attr({"aria-valuemax":this.options.max,"aria-valuenow":e}),this.overlayDiv&&(this.overlayDiv.remove(),this.overlayDiv=null)),this.oldValue!==e&&(this.oldValue=e,this._trigger("change")),e===this.options.max&&this._trigger("complete")}})}); \ No newline at end of file diff --git a/models/pastafari2.py b/models/pastafari2.py new file mode 100644 index 0000000..d8fc4f8 --- /dev/null +++ b/models/pastafari2.py @@ -0,0 +1,31 @@ +from paramecio2.libraries.db.webmodel import WebModel +from paramecio2.libraries.db import corefields +from paramecio2.libraries.db.extrafields.dictfield import DictField +from paramecio2.libraries.db.extrafields.datefield import DateField +from paramecio2.libraries.db.extrafields.datetimefield import DateTimeField +from paramecio2.libraries.db.extrafields.ipfield import IpField +from paramecio2.libraries.db.extrafields.urlfield import UrlField +from paramecio2.libraries.db.extrafields.urlfield import DomainField +from paramecio2.libraries.db.extrafields.dictfield import DictField +from paramecio2.libraries.db.extrafields.jsonfield import JsonValueField +from paramecio2.libraries.db.extrafields.parentfield import ParentField +from paramecio2.libraries.urls import make_media_url +from paramecio2.libraries import datetime +from paramecio2.modules.admin.models.admin import UserAdmin +from modules.monit.models.monit import Server + +class ServerGroup(WebModel): + + def __init__(self, connection): + + super().__init__(connection) + self.register(corefields.CharField('group'), True) + self.register(corefields.CharField('code_group'), True) + + +class ServerDbTask(Server): + + def __init__(self, connection=None): + + super().__init__(connection) + self.register(corefields.ForeignKeyField('group_id', ServerGroup(connection), 11, False, 'id', 'group', select_fields=[])) diff --git a/models/tasks.py b/models/tasks.py new file mode 100644 index 0000000..ac0398e --- /dev/null +++ b/models/tasks.py @@ -0,0 +1,306 @@ +from paramecio2.libraries.db.webmodel import WebModel +from paramecio2.libraries.db import corefields +from paramecio2.libraries.db.extrafields.ipfield import IpField +from paramecio2.libraries.db.extrafields.arrayfield import ArrayField +from paramecio2.libraries.db.extrafields.parentfield import ParentField +from paramecio2.libraries.db.extrafields.datefield import DateField +from paramecio2.libraries.db.extrafields.dictfield import DictField +from paramecio2.libraries.i18n import I18n +import urllib3 +try: + import ujson as json +except: + import json + +from redis import Redis +from rq import Queue +import importlib +import traceback +from modules.monit.models.monit import LonelyIpField + +class Task(WebModel): + + def __init__(self, connection): + + super().__init__(connection) + + self.register(corefields.CharField('name_task'), True) + self.register(corefields.CharField('description_task'), True) + self.register(corefields.CharField('codename_task'), True) + self.register(corefields.CharField('path')) + self.register(IpField('server')) + self.register(corefields.CharField('hostname')) + self.register(corefields.CharField('where_sql_server'), 2000) + self.register(corefields.CharField('user')) + self.register(corefields.CharField('password'), 2000) + self.register(corefields.CharField('user_path')) + self.register(corefields.CharField('os_codename')) + self.register(corefields.CharField('url_return')) + self.register(corefields.CharField('ssh_user')) + self.register(corefields.CharField('ssh_key_pub')) + self.register(corefields.CharField('ssh_key_priv')) + self.register(corefields.CharField('ssh_key_password')) + self.register(DictField('data', corefields.CharField(''))) + self.register(corefields.IntegerField('num_servers')) + self.register(corefields.BooleanField('is_parent')) + self.register(ParentField('parent_id', size=11, required=False, field_name='name_task')) + self.fields['where_sql_server'].escape=True + + self.txt_error='' + + def run_task(self, server, path, name_task, codename_task, description_task, data={}, user='', password='', where_sql_server='', url='', ssh_key_priv='', ssh_key_password='', send_task=True): + + logtask=LogTask(self.connection) + #servers=Server(self.connection) + + self.safe_query() + logtask.safe_query() + + parent_id=None + yes_parent=False + """ + if type(server).__name__=='list': + server_list=server + server=None + yes_parent=True + """ + if self.insert({'name_task': name_task,'description_task': description_task, 'url_return': url, 'server': server, 'where_sql_server': where_sql_server, 'data': data , 'user': user, 'password': password, 'path': path, 'where_sql_server' : where_sql_server, 'ssh_key_priv': ssh_key_priv, 'ssh_key_password': ssh_key_password}): + + task_id=self.insert_id() + parent_id=task_id + self.task_id=task_id + arr_ids=[] + + if where_sql_server!='': + yes_parent=True + + """ + if yes_parent: + for server in server_list: + self.insert({'name_task': name_task,'description_task': description_task, 'url_return': url, 'server': server, 'where_sql_server': '', 'data': data , 'user': user, 'password': password, 'path': path, 'where_sql_server' : where_sql_server, 'ssh_key_priv': ssh_key_priv, 'ssh_key_password': ssh_key_password, 'parent_id': parent_id}) + arr_ids.append(self.insert_id()) + """ + + if send_task: + """ + http = urllib3.PoolManager() + + try: + + r = http.request('GET', 'http://127.0.0.1:1337', fields={'task_id': task_id, 'api_key': api_key}) + + if r.status!=200: + self.error=True + self.txt_error="Cannot access to task server: Error "+str(r.status) + return False + + else: + resp=json.loads(r.data.decode('utf-8')) + + if resp['error']: + self.error=True + self.txt_error=resp['message'] + logtask.insert(resp) + return False + + except urllib3.exceptions.MaxRetryError: + self.txt_error='Cannot connect to the task server, check if is up' + return False + """ + + return self.send_task(task_id, yes_parent, where_sql_server) + + """ + q = Queue(connection=Redis(host=redis_host, port=redis_port, password=redis_password)) + + try: + + if where_sql_server=='': + result = q.enqueue('modules.pastafari.servers.server_rq.send_pastafari_task', task_id, server, job_timeout=rq_timeout) + + else: + + for arr_server in servers.set_conditions(where_sql_server, []).select_to_array(['ip']): + result=q.enqueue('modules.pastafari.servers.server_rq.send_pastafari_task', task_id, arr_server['ip'], job_timeout=rq_timeout) + except: + + self.error=True + self.txt_error=traceback.format_exc() + return False + """ + + + + return True + + else: + + self.error=True + self.txt_error="Cannot insert the task" + return False + + + def send_task(self, task_id, yes_parent=False, where_sql_server=''): + + """ + http = urllib3.PoolManager() + + try: + + r = http.request('GET', 'http://127.0.0.1:1337', fields={'task_id': task_id, 'api_key': api_key}) + + if r.status!=200: + self.error=True + self.txt_error="Cannot access to task server: Error "+str(r.status) + return False + + else: + resp=json.loads(r.data.decode('utf-8')) + + if resp['error']: + self.error=True + self.txt_error=resp['message'] + logtask.insert(resp) + return False + + except urllib3.exceptions.MaxRetryError: + self.error=True + self.txt_error='Cannot connect to the task server, check if is up' + return False + """ + + q = Queue(connection=Redis()) + + if yes_parent: + arr_task=self.select_a_row(task_id) + #If where sql, send multiple tasks. + sql_insert_values=[] + sql_insert='insert into task (`name_task`, `description_task`, `url_return`, `server`, `hostname`, `data`, `user`, `password`, `path`, `ssh_key_priv`, `ssh_key_password`, `parent_id`) VALUES ' + + with self.query('select id, hostname from serverdbtask '+where_sql_server) as cursor: + + for row in cursor: + arr_task['server']=row['hostname'] + sql_insert_values.append("('"+arr_task['name_task']+"', '"+arr_task['description_task']+"', '"+arr_task['url_return']+"', '"+arr_task['server']+"', '"+arr_task['server']+"', '"+arr_task['data']+"', '"+arr_task['user']+"', '"+arr_task['password']+"', '"+arr_task['path']+"', '"+arr_task['ssh_key_priv']+"', '"+arr_task['ssh_key_password']+"', '"+str(task_id)+"')") + + #self.insert({'name_task': name_task,'description_task': description_task, 'url_return': url, 'server': server, 'where_sql_server': where_sql_server, 'data': data , 'user': user, 'password': password, 'path': path, 'where_sql_server' : where_sql_server, 'ssh_key_priv': ssh_key_priv, 'ssh_key_password': ssh_key_password}) + pass + + final_sql=sql_insert+", ".join(sql_insert_values) + + self.query(final_sql) + + with self.query('select id from task WHERE parent_id=%s', [task_id]) as cursor: + + for row in cursor: + #print(row) + result = q.enqueue(task, row['id'], job_timeout=3600) + + else: + + #Enqueue task function. + + result = q.enqueue(task, task_id, job_timeout=3600) + + return True + +# Function used in rq worker for exec the ssh task. + +def task(task_id): + + conn=WebModel.connection() + + task=Task(conn) + + logtask=LogTask(conn) + logtask.safe_query() + + arr_task=task.select_a_row(task_id) + + if not arr_task: + + self.logtask.insert({'task_id': task_id, 'progress': 100, 'message': I18n.lang('pastafari', 'error_task_not_exists', 'Error: task not exists'), 'status': 1, 'error': 1, 'server': ''}) + + conn.close() + + return False + + server=arr_task['server'] + + try: + + ssh_task=importlib.import_module(arr_task['path']) + + """ + | name_task | varchar(255) | NO | | | | + | description_task | varchar(255) | NO | | | | + | codename_task | varchar(255) | NO | | | | + | path | varchar(255) | NO | | | | + | server | varchar(255) | NO | | | | + | hostname | varchar(255) | NO | | | | + | where_sql_server | varchar(255) | NO | | | | + | user | varchar(255) | NO | | | | + | password | varchar(255) | NO | | | | + | user_path | varchar(255) | NO | | | | + | os_codename | varchar(255) | NO | | | | + | url_return | varchar(255) | NO | | | | + | data | text | NO | | NULL | | + | ssh_key_pub | varchar(255) | NO | | | | + | ssh_key_priv | varchar(255) | NO | | | | + | ssh_user | varchar(255) | NO | | | | + | num_servers | int(11) | NO | | 0 | | + +------------------+--------------+------+-----+---------+----------------+ + """ + + remote_user=arr_task['user'] + remote_password=arr_task['password'] + private_key=arr_task['ssh_key_priv'] + password_key=arr_task.get('ssh_key_password', '') + + final_task=ssh_task.ServerTask(server, conn, remote_user=remote_user, remote_password=remote_password, private_key=private_key, password_key=password_key, remote_path='pastafari2', task_id=task_id, data=json.loads(arr_task['data'])) + + final_task.exec() + + except: + + if conn==None: + conn=WebModel.connection() + logtask.sqlclass=conn + + logtask.insert({'task_id': task_id, 'progress': 100, 'message': I18n.lang('pastafari', 'error_in_task', 'Error: error in task ')+traceback.format_exc(), 'error': 1, 'status': 1, 'server': server}) + + conn.close() + + return False + + conn.close() + + return True + + +class LogTask(WebModel): + + def __init__(self, connection): + + super().__init__(connection) + + self.register(DateField('path')) + self.register(corefields.ForeignKeyField('task_id', Task(connection), size=11, required=False, identifier_field='id', named_field="name_task")) + self.register(IpField('server')) + self.register(corefields.DoubleField('progress')) + self.register(corefields.BooleanField('no_progress')) + self.register(corefields.TextField('message')) + self.register(corefields.BooleanField('error')) + self.register(corefields.BooleanField('status')) + self.register(ArrayField('data', corefields.CharField('data'))) + self.register(corefields.BooleanField('code_error')) + +# For grouping + +class TaskDone(WebModel): + + def __init__(self, connection): + + super().__init__(connection) + self.register(corefields.CharField('name_task'), True) + self.register(LonelyIpField('ip'), True) diff --git a/scripts/system/alive.sh b/scripts/system/alive.sh new file mode 100644 index 0000000..552c071 --- /dev/null +++ b/scripts/system/alive.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +echo '{"progress": 0, "message": "This server is up!!!", "error":0, "code_error": 0, "sucks": 1}' + +sleep 1 + +echo '{"progress": 100, "message": "Finishing scripts without errors", "error":0, "code_error": 0, "sucks": 1}' + +exit 0 + diff --git a/scripts/system/install_git.sh b/scripts/system/install_git.sh new file mode 100644 index 0000000..bfb84a9 --- /dev/null +++ b/scripts/system/install_git.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +echo "Installing git" + +echo '{"error": 0, "status": 0, "progress": 0, "no_progress":0, "message": "Installing git..."}' + +sleep 1 + +sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y git + +if [ $? -eq 0 ]; then + + sleep 1 + + #echo "Installed git sucessfully if not error..." + echo '{"error": 0, "status": 0, "progress": 100, "no_progress":0, "message": "Installed git sucessfully..."}' + + sleep 1 + +else + + echo "Sorry, cannot update the server..." + + exit; + +fi + + + diff --git a/scripts/system/install_psutil.sh b/scripts/system/install_psutil.sh new file mode 100644 index 0000000..3b2dadf --- /dev/null +++ b/scripts/system/install_psutil.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +# Install pip + +sleep 1 + +echo '{"error": 0, "status": 0, "progress": 100, "no_progress":0, "message": "Installing python3-psutil..."}' + +sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y python3-pip + +#sudo pip3 install psutil +sudo apt-get install -y python3-psutil + +#echo "Installed python3-psutil sucessfully if not error..." + +sleep 1 + +echo '{"error": 0, "status": 0, "progress": 100, "no_progress":0, "message": "Installed python3-psutil sucessfully..."}' + diff --git a/scripts/system/install_python.sh b/scripts/system/install_python.sh new file mode 100644 index 0000000..07746ad --- /dev/null +++ b/scripts/system/install_python.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +sleep 1 + +echo '{"error": 0, "status": 0, "progress": 0, "no_progress":0, "message": "Installing Python..."}' + +sleep 1 + +sudo apt-get -y install python3 python3-pip + +if [ $? -eq 0 ]; then + + #echo "Installed python3 sucessfully if not error..." + echo '{"error": 0, "status": 0, "progress": 100, "no_progress":0, "message": "Installed python sucessfully..."}' + + sleep 1 + +else + + echo "Sorry, cannot install python..." + + exit 1; + +fi + +sleep 1 + diff --git a/scripts/system/install_pzoo_stats.py b/scripts/system/install_pzoo_stats.py new file mode 100644 index 0000000..8c3ee93 --- /dev/null +++ b/scripts/system/install_pzoo_stats.py @@ -0,0 +1,192 @@ +#!/usr/bin/python3 -u + +# A script for install pzoo user + +import subprocess +import argparse +import re +import os +import shutil +import pwd +from subprocess import call +from pathlib import Path +from time import sleep + +parser = argparse.ArgumentParser(description='A script for install leviathan user') + +#parser.add_argument('--url', help='The url where notify updates', required=True) +parser.add_argument('--url_stats', help='The url where pastafaristats notify the stats', required=True) +parser.add_argument('--user', help='The user for pastafari', required=True) +parser.add_argument('--pub_key', help='The pub key used in pastafari user', required=True) +parser.add_argument('--group', help='Server group', required=False) + +args = parser.parse_args() + +#url=args.url + +check_url = re.compile( + r'^(?:http|ftp)s?://' # http:// or https:// + r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' #domain... + r'localhost|' #localhost... + r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip + r'(?::\d+)?' # optional port + r'(?:/?|[/?]\S+)$', re.IGNORECASE) + +print('{"error": 0, "status": 0, "progress": 0, "no_progress":0, "message": "Installing tools for monitoring the server..."}') + +#check_url.match(args.url) and + +if check_url.match(args.url_stats): + + try: + u=pwd.getpwnam(args.user) + + if call("sudo userdel -r %s" % args.user, shell=True) > 0: + print('Error, user with same username exists and cannot delete of the server') + exit(1) + else: + print('Cleaning user with the same name') + + except: + pass + + # Create users + + if call("sudo useradd -m -s /bin/bash %s" % args.user, shell=True) > 0: + + # Delete user with this name, you should not install it in a old server. + + if call("sudo userdel -r %s" % args.user, shell=True) > 0: + print('Error, remove the old user') + exit(1) + else: + + if call("sudo useradd -m -s /bin/sh %s" % args.user, shell=True) > 0: + print('Error, cannot add a new user') + exit(1) + + else: + print('Added user') + + if call("sudo mkdir -p /home/"+args.user+"/.ssh && sudo chown "+args.user+":"+args.user+" /home/"+args.user+"/.ssh && sudo chmod 700 /home/"+args.user+"/.ssh", shell=True) > 0: + print('Error, cannot add ssh directory') + exit(1) + else: + print('Added ssh directory') + + if call("sudo cp "+args.pub_key+" /home/"+args.user+"/.ssh/authorized_keys && sudo chown "+args.user+":"+args.user+" /home/"+args.user+"/.ssh/authorized_keys && sudo chmod 600 /home/"+args.user+"/.ssh/authorized_keys", shell=True) > 0: + print('Error, cannot pub key to user') + exit(1) + else: + print('Added pub key to user') + + # Edit sudo file + + with open('modules/pastafari2/scripts/system/sudoers.d/spanel') as f: + sudoers=f.read() + + with open('/etc/sudoers.d/spanel', 'w') as f: + sudoers=sudoers.replace("spanel", args.user) + f.write(sudoers) + + # Installing composer things for php alerts... + + + #mkdir $HOME/pzoo/scripts + + #chown $USER:$USER $HOME/pzoo/scripts + + #su - $USER -s /bin/bash -c "composer --working-dir=$HOME/scripts require guzzlehttp/guzzle:~6.0" + + #pt=Path('/home/'+args.user+'/pzoo/tasks') + + #pt.mkdir(mode=0o755, parents=True, exist_ok=True) + + # Create scripts pzoo + """ + ps=Path('/home/'+args.user+'/pzoo/scripts') + + ps.mkdir(mode=0o755, parents=True, exist_ok=True) + + shutil.chown('/home/'+args.user+'/pzoo', args.user, args.user) + + shutil.chown('/home/'+args.user+'/pzoo/scripts', args.user, args.user) + + + if call("su - "+args.user+" -s /bin/bash -c \"composer --working-dir=/home/"+args.user+"/pzoo/scripts require guzzlehttp/guzzle:~6.0\"", shell=True)>0: + print('Error, cannot install scripts for use in check scripts') + exit(1) + else: + print('Composer dependencies for check scripts added successfully') + + # Edit get_updates.py + + with open('scripts/system/get_updates.py') as f: + get_updates=f.read() + + with open('/etc/cron.daily/get_updates.py', 'w') as f: + url_updates=args.url.replace('/getinfo/', '/getupdates/') + get_updates=get_updates.replace("http://url/to/server/token/ip", url_updates) + f.write(get_updates) + + os.chmod('/etc/cron.daily/get_updates.py', 0o700) + """ + if call("sudo pip3 install --upgrade git+https://bitbucket.org/paramecio/pastafaristats", shell=True)>0: + print('Error, cannot install pastafari stats') + exit(1) + else: + print('Added pastafari stats') + + # Add configuration to pastafari stats + + if not os.path.isdir('/etc/pastafari'): + # Create pastafari dir + p=Path('/etc/pastafari') + p.mkdir(mode=0o755, parents=False, exist_ok=True) + + with open('/etc/pastafari/stats.cfg', 'w') as f: + + f.write("[DEFAULT]\n\nurl_server="+args.url_stats+"\ngroup="+args.group) + + + with open('/etc/systemd/system/pastafaristats.service', 'w') as f: + + systemd_unit=""" + # Save it in /etc/systemd/system/pastafaristats.service + + [Unit] + Description=Pastafari Stats + After=syslog.target + After=network.target + + [Service] + Type=simple + User=pzoo + Group=pzoo + ExecStart=pastafaristats + Restart=always + Environment=PYTHONUNBUFFERED=1 + + [Install] + WantedBy=multi-user.target + """ + + systemd_unit=systemd_unit.replace('pzoo', args.user) + + f.write(systemd_unit) + + if call("sudo systemctl enable pastafaristats.service && sudo systemctl start pastafaristats.service", shell=True)>0: + print('Error, cannot start pastafari stats') + exit(1) + else: + print('Pastafari stats ready') + + print('{"error": 0, "status": 0, "progress": 100, "no_progress":0, "message": "Tools installed..."}') + + sleep(1) + +else: + + print('Error installing the module, not valid url') + + exit(1) diff --git a/scripts/system/sudoers.d/spanel b/scripts/system/sudoers.d/spanel new file mode 100644 index 0000000..6e89fec --- /dev/null +++ b/scripts/system/sudoers.d/spanel @@ -0,0 +1 @@ +spanel ALL=(ALL) NOPASSWD: ALL diff --git a/settings/config_admin.py b/settings/config_admin.py new file mode 100644 index 0000000..1e8e9e0 --- /dev/null +++ b/settings/config_admin.py @@ -0,0 +1,10 @@ +from paramecio2.libraries.config_admin import config_admin +from paramecio2.libraries.i18n import I18n + +#modules_admin=[[I18n.lang('admin', 'users_admin', 'User\'s Admin'), 'paramecio.modules.admin.admin.ausers', 'ausers']] + +config_admin.append([I18n.lang('pastafari2', 'pastafari_admin', 'Pastafari admin')]) + +config_admin.append([I18n.lang('pastafari2', 'settings', 'Settings'), 'modules.pastafari2.admin.dashboard', 'admin_app.pastafari2_settings', 'fa-code']) +config_admin.append([I18n.lang('pastafari2', 'servers', 'Servers'), 'modules.pastafari2.admin.dashboard', 'admin_app.pastafari2_dashboard', 'fa-linux']) +config_admin.append([I18n.lang('pastafari2', 'groups', 'Groups'), 'modules.pastafari2.admin.groups', 'admin_app.pastafari2_groups', 'fa-object-group']) diff --git a/tasks/system/ping.py b/tasks/system/ping.py new file mode 100644 index 0000000..2d173d6 --- /dev/null +++ b/tasks/system/ping.py @@ -0,0 +1,14 @@ +from modules.pastafari2.libraries.task import Task +from modules.pastafari2.models.pastafari2 import ServerDbTask + +class ServerTask(Task): + + def __init__(self, server, conn, remote_user='root', remote_password='', private_key='./ssh/id_rsa', password_key='', remote_path='leviathan', task_id=0, data={}): + + super().__init__(server, conn, remote_user, remote_password, private_key, password_key, remote_path, task_id, data) + + self.files=[['modules/pastafari2/scripts/system/alive.sh', 0o755]] + + self.commands_to_execute=[] + + self.commands_to_execute.append(['modules/pastafari2/scripts/system/alive.sh', '']) diff --git a/tasks/system/task.py b/tasks/system/task.py new file mode 100644 index 0000000..d351dc5 --- /dev/null +++ b/tasks/system/task.py @@ -0,0 +1,38 @@ +from modules.pastafari2.libraries.task import Task +from modules.pastafari2.models.pastafari2 import ServerDbTask + +class ServerTask(Task): + + def __init__(self, server, conn, remote_user='root', remote_password='', private_key='./ssh/id_rsa', password_key='', remote_path='leviathan', task_id=0, data={}): + + super().__init__(server, conn, remote_user, remote_password, private_key, password_key, remote_path, task_id, data) + + #self.files=[['modules/pastafari2/scripts/system/alive.sh', 0o755]] + + self.files=[['modules/pastafari2/scripts/system/install_pzoo_stats.py', 0o755]] + self.files.append(['modules/pastafari2/scripts/system/install_python.sh', 0o755]) + self.files.append(['modules/pastafari2/scripts/system/install_psutil.sh', 0o755]) + self.files.append(['modules/pastafari2/scripts/system/install_git.sh', 0o755]) + self.files.append(['modules/pastafari2/scripts/system/sudoers.d/spanel', 0o640]) + + self.files.append([self.data['pub_key'], 0o600]) + + self.commands_to_execute=[] + + #self.commands_to_execute.append(['modules/pastafari2/scripts/system/alive.sh', '']) + self.commands_to_execute.append(['modules/pastafari2/scripts/system/install_git.sh', '']) + self.commands_to_execute.append(['modules/pastafari2/scripts/system/install_python.sh', '']) + self.commands_to_execute.append(['modules/pastafari2/scripts/system/install_psutil.sh', '']) + self.commands_to_execute.append(['modules/pastafari2/scripts/system/install_pzoo_stats.py', '--user='+self.data['ssh_user']+' --pub_key='+self.data['pub_key']+' --url_stats='+self.data['url_stats']+' --group='+self.data['group_name']]) + + def post_task(self): + + server=ServerDbTask(self.connection) + + server.safe_query() + + #'subdomain_id': self.data['subdomain_id'] + + server.insert({'hostname': self.data['hostname'], 'ip': self.data['ip'], 'group_id': self.data['group_id']}) + + return True diff --git a/templates/admin/add_server.phtml b/templates/admin/add_server.phtml new file mode 100644 index 0000000..151e9e7 --- /dev/null +++ b/templates/admin/add_server.phtml @@ -0,0 +1,42 @@ +<%inherit file="dashboard.phtml"/> +<%block name="content"> +
+

Servers >> ${lang('pastafari2', 'add_server', 'Add server')}

+

${lang('pastafari2', 'add_server', 'Add server')}

+
+
+

Please fill the form for add the new server to the system.

+

+

${group_form.form()|n}

+

+

+

+

+
+
+

Servers >> ${lang('pastafari2', 'add_server', 'Add server')}

+ + +
+ diff --git a/templates/admin/dash_pastafari.phtml b/templates/admin/dash_pastafari.phtml new file mode 100644 index 0000000..fcfa94e --- /dev/null +++ b/templates/admin/dash_pastafari.phtml @@ -0,0 +1,135 @@ +<%inherit file="dashboard.phtml"/> +<%block name="content"> +
+

${lang('pastafari2', 'add_server', 'Add server')} +

${lang('monit', 'choose_group', 'Choose group')}: + +

+ + + +
+
+ + + +
+ +<%block name="jscript_block"> + + + + diff --git a/templates/admin/multiprogress.phtml b/templates/admin/multiprogress.phtml new file mode 100644 index 0000000..3b7eac7 --- /dev/null +++ b/templates/admin/multiprogress.phtml @@ -0,0 +1,104 @@ +<%inherit file="dashboard.phtml"/> +<%block name="content"> +

${name_task}

+

${description_task}

+
+

${lang('pastafari2', 'num_servers', 'Number of servers')}: ${num_servers}

+

${lang('pastafari2', 'completed_tasks', 'Completed tasks')}: 0

+ + + + + + + +
+ + +<%block name="jscript_block"> + + diff --git a/templates/admin/progress.phtml b/templates/admin/progress.phtml new file mode 100644 index 0000000..907eb54 --- /dev/null +++ b/templates/admin/progress.phtml @@ -0,0 +1,224 @@ +<%inherit file="dashboard.phtml"/> +<%block name="extra_css"> + + + + +<%block name="content"> +

${lang('pastafari2', 'task progress', 'Task progress')} - ${name_task}

+

${description_task}

+
+ +
${lang('pastafari2', 'processing_task', 'Processing task...')}
+
+ +<%block name="jscript_block"> + + + diff --git a/templates/admin/settings.phtml b/templates/admin/settings.phtml new file mode 100644 index 0000000..40497f4 --- /dev/null +++ b/templates/admin/settings.phtml @@ -0,0 +1,49 @@ +<%inherit file="dashboard.phtml"/> +<%block name="content"> +
+
+

${lang('pastafari2', 'global_ssh_keys', 'Global ssh keys')}

+
+ ${txt_error|n} + ${txt_generate_key|n} +
+ % if not regenerate: +

+ + % endif +
+
+ +<%block name="jscript_block"> + + + +