├── .gitignore ├── README.md ├── Vagrantfile ├── ansible-conf └── ansible.cfg ├── ansible.wsgi ├── ansible_uwsgi.ini ├── apache-conf └── ansible.conf ├── celery-conf └── supervisord.conf ├── desktop ├── __init__.py ├── apps │ ├── __init__.py │ ├── account │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── crypt.py │ │ ├── models.py │ │ ├── urls.py │ │ └── views.py │ └── ansible │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── elfinder │ │ ├── .gitignore │ │ ├── __init__.py │ │ ├── conf │ │ │ ├── __init__.py │ │ │ └── settings.py │ │ ├── connector.py │ │ ├── exceptions.py │ │ ├── fields.py │ │ ├── locale │ │ │ └── el │ │ │ │ └── LC_MESSAGES │ │ │ │ ├── django.mo │ │ │ │ └── django.po │ │ ├── models.py │ │ ├── static │ │ │ ├── css │ │ │ │ └── jquery.elfinder-widget.full.css │ │ │ ├── elfinder │ │ │ │ ├── css │ │ │ │ │ ├── elfinder.full.css │ │ │ │ │ ├── elfinder.min.css │ │ │ │ │ └── theme.css │ │ │ │ ├── img │ │ │ │ │ ├── arrows-active.png │ │ │ │ │ ├── arrows-normal.png │ │ │ │ │ ├── crop.gif │ │ │ │ │ ├── dialogs.png │ │ │ │ │ ├── icons-big.png │ │ │ │ │ ├── icons-small.png │ │ │ │ │ ├── logo.png │ │ │ │ │ ├── progress.gif │ │ │ │ │ ├── quicklook-bg.png │ │ │ │ │ ├── quicklook-icons.png │ │ │ │ │ ├── resize.png │ │ │ │ │ ├── spinner-mini.gif │ │ │ │ │ └── toolbar.png │ │ │ │ ├── js │ │ │ │ │ ├── elfinder.full.js │ │ │ │ │ ├── elfinder.min.js │ │ │ │ │ ├── i18n │ │ │ │ │ │ ├── elfinder.LANG.js │ │ │ │ │ │ ├── elfinder.ar.js │ │ │ │ │ │ ├── elfinder.bg.js │ │ │ │ │ │ ├── elfinder.ca.js │ │ │ │ │ │ ├── elfinder.cs.js │ │ │ │ │ │ ├── elfinder.de.js │ │ │ │ │ │ ├── elfinder.el.js │ │ │ │ │ │ ├── elfinder.es.js │ │ │ │ │ │ ├── elfinder.fa.js │ │ │ │ │ │ ├── elfinder.fr.js │ │ │ │ │ │ ├── elfinder.hu.js │ │ │ │ │ │ ├── elfinder.it.js │ │ │ │ │ │ ├── elfinder.jp.js │ │ │ │ │ │ ├── elfinder.ko.js │ │ │ │ │ │ ├── elfinder.nl.js │ │ │ │ │ │ ├── elfinder.no.js │ │ │ │ │ │ ├── elfinder.pl.js │ │ │ │ │ │ ├── elfinder.pt_BR.js │ │ │ │ │ │ ├── elfinder.ru.js │ │ │ │ │ │ ├── elfinder.tr.js │ │ │ │ │ │ └── elfinder.zh_CN.js │ │ │ │ │ └── proxy │ │ │ │ │ │ └── elFinderSupportVer1.js │ │ │ │ └── sounds │ │ │ │ │ └── rm.wav │ │ │ ├── images │ │ │ │ └── close-button.png │ │ │ └── js │ │ │ │ └── jquery.elfinder-widget.full.js │ │ ├── tests │ │ │ ├── __init__.py │ │ │ ├── connector.py │ │ │ ├── media │ │ │ │ └── files │ │ │ │ │ ├── 2bytes.txt │ │ │ │ │ └── directory │ │ │ │ │ └── yawd-logo.png │ │ │ └── volumes.py │ │ ├── urls.py │ │ ├── utils │ │ │ ├── __init__.py │ │ │ ├── accesscontrol.py │ │ │ ├── archivers.py │ │ │ └── volumes.py │ │ ├── views.py │ │ ├── volumes │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── filesystem.py │ │ │ └── storage.py │ │ └── widgets.py │ │ ├── job_run_test.py │ │ ├── models.py │ │ ├── path_utils.py │ │ ├── tasks.py │ │ ├── urls.py │ │ └── views.py └── core │ ├── __init__.py │ ├── auth │ ├── __init__.py │ ├── backend.py │ └── views.py │ ├── internal │ ├── __init__.py │ └── settings_local.py │ ├── lib │ ├── __init__.py │ ├── django_util.py │ ├── exceptions.py │ └── exceptions_renderable.py │ ├── locale │ └── zh_CN │ │ └── LC_MESSAGES │ │ ├── django.mo │ │ └── django.po │ ├── middleware.py │ ├── pagination │ ├── __init__.py │ ├── middleware.py │ ├── models.py │ ├── paginator.py │ ├── templates │ │ └── pagination │ │ │ └── pagination.html │ ├── templatetags │ │ ├── __init__.py │ │ └── pagination_tags.py │ └── tests.py │ ├── settings.py │ ├── static │ ├── admin │ │ ├── css │ │ │ ├── base.css │ │ │ ├── changelists.css │ │ │ ├── dashboard.css │ │ │ ├── forms.css │ │ │ ├── ie.css │ │ │ ├── login.css │ │ │ ├── rtl.css │ │ │ └── widgets.css │ │ ├── img │ │ │ ├── changelist-bg.gif │ │ │ ├── changelist-bg_rtl.gif │ │ │ ├── chooser-bg.gif │ │ │ ├── chooser_stacked-bg.gif │ │ │ ├── default-bg-reverse.gif │ │ │ ├── default-bg.gif │ │ │ ├── deleted-overlay.gif │ │ │ ├── gis │ │ │ │ ├── move_vertex_off.png │ │ │ │ └── move_vertex_on.png │ │ │ ├── icon-no.gif │ │ │ ├── icon-unknown.gif │ │ │ ├── icon-yes.gif │ │ │ ├── icon_addlink.gif │ │ │ ├── icon_alert.gif │ │ │ ├── icon_calendar.gif │ │ │ ├── icon_changelink.gif │ │ │ ├── icon_clock.gif │ │ │ ├── icon_deletelink.gif │ │ │ ├── icon_error.gif │ │ │ ├── icon_searchbox.png │ │ │ ├── icon_success.gif │ │ │ ├── inline-delete-8bit.png │ │ │ ├── inline-delete.png │ │ │ ├── inline-restore-8bit.png │ │ │ ├── inline-restore.png │ │ │ ├── inline-splitter-bg.gif │ │ │ ├── nav-bg-grabber.gif │ │ │ ├── nav-bg-reverse.gif │ │ │ ├── nav-bg-selected.gif │ │ │ ├── nav-bg.gif │ │ │ ├── selector-icons.gif │ │ │ ├── selector-search.gif │ │ │ ├── sorting-icons.gif │ │ │ ├── tool-left.gif │ │ │ ├── tool-left_over.gif │ │ │ ├── tool-right.gif │ │ │ ├── tool-right_over.gif │ │ │ ├── tooltag-add.gif │ │ │ ├── tooltag-add_over.gif │ │ │ ├── tooltag-arrowright.gif │ │ │ └── tooltag-arrowright_over.gif │ │ └── js │ │ │ ├── LICENSE-JQUERY.txt │ │ │ ├── SelectBox.js │ │ │ ├── SelectFilter2.js │ │ │ ├── actions.js │ │ │ ├── actions.min.js │ │ │ ├── admin │ │ │ ├── DateTimeShortcuts.js │ │ │ ├── RelatedObjectLookups.js │ │ │ └── ordering.js │ │ │ ├── calendar.js │ │ │ ├── collapse.js │ │ │ ├── collapse.min.js │ │ │ ├── core.js │ │ │ ├── getElementsBySelector.js │ │ │ ├── inlines.js │ │ │ ├── inlines.min.js │ │ │ ├── jquery.init.js │ │ │ ├── jquery.js │ │ │ ├── jquery.min.js │ │ │ ├── prepopulate.js │ │ │ ├── prepopulate.min.js │ │ │ ├── timeparse.js │ │ │ └── urlify.js │ ├── css │ │ └── portal.css │ ├── ext │ │ ├── css │ │ │ ├── awx.min.css │ │ │ ├── bootstrap-datetimepicker.min.css │ │ │ ├── bootstrap-responsive.min.css │ │ │ ├── bootstrap-theme.css │ │ │ ├── bootstrap.min.css │ │ │ ├── elfinder.min.css │ │ │ ├── elfinder.theme.css │ │ │ ├── font-awesome-ie7.min.css │ │ │ ├── font-awesome.min.css │ │ │ ├── images │ │ │ │ ├── animated-overlay.gif │ │ │ │ ├── glyphicons-halflings-white.png │ │ │ │ ├── glyphicons-halflings.png │ │ │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ │ │ │ ├── ui-bg_flat_75_ffffff_40x100.png │ │ │ │ ├── ui-bg_glass_55_fbf9ee_1x400.png │ │ │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ │ │ ├── ui-bg_glass_75_dadada_1x400.png │ │ │ │ ├── ui-bg_glass_75_e6e6e6_1x400.png │ │ │ │ ├── ui-bg_glass_95_fef1ec_1x400.png │ │ │ │ ├── ui-bg_highlight-soft_75_cccccc_1x100.png │ │ │ │ ├── ui-icons_222222_256x240.png │ │ │ │ ├── ui-icons_2e83ff_256x240.png │ │ │ │ ├── ui-icons_454545_256x240.png │ │ │ │ ├── ui-icons_888888_256x240.png │ │ │ │ └── ui-icons_cd0a0a_256x240.png │ │ │ ├── jquery-ui.css │ │ │ ├── select2-bootstrap.css │ │ │ ├── select2.css │ │ │ └── simplePagination.css │ │ ├── elfinder │ │ │ ├── img │ │ │ │ ├── arrows-active.png │ │ │ │ ├── arrows-normal.png │ │ │ │ ├── crop.gif │ │ │ │ ├── dialogs.png │ │ │ │ ├── icons-big.png │ │ │ │ ├── icons-small.png │ │ │ │ ├── logo.png │ │ │ │ ├── progress.gif │ │ │ │ ├── quicklook-bg.png │ │ │ │ ├── quicklook-icons.png │ │ │ │ ├── resize.png │ │ │ │ ├── spinner-mini.gif │ │ │ │ └── toolbar.png │ │ │ └── js │ │ │ │ ├── elfinder.min.js │ │ │ │ ├── i18n │ │ │ │ ├── elfinder.LANG.js │ │ │ │ ├── elfinder.ar.js │ │ │ │ ├── elfinder.bg.js │ │ │ │ ├── elfinder.ca.js │ │ │ │ ├── elfinder.cs.js │ │ │ │ ├── elfinder.de.js │ │ │ │ ├── elfinder.es.js │ │ │ │ ├── elfinder.fr.js │ │ │ │ ├── elfinder.hu.js │ │ │ │ ├── elfinder.jp.js │ │ │ │ ├── elfinder.nl.js │ │ │ │ ├── elfinder.no.js │ │ │ │ ├── elfinder.pl.js │ │ │ │ ├── elfinder.pt_BR.js │ │ │ │ ├── elfinder.ru.js │ │ │ │ └── elfinder.zh_CN.js │ │ │ │ └── proxy │ │ │ │ └── elFinderSupportVer1.js │ │ ├── font │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ └── fontawesome-webfont.woff │ │ ├── img │ │ │ ├── animated-overlay.gif │ │ │ ├── glyphicons-halflings-white.png │ │ │ ├── glyphicons-halflings.png │ │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ │ │ ├── ui-bg_flat_75_ffffff_40x100.png │ │ │ ├── ui-bg_glass_55_fbf9ee_1x400.png │ │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ │ ├── ui-bg_glass_75_dadada_1x400.png │ │ │ ├── ui-bg_glass_75_e6e6e6_1x400.png │ │ │ ├── ui-bg_glass_95_fef1ec_1x400.png │ │ │ ├── ui-bg_highlight-soft_75_cccccc_1x100.png │ │ │ ├── ui-icons_222222_256x240.png │ │ │ ├── ui-icons_2e83ff_256x240.png │ │ │ ├── ui-icons_454545_256x240.png │ │ │ ├── ui-icons_888888_256x240.png │ │ │ └── ui-icons_cd0a0a_256x240.png │ │ ├── js │ │ │ ├── bootstrap-datetimepicker.min.js │ │ │ ├── bootstrap.min.js │ │ │ ├── highcharts.js │ │ │ ├── html5.js │ │ │ └── jquery │ │ │ │ ├── jquery-1.8.1.min.js │ │ │ │ ├── jquery-ui.min.js │ │ │ │ ├── jquery.bsFormAlerts.min.js │ │ │ │ ├── jquery.simplePagination.js │ │ │ │ └── plugins │ │ │ │ └── jquery.validate.min.js │ │ └── prettify │ │ │ ├── lang-apollo.js │ │ │ ├── lang-basic.js │ │ │ ├── lang-clj.js │ │ │ ├── lang-css.js │ │ │ ├── lang-dart.js │ │ │ ├── lang-erlang.js │ │ │ ├── lang-go.js │ │ │ ├── lang-hs.js │ │ │ ├── lang-lisp.js │ │ │ ├── lang-llvm.js │ │ │ ├── lang-lua.js │ │ │ ├── lang-matlab.js │ │ │ ├── lang-ml.js │ │ │ ├── lang-mumps.js │ │ │ ├── lang-n.js │ │ │ ├── lang-pascal.js │ │ │ ├── lang-proto.js │ │ │ ├── lang-r.js │ │ │ ├── lang-rd.js │ │ │ ├── lang-scala.js │ │ │ ├── lang-sql.js │ │ │ ├── lang-tcl.js │ │ │ ├── lang-tex.js │ │ │ ├── lang-vb.js │ │ │ ├── lang-vhdl.js │ │ │ ├── lang-wiki.js │ │ │ ├── lang-xq.js │ │ │ ├── lang-yaml.js │ │ │ ├── prettify.css │ │ │ ├── prettify.js │ │ │ ├── run_prettify.js │ │ │ └── skins │ │ │ ├── desert.css │ │ │ ├── doxy.css │ │ │ ├── sons-of-obsidian.css │ │ │ └── sunburst.css │ └── img │ │ ├── bg-login.png │ │ ├── bstree-halflings.png │ │ ├── favicon.ico │ │ └── login-logo.png │ ├── templates │ ├── 404.html │ ├── 500.html │ ├── account │ │ ├── credential.html │ │ ├── list_users.html │ │ └── profile.html │ ├── ansible │ │ ├── add_project.html │ │ ├── deploykey.html │ │ ├── execute_playbook.html │ │ ├── execute_scp.html │ │ ├── execute_script.html │ │ ├── explore.html │ │ ├── list_projects.html │ │ ├── list_projects_group.html │ │ ├── manage_project.html │ │ ├── result.html │ │ ├── schedule.html │ │ ├── view_file.html │ │ ├── view_project.html │ │ └── view_project_logs.html │ ├── base.html │ ├── error.html │ ├── index.html │ ├── login.html │ ├── logs.html │ ├── popup_error.html │ └── settings.html │ ├── urls.py │ ├── views.py │ └── wsgi.py ├── documents └── ansible_ui平台用户手册.pdf ├── manage.py ├── nginx-conf └── nginx_ansible.conf ├── playbook.yml ├── projects └── demo │ ├── inventories │ └── hosts │ └── playbooks │ ├── copy.yml │ └── ping.yml └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .idea 3 | .vagrant 4 | desktop/apps/account/migrations 5 | desktop/apps/ansible/migrations 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | About 2 | ===== 3 | 该平台为[ansible](https://github.com/ansible/ansible)系统的web程序 4 | 5 | 6 | Function 7 | ===== 8 | * 按照项目来组织布局,更为直观,上手简单 9 | * 提供简单易懂的主机管理界面 10 | * 提供用户密钥管理功能 11 | * 提供yml文件界面管理功能 12 | * 提供任务部署功能 13 | * 提供文件传输功能 14 | * 提供命令执行功能 15 | * 提供预约执行功能 16 | * 提供任务模板功能 17 | * 提供log功能 18 | * 提供邮件通知功能 19 | * 基于celery队列进行任务分发,便于扩展 20 | 21 | UserManual 22 | ===== 23 | [ansible_ui平台用户手册](https://github.com/alaxli/ansible_ui/tree/master/documents) 24 | 25 | Requirements 26 | ===== 27 | * pip 28 | * virtualenv 29 | * mysql-server,mysql-devel 30 | * openldap-devel 31 | 32 | Install 33 | ===== 34 | * 系统为CentOS6.5 35 | * 添加系统用户 36 | 37 | 38 | useradd ansible 39 | su - ansible 40 | 41 | 42 | * 配置virtualenv环境 43 | 44 | 45 | virtualenv envansible 46 | source envansible/bin/active 47 | 48 | 49 | * 下载源码 50 | 51 | 52 | git clone https://github.com/alaxli/ansible_ui.git 53 | 54 | 55 | * 安装依赖库 56 | 57 | 58 | cd ansible-ui 59 | pip install -r requirements.txt 60 | pip install PIL --allow-external PIL --allow-unverified PIL 61 | 62 | 63 | * 配置ldap、数据库和邮件信息 64 | 65 | 66 | cd desktop/core/internal 67 | vim settings_local.py 68 | # 修改 LDAP Datebase Mail 和ansible_playbook命令位置(which ansible_playbook)配置 69 | 如果需要使用ldap,还需要修改settings.py,去掉下面行的注释 70 | #'desktop.core.auth.backend.LdapBackend', 71 | 72 | 73 | * 配置数据库 74 | 75 | 76 | create database ansible CHARACTER SET utf8; 77 | grant all on ansible.* to ansibleuser@'localhost' identified by '******'; 78 | 79 | 80 | * 初始化数据库 81 | 82 | python manage.py schemamigration desktop.apps.account --init 83 | python manage.py schemamigration desktop.apps.ansible --init 84 | python manage.py syncdb 85 | python manage.py migrate ansible 86 | python manage.py migrate account 87 | python manage.py migrate kombu.transport.django 88 | python manage.py migrate djcelery 89 | python manage.py migrate guardian 90 | 91 | 92 | * 配置celery 93 | 94 | 95 | 修改celery-conf/supervisord.conf 96 | [inet_http_server] #配置web管理supervisor 97 | [program:ansible_celeryd] #修改command中 virtualenv 和 ansible_ui home 98 | 99 | 100 | * 启动celery 101 | 102 | 103 | supervisord -c celery-conf/supervisord.conf 104 | 105 | 106 | * 配置ansible 107 | 108 | 109 | cp ansible-conf/ansible.cfg ~/.ansible.cfg 110 | 111 | 112 | * Vagrant + Ansible 113 | 114 | 感谢[yunlzheng](https://github.com/yunlzheng)提供了使用vagrant+ansible自动构建开发环境的方式[Vagrantfile](https://github.com/alaxli/ansible_ui/blob/master/Vagrantfile)+[playbook.yml](https://github.com/alaxli/ansible_ui/blob/master/playbook.yml),具体操作推荐阅读yunlzheng的[《利用Ansible将开发环境纳入版本管理》](http://yunlzheng.github.io/2014/08/08/vagrant-with-ansible/) 115 | 116 | 117 | Run 118 | ===== 119 | * 直接运行 120 | 121 | 122 | python manage.py runserver ip:8000 123 | 124 | 125 | * apache + wsgi 126 | 127 | 修改apache-conf/ansible.cfg : ansible_ui_dir,指向实际目录 128 | 修改ansible.wsgi : yourvirtualenv 指向实际目录 129 | 拷贝apache-conf/ansible.cfg 到apache配置目录下 130 | 重启 httpd 131 | 132 | 133 | * nginx + uwsgi 134 | 135 | 配置nginx: 参考nginx-conf/nginx_ansible.cfg 136 | 启动nginx 137 | 修改ansible.wsgi : yourvirtualenv 指向实际目录 138 | 修改ansible_uwsgi.ini : 修改相关配置 139 | 启动 uwsgi: uwsgi --ini ansible_uwsgi.ini -d ansible.log 140 | 141 | 142 | Demo 143 | ===== 144 | * http://115.28.87.99:8888 用户名:admin 密码:ansible (由于服务器资源紧张,暂时关闭) 145 | 146 | Problem 147 | ===== 148 | * 已知问题:在单核CPU服务器上任务无法运行(在调用pexpect时会报“ValueError: I/O operation on closed file”错误,类似这个[问题](http://stackoverflow.com/questions/24524162/pexpect-runs-failed-when-use-multiprocessing),如果要在单核CPU服务器上运行,请将pexpect降为2.4版本 149 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! 5 | VAGRANTFILE_API_VERSION = "2" 6 | 7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 8 | config.vm.box = "base" 9 | 10 | config.vm.network "private_network", ip: "10.2.3.4" 11 | 12 | config.vm.provision "ansible" do |ansible| 13 | ansible.playbook = "playbook.yml" 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /ansible-conf/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | host_key_checking = False 3 | forks = 100 4 | -------------------------------------------------------------------------------- /ansible.wsgi: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | import sys 4 | import site 5 | 6 | site.addsitedir('/works/envansible/lib64/python2.6/site-packages') 7 | site.addsitedir('/works/envansible/lib/python2.6/site-packages/') 8 | 9 | activate_env = os.path.expanduser('/works/envansible/bin/activate_this.py') 10 | execfile(activate_env, dict(__file__ = activate_env)) 11 | 12 | default_encoding = 'utf-8' 13 | if sys.getdefaultencoding() != default_encoding: 14 | reload(sys) 15 | sys.setdefaultencoding(default_encoding) 16 | 17 | current_dir = os.path.dirname(__file__) 18 | if current_dir not in sys.path: sys.path.append(current_dir) 19 | os.environ['DJANGO_SETTINGS_MODULE'] = 'desktop.core.settings' 20 | 21 | 22 | import djcelery 23 | djcelery.setup_loader() 24 | 25 | import django.core.handlers.wsgi 26 | application = django.core.handlers.wsgi.WSGIHandler() 27 | -------------------------------------------------------------------------------- /ansible_uwsgi.ini: -------------------------------------------------------------------------------- 1 | # mysite_uwsgi.ini file 2 | [uwsgi] 3 | 4 | # Django-related settings 5 | # the virtualenv (full path) 6 | home = /works/envansible 7 | # the base directory (full path) 8 | chdir = /works/ansible_ui 9 | # Django's wsgi file 10 | wsgi-file = ansible.wsgi 11 | 12 | # process-related settings 13 | # master 14 | master = true 15 | # maximum number of worker processes 16 | processes = 1 17 | # the socket (use the full path to be safe 18 | socket = localhost:9001 19 | # ... with appropriate permissions - may be needed 20 | # chmod-socket = 664 21 | # clear environment on exit 22 | vacuum = true 23 | 24 | daemonize = localhost:8077 25 | -------------------------------------------------------------------------------- /apache-conf/ansible.conf: -------------------------------------------------------------------------------- 1 | LoadModule wsgi_module modules/mod_wsgi.so 2 | 3 | 4 | ServerName your.domain.name 5 | DocumentRoot ansible_ui_dir 6 | ErrorLog logs/ansibleui-error_log 7 | CustomLog logs/ansibleui-access_log common 8 | WSGIScriptAlias / "/ansible_ui_dir/django.wsgi" 9 | 10 | WSGIDaemonProcess ansible user=ansible group=ansible processes=2 threads=20 maximum-requests=1000 display-name="%{GROUP}" 11 | WSGIProcessGroup ansible 12 | 13 | Alias "/static" "ansible_ui_dir/desktop/core/static" 14 | 15 | 16 | SetHandler None 17 | 18 | 19 | 20 | Order Deny,Allow 21 | Allow from all 22 | 23 | 24 | -------------------------------------------------------------------------------- /desktop/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'liyang1' 2 | -------------------------------------------------------------------------------- /desktop/apps/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'liyang1' 2 | -------------------------------------------------------------------------------- /desktop/apps/account/__init__.py: -------------------------------------------------------------------------------- 1 | ''' init for account app''' 2 | __author__ = 'liyang1' 3 | -------------------------------------------------------------------------------- /desktop/apps/account/admin.py: -------------------------------------------------------------------------------- 1 | ''' 2 | this module register profile of user to admin 3 | ''' 4 | #!/usr/bin/python 5 | # -*- coding : utf-8 -*- 6 | 7 | from django.contrib import admin 8 | from desktop.apps.account.models import Profile 9 | 10 | class ProfileAdmin(admin.ModelAdmin): 11 | """docstring for AuthorAdmin""" 12 | list_display = ('user', 'cn_name', 'ssh_password', 'ssh_key', 'phonenum') 13 | 14 | 15 | admin.site.register(Profile, ProfileAdmin) 16 | -------------------------------------------------------------------------------- /desktop/apps/account/crypt.py: -------------------------------------------------------------------------------- 1 | def AESencrypt(password, plaintext, base64=False): 2 | import hashlib, os 3 | from Crypto.Cipher import AES 4 | SALT_LENGTH = 32 5 | DERIVATION_ROUNDS=1337 6 | BLOCK_SIZE = 16 7 | KEY_SIZE = 32 8 | MODE = AES.MODE_CBC 9 | 10 | salt = os.urandom(SALT_LENGTH) 11 | iv = os.urandom(BLOCK_SIZE) 12 | 13 | paddingLength = 16 - (len(plaintext) % 16) 14 | paddedPlaintext = plaintext+chr(paddingLength)*paddingLength 15 | derivedKey = password 16 | for i in range(0,DERIVATION_ROUNDS): 17 | derivedKey = hashlib.sha256(derivedKey+salt).digest() 18 | derivedKey = derivedKey[:KEY_SIZE] 19 | cipherSpec = AES.new(derivedKey, MODE, iv) 20 | ciphertext = cipherSpec.encrypt(paddedPlaintext) 21 | ciphertext = ciphertext + iv + salt 22 | if base64: 23 | import base64 24 | return base64.b64encode(ciphertext) 25 | else: 26 | return ciphertext.encode("hex") 27 | 28 | def AESdecrypt(password, ciphertext, base64=False): 29 | import hashlib 30 | from Crypto.Cipher import AES 31 | SALT_LENGTH = 32 32 | DERIVATION_ROUNDS=1337 33 | BLOCK_SIZE = 16 34 | KEY_SIZE = 32 35 | MODE = AES.MODE_CBC 36 | 37 | if base64: 38 | import base64 39 | decodedCiphertext = base64.b64decode(ciphertext) 40 | else: 41 | decodedCiphertext = ciphertext.decode("hex") 42 | startIv = len(decodedCiphertext)-BLOCK_SIZE-SALT_LENGTH 43 | startSalt = len(decodedCiphertext)-SALT_LENGTH 44 | data, iv, salt = decodedCiphertext[:startIv], decodedCiphertext[startIv:startSalt], decodedCiphertext[startSalt:] 45 | derivedKey = password 46 | for i in range(0, DERIVATION_ROUNDS): 47 | derivedKey = hashlib.sha256(derivedKey+salt).digest() 48 | derivedKey = derivedKey[:KEY_SIZE] 49 | cipherSpec = AES.new(derivedKey, MODE, iv) 50 | plaintextWithPadding = cipherSpec.decrypt(data) 51 | paddingLength = ord(plaintextWithPadding[-1]) 52 | plaintext = plaintextWithPadding[:-paddingLength] 53 | return plaintext 54 | 55 | #a = AESencrypt("password", "ABC") 56 | #print a 57 | #print AESdecrypt("password", a) 58 | -------------------------------------------------------------------------------- /desktop/apps/account/models.py: -------------------------------------------------------------------------------- 1 | ''' 2 | models for ansible app 3 | ''' 4 | from django.db import models 5 | #from django.conf import settings 6 | from django.contrib.auth.models import User 7 | #from django.db.models.signals import post_save 8 | from django.utils.translation import ugettext_lazy as _ 9 | 10 | class Profile(models.Model): 11 | ''' User's Profile''' 12 | user = models.ForeignKey(User, unique=True, verbose_name=_('user')) 13 | cn_name = models.CharField(_('cn_name'), 14 | max_length=50, 15 | null=True, 16 | blank=True) 17 | ssh_password = models.CharField(_('ssh_password'), 18 | max_length=256, 19 | null=True, 20 | blank=True) 21 | ssh_key = models.CharField(_('ssh_key'), 22 | max_length=2048, 23 | null=True, 24 | blank=True) 25 | phonenum = models.CharField('phonenum', 26 | max_length=50, 27 | null=True, 28 | blank=True) 29 | 30 | def __unicode__(self): 31 | return self.user.username 32 | 33 | def get_absolute_url(self): 34 | ''' get absolute url''' 35 | return ('profile_detail', None, {'username': self.user.username}) 36 | get_absolute_url = models.permalink(get_absolute_url) 37 | 38 | class Meta: 39 | verbose_name = _('profile') 40 | verbose_name_plural = _('profiles') 41 | 42 | 43 | #def create_profile(sender, instance=None, **kwargs): 44 | # if instance is None: 45 | # return 46 | # profile, created = Profile.objects.get_or_create(user=instance) 47 | 48 | #post_save.connect(create_profile, sender=User) 49 | -------------------------------------------------------------------------------- /desktop/apps/account/urls.py: -------------------------------------------------------------------------------- 1 | ''' 2 | urls for ansible app 3 | ''' 4 | from django.conf.urls import patterns, url 5 | from desktop.core.lib.django_util import get_name_re_rule 6 | 7 | name_re = get_name_re_rule() 8 | 9 | 10 | urlpatterns = patterns('desktop.apps.account.views', 11 | url(r'^$', 'list_users'), 12 | url(r'^users$', 'list_users'), 13 | url(r'^users/profile/(?P%s)$' % (name_re,), 14 | 'profile',name='profile'), 15 | url(r'^users/credential/(?P%s)$' % (name_re,), 16 | 'credential',name='credential'), 17 | url(r'^users/delete/(?P%s)$' % (name_re,), 18 | 'delete_user',name='delete_user'), 19 | url(r'^users/add_ldap_users$', 'add_ldap_users',name='add_ldap_users'), 20 | url(r'^users/sync_ldap_users_groups$', 21 | 'sync_ldap_users_groups',name='sync_ldap_users_groups'), 22 | 23 | ) 24 | 25 | -------------------------------------------------------------------------------- /desktop/apps/account/views.py: -------------------------------------------------------------------------------- 1 | ''' 2 | modules for account app 3 | ''' 4 | try: 5 | import json 6 | except: 7 | import simplejson as json 8 | 9 | from django.contrib.auth.models import User, Group 10 | from desktop.core.lib.django_util import render 11 | from desktop.core.lib.exceptions_renderable import PopupException 12 | from desktop.apps.account.models import Profile 13 | from desktop.apps.account.crypt import AESencrypt 14 | 15 | def list_users(request): 16 | ''' list users ''' 17 | users_json = json.dumps(list(User.objects.values_list('id', flat=True))) 18 | return render("account/list_users.html", request ,{ 19 | 'login_user':request.user, 20 | 'users': User.objects.all(), 21 | 'users_json': users_json, 22 | 'request': request, 23 | }) 24 | 25 | def delete_user(request, username): 26 | ''' delete user ''' 27 | return render("account/list_users.html",request,{}) 28 | 29 | 30 | def profile(request, username): 31 | ''' user's profile ''' 32 | view_user = User.objects.get(username = username) 33 | profile = None 34 | if request.method == 'POST': 35 | cn_name = request.POST.get('cn') 36 | phonenum = request.POST.get('phone') 37 | profile = Profile.objects.get_or_create(user=request.user)[0] 38 | profile.cn_name = cn_name 39 | profile.phonenum = phonenum 40 | profile.save() 41 | try: 42 | profile = Profile.objects.get(user=request.user) 43 | except: 44 | profile = None 45 | if profile == None: 46 | try: 47 | profile = Profile.objects.get(user=view_user) 48 | except: 49 | pass 50 | return render("account/profile.html", request, { 51 | 'login_user':request.user, 52 | 'username':username, 53 | 'view_user':view_user, 54 | 'profile':profile, 55 | }) 56 | 57 | def credential(request, username): 58 | ''' user's credential , for ansible jobs ''' 59 | user = User.objects.get(username = username) 60 | if (request.user != user): 61 | raise PopupException('Sorry, you have not right to see the cretential!') 62 | 63 | if request.method == 'POST': 64 | ssh_password = request.POST.get('ssh_password') 65 | ssh_key = request.POST.get('id_dsa_pub') 66 | profile = Profile.objects.get_or_create(user=request.user)[0] 67 | if ssh_password == '': 68 | profile.ssh_password='' 69 | else: 70 | profile.ssh_password = AESencrypt("pass34",ssh_password) 71 | profile.ssh_key = ssh_key 72 | profile.save() 73 | 74 | try: 75 | profile = Profile.objects.get(user=request.user) 76 | except: 77 | profile = None 78 | return render("account/credential.html", request, { 79 | 'username':username, 80 | 'profile':profile, 81 | }) 82 | 83 | def add_ldap_users(request): 84 | return render("account/list_users.html", request, {}) 85 | 86 | def sync_ldap_users_groups(request): 87 | return render("account/list_users.html", request, {}) 88 | -------------------------------------------------------------------------------- /desktop/apps/ansible/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/__init__.py -------------------------------------------------------------------------------- /desktop/apps/ansible/admin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding : utf-8 -*- 3 | 4 | from django.contrib import admin 5 | from desktop.apps.ansible.models import * 6 | from guardian.admin import GuardedModelAdmin 7 | 8 | class PackageAdmin(admin.ModelAdmin): 9 | """docstring for packageAdmin""" 10 | list_display = ('id','scmurl','version','date','project') 11 | search_fields = ('project',) 12 | 13 | class ProjectAdmin(GuardedModelAdmin): 14 | """docstring for AuthorAdmin""" 15 | list_display = ('name','description','created_by','creation_date','active') 16 | search_fields = ('name',) 17 | 18 | class JobAdmin(admin.ModelAdmin): 19 | """ docstring for JobAdmin """ 20 | list_display = ('name','description','created_by','creation_date','project','status','execute_date') 21 | search_fields = ('project', 'status') 22 | 23 | admin.site.register(Project,ProjectAdmin) 24 | admin.site.register(Package,PackageAdmin) 25 | admin.site.register(Job,JobAdmin) 26 | -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.91.00-rc1' -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/conf/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/elfinder/conf/__init__.py -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/exceptions.py: -------------------------------------------------------------------------------- 1 | from django.utils.translation import ugettext as _ 2 | 3 | class ElfinderErrorMessages: 4 | """ 5 | Standard error message codes, the text message of which is handled by the 6 | elFinder client 7 | """ 8 | 9 | ERROR_UNKNOWN = 'errUnknown' 10 | ERROR_UNKNOWN_CMD = 'errUnknownCmd' 11 | ERROR_CONF = 'errConf' 12 | ERROR_CONF_NO_JSON = 'errJSON' 13 | ERROR_CONF_NO_VOL = 'errNoVolumes' 14 | ERROR_INV_PARAMS = 'errCmdParams' 15 | ERROR_OPEN = 'errOpen' 16 | ERROR_DIR_NOT_FOUND = 'errFolderNotFound' 17 | ERROR_FILE_NOT_FOUND = 'errFileNotFound' #'File not found.' 18 | ERROR_TRGDIR_NOT_FOUND = 'errTrgFolderNotFound' #'Target folder "$1" not found.' 19 | ERROR_NOT_DIR = 'errNotFolder' 20 | ERROR_NOT_FILE = 'errNotFile' 21 | ERROR_PERM_DENIED = 'errPerm' 22 | ERROR_LOCKED = 'errLocked' #'"$1" is locked and can not be renamed, moved or removed.' 23 | ERROR_EXISTS = 'errExists' #'File named "$1" already exists.' 24 | ERROR_INVALID_NAME = 'errInvName' #'Invalid file name.' 25 | ERROR_MKDIR = 'errMkdir' 26 | ERROR_MKFILE = 'errMkfile' 27 | ERROR_RENAME = 'errRename' 28 | ERROR_COPY = 'errCopy' 29 | ERROR_MOVE = 'errMove' 30 | ERROR_COPY_FROM = 'errCopyFrom' 31 | ERROR_COPY_TO = 'errCopyTo' 32 | ERROR_COPY_ITSELF = 'errCopyInItself' 33 | ERROR_REPLACE = 'errReplace' #'Unable to replace "$1".' 34 | ERROR_RM = 'errRm' #'Unable to remove "$1".' 35 | ERROR_RM_SRC = 'errRmSrc' #'Unable remove source file(s)' 36 | ERROR_UPLOAD = 'errUpload' #'Upload error.' 37 | ERROR_UPLOAD_FILE = 'errUploadFile' #'Unable to upload "$1".' 38 | ERROR_UPLOAD_NO_FILES = 'errUploadNoFiles' #'No files found for upload.' 39 | ERROR_UPLOAD_TOTAL_SIZE = 'errUploadTotalSize' #'Data exceeds the maximum allowed size.' 40 | ERROR_UPLOAD_FILE_SIZE = 'errUploadFileSize' #'File exceeds maximum allowed size.' 41 | ERROR_UPLOAD_FILE_MIME = 'errUploadMime' #'File type not allowed.' 42 | ERROR_UPLOAD_TRANSFER = 'errUploadTransfer' #'"$1" transfer error.' 43 | ERROR_ACCESS_DENIED = 'errAccess' 44 | ERROR_NOT_REPLACE = 'errNotReplace' #Object "$1" already exists at this location and can not be replaced with object of another type. 45 | ERROR_SAVE = 'errSave' 46 | ERROR_EXTRACT = 'errExtract' 47 | ERROR_ARCHIVE = 'errArchive' 48 | ERROR_NOT_ARCHIVE = 'errNoArchive' 49 | ERROR_ARCHIVE_TYPE = 'errArcType' 50 | ERROR_ARC_SYMLINKS = 'errArcSymlinks' 51 | ERROR_ARC_MAXSIZE = 'errArcMaxSize' 52 | ERROR_RESIZE = 'errResize' 53 | ERROR_UNSUPPORT_TYPE = 'errUsupportType' 54 | ERROR_NOT_UTF8_CONTENT = 'errNotUTF8Content' 55 | ERROR_NETMOUNT = 'errNetMount' 56 | ERROR_NETMOUNT_NO_DRIVER = 'errNetMountNoDriver' 57 | ERROR_NETMOUNT_FAILED = 'errNetMountFailed' 58 | 59 | class VolumeNotFoundError(Exception): 60 | def __init__(self): 61 | super(VolumeNotFoundError, self).__init__(_("Volume could not be found")) 62 | 63 | class FileNotFoundError(Exception): 64 | def __init__(self): 65 | super(FileNotFoundError, self).__init__(ElfinderErrorMessages.ERROR_FILE_NOT_FOUND) 66 | 67 | class DirNotFoundError(Exception): 68 | def __init__(self): 69 | super(DirNotFoundError, self).__init__(ElfinderErrorMessages.ERROR_DIR_NOT_FOUND) 70 | 71 | class PermissionDeniedError(Exception): 72 | def __init__(self): 73 | super(PermissionDeniedError, self).__init__(ElfinderErrorMessages.ERROR_PERM_DENIED) 74 | 75 | class NamedError(Exception): 76 | """ 77 | Elfinder-specific exception. 78 | `msg` contains the error code 79 | `name` holds the path for which operation failed 80 | """ 81 | def __init__(self, msg, name): 82 | self.name = name 83 | super(NamedError, self).__init__(msg) 84 | 85 | class NotAnImageError(Exception): 86 | def __init__(self): 87 | super(NotAnImageError, self).__init__(_('This is not a valid image file')) -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/locale/el/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/elfinder/locale/el/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/locale/el/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2013-06-08 22:56+0300\n" 11 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 12 | "Last-Translator: FULL NAME \n" 13 | "Language-Team: LANGUAGE \n" 14 | "Language: \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 19 | 20 | #: connector.py:336 connector.py:344 21 | msgid "File not found" 22 | msgstr "Δε βρέθηκε το αρχείο" 23 | 24 | #: connector.py:339 25 | msgid "Access denied" 26 | msgstr "Απαγορεύεται η πρόσβαση" 27 | 28 | #: exceptions.py:61 29 | msgid "Volume could not be found" 30 | msgstr "Δε βρέθηκε ο δίσκος" 31 | 32 | #: exceptions.py:87 33 | msgid "This is not a valid image file" 34 | msgstr "Μη έγκυρο αρχείο εικόνας" 35 | 36 | #: fields.py:50 37 | msgid "This file is no longer valid" 38 | msgstr "Αυτό το αρχείο δεν είναι πια έγκυρο" 39 | 40 | #: widgets.py:93 41 | msgid "Size" 42 | msgstr "Μέγεθος" 43 | 44 | #: widgets.py:94 45 | msgid "Path" 46 | msgstr "Διαδρομή" 47 | 48 | #: widgets.py:95 49 | msgid "Link" 50 | msgstr "Σύνδεσμος" 51 | 52 | #: widgets.py:96 53 | msgid "Modified" 54 | msgstr "Τροποποίηση" 55 | 56 | #: widgets.py:97 57 | msgid "Dimensions" 58 | msgstr "Διαστάσεις" 59 | 60 | #: widgets.py:98 61 | msgid "Update" 62 | msgstr "Αλλαγή" 63 | 64 | #: widgets.py:99 65 | msgid "Set" 66 | msgstr "Ορισμός" 67 | 68 | #: widgets.py:100 69 | msgid "Clear" 70 | msgstr "Εκκαθάριση" 71 | 72 | #: volumes/base.py:192 73 | msgid "No volume id found" 74 | msgstr "Δε βρέθηκε το volume id" 75 | 76 | #: volumes/base.py:1189 77 | msgid "Unknown" 78 | msgstr "Άγνωστο" 79 | -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/models.py: -------------------------------------------------------------------------------- 1 | #Empty required models file -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/static/css/jquery.elfinder-widget.full.css: -------------------------------------------------------------------------------- 1 | .elfinder-widget { 2 | float: left; 3 | } 4 | 5 | .elfinder-wrapper { 6 | background-color: #444; 7 | background-color: rgba(32, 32, 32, .7); 8 | display: none; 9 | height: 100%; 10 | left: 0; 11 | position: fixed; 12 | top: 0; 13 | width: 100%; 14 | z-index: 2000; 15 | } 16 | 17 | .elfinder-widget .elfinder-info-title { 18 | margin: 0 0 20px 0; 19 | } 20 | 21 | .elfinder-close { 22 | background: url(../images/close-button.png) 100% 0 no-repeat; 23 | cursor: pointer; 24 | float: right; 25 | height: 32px; 26 | margin: 15px 10px 0 0; 27 | max-width: 1260px; 28 | width: 32px; 29 | } 30 | 31 | .elfinder-root { 32 | font-size: 16px; 33 | margin: 15px auto 0 auto; 34 | max-width: 1260px; 35 | z-index: 2100; 36 | } 37 | 38 | .tmb { 39 | background-position: 50% 50%; 40 | background-repeat: no-repeat no-repeat; 41 | } 42 | 43 | .clear { 44 | clear: both; 45 | } -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/static/elfinder/css/theme.css: -------------------------------------------------------------------------------- 1 | /** 2 | * MacOS X like theme for elFinder. 3 | * Required jquery ui "smoothness" theme. 4 | * 5 | * @author Dmitry (dio) Levashov 6 | **/ 7 | 8 | /* dialogs */ 9 | .std42-dialog, .std42-dialog .ui-widget-content { background-color:#ededed; background-image:none; background-clip: content-box; } 10 | 11 | /* navbar */ 12 | .elfinder .elfinder-navbar { background:#dde4eb; } 13 | .elfinder-navbar .ui-state-hover { background:transparent; border-color:transparent; } 14 | .elfinder-navbar .ui-state-active { background: #3875d7; border-color:#3875d7; color:#fff; } 15 | .elfinder-navbar .elfinder-droppable-active {background:#A7C6E5 !important;} 16 | /* disabled elfinder */ 17 | .elfinder-disabled .elfinder-navbar .ui-state-active { background: #dadada; border-color:#aaa; color:#fff; } 18 | 19 | 20 | /* current directory */ 21 | /* selected file in "icons" view */ 22 | .elfinder-cwd-view-icons .elfinder-cwd-file .ui-state-hover { background:#ccc; } 23 | /* list view*/ 24 | .elfinder-cwd table tr:nth-child(odd) { background-color:#edf3fe; } 25 | .elfinder-cwd table tr { border-top:1px solid #fff; } 26 | 27 | /* common selected background/color */ 28 | .elfinder-cwd-view-icons .elfinder-cwd-file .elfinder-cwd-filename.ui-state-hover, 29 | .elfinder-cwd table td.ui-state-hover, 30 | .elfinder-button-menu .ui-state-hover { background: #3875d7; color:#fff;} 31 | /* disabled elfinder */ 32 | .elfinder-disabled .elfinder-cwd-view-icons .elfinder-cwd-file .elfinder-cwd-filename.ui-state-hover, 33 | .elfinder-disabled .elfinder-cwd table td.ui-state-hover { background:#dadada;} 34 | 35 | /* statusbar */ 36 | .elfinder .elfinder-statusbar { color:#555; } 37 | .elfinder .elfinder-statusbar a { text-decoration:none; color:#555;} 38 | 39 | 40 | .std42-dialog .elfinder-help, .std42-dialog .elfinder-help .ui-widget-content { background:#fff;} 41 | 42 | /* contextmenu */ 43 | .elfinder-contextmenu .ui-state-hover { background: #3875d7; color:#fff; } 44 | .elfinder-contextmenu .ui-state-hover .elfinder-contextmenu-arrow { background-image:url('../img/arrows-active.png'); } 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/static/elfinder/img/arrows-active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/elfinder/static/elfinder/img/arrows-active.png -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/static/elfinder/img/arrows-normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/elfinder/static/elfinder/img/arrows-normal.png -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/static/elfinder/img/crop.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/elfinder/static/elfinder/img/crop.gif -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/static/elfinder/img/dialogs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/elfinder/static/elfinder/img/dialogs.png -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/static/elfinder/img/icons-big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/elfinder/static/elfinder/img/icons-big.png -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/static/elfinder/img/icons-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/elfinder/static/elfinder/img/icons-small.png -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/static/elfinder/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/elfinder/static/elfinder/img/logo.png -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/static/elfinder/img/progress.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/elfinder/static/elfinder/img/progress.gif -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/static/elfinder/img/quicklook-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/elfinder/static/elfinder/img/quicklook-bg.png -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/static/elfinder/img/quicklook-icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/elfinder/static/elfinder/img/quicklook-icons.png -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/static/elfinder/img/resize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/elfinder/static/elfinder/img/resize.png -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/static/elfinder/img/spinner-mini.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/elfinder/static/elfinder/img/spinner-mini.gif -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/static/elfinder/img/toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/elfinder/static/elfinder/img/toolbar.png -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/static/elfinder/sounds/rm.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/elfinder/static/elfinder/sounds/rm.wav -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/static/images/close-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/elfinder/static/images/close-button.png -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/tests/__init__.py: -------------------------------------------------------------------------------- 1 | from connector import * 2 | from volumes import * -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/tests/media/files/2bytes.txt: -------------------------------------------------------------------------------- 1 | 01 -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/tests/media/files/directory/yawd-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/elfinder/tests/media/files/directory/yawd-logo.png -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | from django.contrib.admin.views.decorators import staff_member_required 3 | from views import ElfinderConnectorView 4 | 5 | urlpatterns = patterns('', 6 | url(r'^yawd-connector/(?P.+)/(?P.+)/$', 7 | staff_member_required(ElfinderConnectorView.as_view()), 8 | name='yawdElfinderConnectorView') 9 | ) -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/elfinder/utils/__init__.py -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/utils/accesscontrol.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def fs_standard_access(attr, path, volume): 5 | """ 6 | Make dotfiles not readable, not writable, hidden and locked. 7 | Should return None to allow for original attribute value, boolean otherwise. 8 | This can be used in the :ref:`setting-accessControl` setting. 9 | 10 | Args: 11 | :attr: One of `read`, `write`, `hidden` and `locked`. 12 | :path: The path to check against. 13 | :volume: The volume responsible for managing the path. 14 | 15 | Returns: 16 | ``True`` if `path` has `attr` permissions, ``False`` if not and 17 | ``None`` to apply the default permission rules. 18 | """ 19 | 20 | if os.path.basename(path) in ['.tmb', '.quarantine']: 21 | #keep reserved folder names intact 22 | return None 23 | 24 | if volume.name() == 'localfilesystem': 25 | if attr in ['read', 'write'] and os.path.basename(path).startswith('.'): 26 | return False 27 | elif attr in ['hidden', 'locked'] and os.path.basename(path).startswith('.'): 28 | return True 29 | -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/utils/archivers.py: -------------------------------------------------------------------------------- 1 | from zipfile import ZipFile 2 | 3 | 4 | class ZipFileArchiver(object): 5 | """ 6 | An archiver used to generate .zip files. 7 | This wraps Python's built in :class:`zipfile.ZipFile` 8 | methods to operate exactly like :class:`tarfile.TarFile` does. 9 | """ 10 | 11 | def __init__(self, *args, **kwargs): 12 | """ 13 | Create a :class:`.ZipFileArchiver` instance. We create a new 14 | :class:`zipfile.ZipFile` and store it to the ``zipfile`` member. 15 | """ 16 | self.zipfile = ZipFile(*args, **kwargs) 17 | 18 | @classmethod 19 | def open(self, *args, **kwargs): 20 | """ 21 | Open the archive. This must be a classmethod. 22 | """ 23 | return ZipFileArchiver(*args,**kwargs) 24 | 25 | def add(self, *args, **kwargs): 26 | """ 27 | Add file to the archive. 28 | """ 29 | self.zipfile.write(*args, **kwargs) 30 | 31 | def extractall(self, *args, **kwargs): 32 | """ 33 | Extract all files from the archive. 34 | """ 35 | self.zipfile.extractall(*args, **kwargs) 36 | 37 | def close(self): 38 | """ 39 | Close the archive. 40 | """ 41 | self.zipfile.close() 42 | -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/utils/volumes.py: -------------------------------------------------------------------------------- 1 | from django.utils.importlib import import_module 2 | from desktop.apps.ansible.elfinder.conf import settings as ls 3 | 4 | 5 | def get_path_driver(hash_, optionset): 6 | """ 7 | Given an ``optionset`` and a path ``hash_`` this function returns 8 | a mounted volume driver for this path. 9 | 10 | This method assumes that the driver uses the default driver 11 | :func:`elfinder.volumes.base.ElfinderVolumeDriver.id` implementation 12 | to generate its id. 13 | """ 14 | for root_options in ls.ELFINDER_CONNECTOR_OPTION_SETS[optionset]['roots']: 15 | if 'driver' in root_options: 16 | if hash_.startswith('%s%s_' % (root_options['driver']._driver_id, root_options['id'])): 17 | return instantiate_driver(root_options) 18 | 19 | 20 | def instantiate_driver(root_options): 21 | """ 22 | Instantiate and return a driver, given its ``root_options``. 23 | """ 24 | class_ = root_options['driver'] if 'driver' in root_options else '' 25 | 26 | if 'driverInstance' in root_options and isinstance(root_options['driverInstance'], class_): 27 | return root_options['driverInstance'] 28 | 29 | if isinstance(class_, basestring) and class_: 30 | try: 31 | split = class_.split('.') 32 | storage_module = import_module('.'.join(split[:-1])) 33 | volume = getattr(storage_module, split[-1])() 34 | except: 35 | raise Exception('Could not import driver "%s"' % class_) 36 | else: 37 | try: 38 | volume = class_() 39 | except TypeError: 40 | raise Exception('Driver "%s" does not exist' % class_) 41 | 42 | try: 43 | volume.mount(root_options) 44 | except Exception as e: 45 | raise Exception('Driver "%s" " %s' % (class_, e)) 46 | 47 | #store driver instance in memory, if the 'keepAlive' option is set 48 | if 'keepAlive' in root_options and root_options['keepAlive']: 49 | root_options['driverInstance'] = volume 50 | 51 | return volume 52 | -------------------------------------------------------------------------------- /desktop/apps/ansible/elfinder/volumes/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/apps/ansible/elfinder/volumes/__init__.py -------------------------------------------------------------------------------- /desktop/apps/ansible/job_run_test.py: -------------------------------------------------------------------------------- 1 | from desktop.apps.ansible.models import * 2 | from desktop.apps.ansible.path_utils import * 3 | import random 4 | import cStringIO 5 | import logging 6 | import os 7 | import select 8 | import subprocess 9 | import time 10 | import traceback 11 | from celery import Task 12 | from django.conf import settings 13 | import pexpect 14 | from desktop.apps.ansible.tasks import * 15 | 16 | #job_pk=init_job() 17 | #start_job(job_pk) 18 | 19 | def init_project(name): 20 | project=Project(name=name) 21 | project.save() 22 | #ensure_project_dir(project.name) 23 | 24 | def init_job(): 25 | project_name="demo" 26 | 27 | project =Project.objects.get(name=project_name) 28 | playbook= get_playbook_local_path(project_name,"echo.yml") 29 | inventory =get_inventory_local_path(project_name,"hosts") 30 | 31 | print playbook 32 | 33 | print inventory 34 | 35 | 36 | 37 | 38 | job=Job() 39 | job.name=random.random() 40 | job.project=project 41 | job.inventory=inventory 42 | job.playbook=playbook 43 | job.save() 44 | 45 | return job.pk 46 | 47 | def start_job(job_pk): 48 | job=Job.objects.get(pk=job_pk) 49 | job.start() 50 | # run_job=RunJob() 51 | # run_job.run(job_pk) 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /desktop/apps/ansible/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url 2 | from desktop.core.lib.django_util import get_name_re_rule,get_id_re_rule 3 | from desktop.apps.ansible.elfinder.views import ElfinderConnectorView 4 | name_re = get_name_re_rule() 5 | id_re=get_id_re_rule() 6 | 7 | urlpatterns = patterns('', 8 | url(r'^(?P.+)/connector/(?P.+)/(?P.+)/$',ElfinderConnectorView.as_view(),name='ElfinderConnector') 9 | ) 10 | 11 | urlpatterns += patterns('desktop.apps.ansible.views', 12 | 13 | url(r'^(?P%s)/template/save$' % name_re,'save_template',name='save_template'), 14 | url(r'^(?P%s)/template/delete' % name_re,'delete_template',name='delete_template'), 15 | 16 | # projects vice 17 | url(r'^$','list_projects',name='list_projects'), 18 | url(r'^list_projects/group/(?P%s)' % name_re, 'list_projects_group', name='list_projects_group'), 19 | url(r'^add$','add_project', name='add_project'), 20 | url(r'^(?P%s)/edit$' % id_re ,'edit_project', name='edit_project'), 21 | url(r'^(?P%s)/manage$' % id_re ,'manage_project', name='manage_project'), 22 | #url(r'^hostlist/(?P%s)' % name_re,'view_hostlist',name='view_hostlist'), 23 | url(r'^(?P%s)/files/(?P.+)' % name_re,'elfinder_view_file',name='elfinder_view_file'), 24 | url(r'^(?P%s)/file/(?P%s)/(?P.+)' % (name_re,name_re),'view_file',name='view_file'), 25 | url(r'^(?P%s)/explore' % name_re,'explore',name='explore'), 26 | url(r'^(?P%s)/delete$' % name_re,'delete_project',name='delete_project'), 27 | url(r'^(?P%s)/deletejob$' % name_re,'delete_job',name='delete_job'), 28 | 29 | url(r'^(?P%s)/deploykey' % name_re,'deploykey',name='deploykey'), 30 | url(r'^(?P%s)/schedule' % name_re,'schedule',name='schedule'), 31 | 32 | url(r'^(?P%s)/execute/playbook/(?P%s)$' % (name_re,id_re),'execute_playbook',name='execute_playbook_template'), 33 | url(r'^(?P%s)/execute/playbook$' % name_re,'execute_playbook',name='execute_playbook'), 34 | url(r'^(?P%s)/execute/script$' % name_re,'execute_script',name='execute_script'), 35 | url(r'^(?P%s)/execute/scp$' % name_re,'execute_scp',name='execute_scp'), 36 | 37 | url(r'^(?P%s)/result_json/(?P%s)$' % (name_re,id_re),'result_json',name='job_result_json'), 38 | url(r'^(?P%s)/restart/(?P%s)$' % (name_re,id_re),'restart_job',name='restart_job'), 39 | url(r'^(?P%s)/result/(?P%s)$' % (name_re,id_re),'result',name='job_execute_result'), 40 | url(r'^(?P%s)/logs$' % name_re,'view_project_logs',name='view_project_logs'), 41 | url(r'^(?P%s)/$' % name_re,'view_project',name='view_project'), 42 | 43 | 44 | ) 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /desktop/core/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'liyang1' 2 | -------------------------------------------------------------------------------- /desktop/core/auth/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /desktop/core/auth/views.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Licensed to Cloudera, Inc. under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. Cloudera, Inc. licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | import logging 19 | from django.http import HttpResponseRedirect 20 | import django.contrib.auth.views 21 | from django.core import urlresolvers 22 | from django.contrib.auth import login 23 | from desktop.core.lib.django_util import render,login_not_required 24 | from django.contrib import auth 25 | 26 | 27 | LOG = logging.getLogger(__name__) 28 | 29 | @login_not_required 30 | def dt_login(request): 31 | redirect_to = request.REQUEST.get('next', '/') 32 | if request.method == 'POST': 33 | data=request.POST 34 | if "username" in data and "password" in data: 35 | username=data.get("username") 36 | password=data.get("password") 37 | 38 | if username and password : 39 | user = auth.authenticate( username=username, password=password ) 40 | if user and user.is_active: 41 | login(request, user) 42 | if request.session.test_cookie_worked(): 43 | request.session.delete_test_cookie() 44 | return HttpResponseRedirect(redirect_to) 45 | 46 | else: 47 | request.session.set_test_cookie() 48 | 49 | return render('login.html', request, { 50 | 'action': urlresolvers.reverse('desktop.core.auth.views.dt_login'), 51 | 'next': redirect_to, 52 | 'login_errors': request.method == 'POST', 53 | }) 54 | 55 | 56 | def dt_logout(request, next_page=None): 57 | """Log out the user""" 58 | return django.contrib.auth.views.logout(request, next_page) 59 | -------------------------------------------------------------------------------- /desktop/core/internal/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/core/internal/__init__.py -------------------------------------------------------------------------------- /desktop/core/internal/settings_local.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | 3 | # LDAP settings 4 | NT4_DOMAIN = "" 5 | LDAP_URL = "ldap://ldapserver:port" 6 | BIND_USER = "CN=adreader,OU=xxx,OU=xxx,DC=xxx,DC=xxxx" 7 | BIND_PASSWORD = "*****" 8 | SEARCH_DN = "ou=xxxx,dc=xxxx,dc=xxxx" 9 | 10 | # Database settings 11 | DATABASES = { 12 | 'default': { 13 | 'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. 14 | 'NAME': 'ansible', # Or path to database file if using sqlite3. 15 | 'USER': 'ansibleuser', # Not used with sqlite3. 16 | 'PASSWORD': '****', # Not used with sqlite3. 17 | 'HOST': 'localhost', 18 | 'PORT': '3306', # Set to empty string for default. Not used with sqlite3. 19 | }, 20 | } 21 | 22 | # Mail settings 23 | MAIL_SENDER = "sender@domain.com" 24 | MAIL_SMTP = "mail.domain.com" 25 | 26 | # Ansible-playbook path 27 | ANSIBLE_PLAYBOOK = '/envansible_dir/bin/ansible-playbook' 28 | -------------------------------------------------------------------------------- /desktop/core/lib/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /desktop/core/lib/django_util.py: -------------------------------------------------------------------------------- 1 | import simplejson 2 | 3 | from django.conf import settings 4 | from django.http import HttpResponse 5 | from django.shortcuts import render_to_response as django_render_to_response 6 | from django.template import RequestContext 7 | 8 | 9 | NAME_RE_RULE = "[^-:\s][^:\s]*" 10 | DESC_RE_RULE = "[\w-]+" 11 | ID_RE_RULE = "[\d-]+" 12 | 13 | def get_name_re_rule(): 14 | return NAME_RE_RULE 15 | 16 | def get_desc_re_rule(): 17 | return DESC_RE_RULE 18 | 19 | def get_id_re_rule(): 20 | return ID_RE_RULE 21 | 22 | 23 | def login_not_required(func): 24 | func.login_not_required = True 25 | return func 26 | 27 | def render(template, request, data, **kwargs): 28 | return django_render_to_response(template,RequestContext(request=request, dict_=data), **kwargs) 29 | 30 | def render_json(data): 31 | if settings.DEBUG: 32 | indent = 2 33 | else: 34 | indent = 0 35 | json = simplejson.dumps(data, indent) 36 | 37 | return HttpResponse(json, mimetype='application/json') 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /desktop/core/lib/exceptions.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import traceback 3 | 4 | 5 | 6 | class StructuredException(Exception): 7 | """ 8 | Many exceptions in this application are a string and some data 9 | that applies to. The middleware will take these exceptions 10 | and render them. 11 | """ 12 | def __init__(self, code, message, data=None, error_code=500): 13 | Exception.__init__(self, message) 14 | self.code = code 15 | self.message = message 16 | self.data = data 17 | self.error_code = error_code 18 | 19 | # Traceback is only relevant if an exception was thrown, caught, and we reraise with this exception. 20 | (type, value, tb) = sys.exc_info() 21 | self.traceback = traceback.extract_tb(tb) 22 | 23 | def __str__(self): 24 | return "%s (code %s): %s" % (self.message, self.code, repr(self.data)) 25 | 26 | @property 27 | def response_data(self): 28 | return dict(code=self.code, 29 | message=self.message, 30 | data=self.data, 31 | traceback=self.traceback) 32 | 33 | class MessageException(StructuredException): 34 | """ 35 | Explicitly specified msg/filename exception. 36 | 37 | This has been superceded by PopupException. 38 | """ 39 | def __init__(self, msg, filename=None, error_code=500): 40 | StructuredException.__init__(self, 41 | code="GENERIC_MESSAGE", 42 | message=msg, 43 | data=dict(filename=filename), 44 | error_code=error_code) 45 | 46 | 47 | -------------------------------------------------------------------------------- /desktop/core/lib/exceptions_renderable.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Licensed to Cloudera, Inc. under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. Cloudera, Inc. licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | """ 18 | These methods should never be placed in 'desktop.lib.exceptions'. 19 | This file exists to remove circular reference caused by importing django_util. 20 | """ 21 | 22 | import sys 23 | import traceback 24 | 25 | # Need full import statement 26 | from desktop.core.lib.django_util import render 27 | 28 | class PopupException(Exception): 29 | """ 30 | Middleware will render this exception; and the template 31 | renders it as a pop-up. 32 | """ 33 | def __init__(self, message, title="Error", detail=None, error_code=500): 34 | Exception.__init__(self, message) 35 | self.message = message 36 | self.title = title 37 | self.detail = detail 38 | self.error_code = error_code 39 | 40 | # Traceback is only relevant if an exception was thrown, caught, and we reraise with this exception. 41 | (type, value, tb) = sys.exc_info() 42 | self.traceback = traceback.extract_tb(tb) 43 | 44 | def response(self, request): 45 | data = dict(title=self.title, message=self.message, detail=self.detail, traceback=self.traceback) 46 | if not request.ajax: 47 | data['request'] = request 48 | response = render("popup_error.html", request, data) 49 | response.status_code = self.error_code 50 | return response 51 | -------------------------------------------------------------------------------- /desktop/core/locale/zh_CN/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alaxli/ansible_ui/ea7a76e1de6d2aec3777c0182dd8cc3529c9ccd7/desktop/core/locale/zh_CN/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /desktop/core/middleware.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Licensed to Cloudera, Inc. under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. Cloudera, Inc. licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | import logging 19 | 20 | import django.views.static 21 | import django.views.generic.base 22 | from django.conf import settings 23 | from django.http import HttpResponseRedirect, HttpResponse 24 | from django.utils.http import urlquote 25 | 26 | from desktop.core.lib.django_util import render,render_json 27 | from desktop.core.lib.exceptions import StructuredException 28 | from desktop.core.lib.exceptions_renderable import PopupException 29 | 30 | from django.contrib.auth import REDIRECT_FIELD_NAME 31 | 32 | LOG = logging.getLogger(__name__) 33 | MIDDLE_WARE_HEADER = "X-Middleware-Response" 34 | 35 | # Views inside Django that don't require login 36 | # (see LoginAndPermissionMiddleware) 37 | DJANGO_VIEW_AUTH_WHITE_LIST = [ 38 | django.views.static.serve, 39 | django.views.generic.base.RedirectView, 40 | ] 41 | 42 | 43 | class ExceptionMiddleware(object): 44 | def process_exception(self, request, exception): 45 | 46 | 47 | import traceback 48 | tb = traceback.format_exc() 49 | 50 | if isinstance(exception, PopupException): 51 | return exception.response(request) 52 | 53 | if isinstance(exception, StructuredException): 54 | if request.ajax: 55 | response = render_json(exception.response_data) 56 | response[MIDDLE_WARE_HEADER] = 'EXCEPTION' 57 | response.status_code = getattr(exception, 'error_code', 500) 58 | return response 59 | else: 60 | response = render("error.html", request,dict(error=exception.response_data.get("message"))) 61 | response.status_code = getattr(exception, 'error_code', 500) 62 | return response 63 | 64 | return None 65 | 66 | class AjaxMiddleware(object): 67 | """ 68 | Middleware that augments request to set request.ajax 69 | for either is_ajax() (looks at HTTP headers) or ?format=json 70 | GET parameters. 71 | """ 72 | def process_request(self, request): 73 | request.ajax = request.is_ajax() or request.REQUEST.get("format", "") == "json" 74 | return None 75 | 76 | class LoginAndPermissionMiddleware(object): 77 | def process_view(self, request, view_func, view_args, view_kwargs): 78 | 79 | # If the view has "opted out" of login required, skip 80 | if hasattr(view_func, "login_not_required"): 81 | return None 82 | 83 | # There are certain django views which are also opt-out, but 84 | # it would be evil to go add attributes to them 85 | if view_func in DJANGO_VIEW_AUTH_WHITE_LIST: 86 | return None 87 | 88 | if request.user.is_active and request.user.is_authenticated(): 89 | return None 90 | 91 | if request.ajax: 92 | # Send back a magic header which causes Hue.Request to interpose itself 93 | # in the ajax request and make the user login before resubmitting the 94 | # request. 95 | response = HttpResponse("/* login required */", content_type="text/javascript") 96 | response[MIDDLE_WARE_HEADER] = 'LOGIN_REQUIRED' 97 | return response 98 | else: 99 | return HttpResponseRedirect("%s?%s=%s" % (settings.LOGIN_URL, REDIRECT_FIELD_NAME, urlquote(request.get_full_path()))) 100 | 101 | 102 | -------------------------------------------------------------------------------- /desktop/core/pagination/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /desktop/core/pagination/middleware.py: -------------------------------------------------------------------------------- 1 | def get_page(self): 2 | """ 3 | A function which will be monkeypatched onto the request to get the current 4 | integer representing the current page. 5 | """ 6 | try: 7 | return int(self.REQUEST['page']) 8 | except (KeyError, ValueError, TypeError): 9 | return 1 10 | 11 | class PaginationMiddleware(object): 12 | """ 13 | Inserts a variable representing the current page onto the request object if 14 | it exists in either **GET** or **POST** portions of the request. 15 | """ 16 | def process_request(self, request): 17 | request.__class__.page = property(get_page) -------------------------------------------------------------------------------- /desktop/core/pagination/models.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /desktop/core/pagination/templates/pagination/pagination.html: -------------------------------------------------------------------------------- 1 | {% if is_paginated %} 2 | {% load i18n %} 3 | 28 | {% endif %} 29 | -------------------------------------------------------------------------------- /desktop/core/pagination/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /desktop/core/pagination/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | >>> from django.core.paginator import Paginator 3 | >>> from pagination.templatetags.pagination_tags import paginate 4 | >>> from django.template import Template, Context 5 | 6 | >>> p = Paginator(range(15), 2) 7 | >>> pg = paginate({'paginator': p, 'page_obj': p.page(1)}) 8 | >>> pg['pages'] 9 | [1, 2, 3, 4, 5, 6, 7, 8] 10 | >>> pg['records']['first'] 11 | 1 12 | >>> pg['records']['last'] 13 | 2 14 | 15 | >>> p = Paginator(range(15), 2) 16 | >>> pg = paginate({'paginator': p, 'page_obj': p.page(8)}) 17 | >>> pg['pages'] 18 | [1, 2, 3, 4, 5, 6, 7, 8] 19 | >>> pg['records']['first'] 20 | 15 21 | >>> pg['records']['last'] 22 | 15 23 | 24 | >>> p = Paginator(range(17), 2) 25 | >>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages'] 26 | [1, 2, 3, 4, 5, 6, 7, 8, 9] 27 | 28 | >>> p = Paginator(range(19), 2) 29 | >>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages'] 30 | [1, 2, 3, 4, None, 7, 8, 9, 10] 31 | 32 | >>> p = Paginator(range(21), 2) 33 | >>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages'] 34 | [1, 2, 3, 4, None, 8, 9, 10, 11] 35 | 36 | # Testing orphans 37 | >>> p = Paginator(range(5), 2, 1) 38 | >>> paginate({'paginator': p, 'page_obj': p.page(1)})['pages'] 39 | [1, 2] 40 | 41 | >>> p = Paginator(range(21), 2, 1) 42 | >>> pg = paginate({'paginator': p, 'page_obj': p.page(1)}) 43 | >>> pg['pages'] 44 | [1, 2, 3, 4, None, 7, 8, 9, 10] 45 | >>> pg['records']['first'] 46 | 1 47 | >>> pg['records']['last'] 48 | 2 49 | 50 | >>> p = Paginator(range(21), 2, 1) 51 | >>> pg = paginate({'paginator': p, 'page_obj': p.page(10)}) 52 | >>> pg['pages'] 53 | [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 54 | >>> pg['records']['first'] 55 | 19 56 | >>> pg['records']['last'] 57 | 21 58 | 59 | >>> t = Template("{% load pagination_tags %}{% autopaginate var 2 %}{% paginate %}") 60 | 61 | >>> from django.http import HttpRequest as DjangoHttpRequest 62 | >>> class HttpRequest(DjangoHttpRequest): 63 | ... page = 1 64 | 65 | >>> t.render(Context({'var': range(21), 'request': HttpRequest()})) 66 | u'\\n\\n