Commit 4d96978b authored by ibuler's avatar ibuler

Merge branch 'master' into audits

parents a448fd02 81ec1219
......@@ -181,6 +181,12 @@ LOGGING = {
'formatter': 'main',
'filename': os.path.join(PROJECT_DIR, 'logs', 'jumpserver.log')
},
'ansible_logs': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'formatter': 'main',
'filename': os.path.join(PROJECT_DIR, 'logs', 'ansible.log')
},
},
'loggers': {
'django': {
......@@ -209,6 +215,10 @@ LOGGING = {
'jumpserver.users.view': {
'handlers': ['console', 'file'],
'level': LOG_LEVEL,
},
'ops.ansible_api': {
'handlers': ['console', 'ansible_logs'],
'level': LOG_LEVEL,
}
}
}
......
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals
"""jumpserver URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
......@@ -27,11 +30,15 @@ urlpatterns = [
url(r'^perms/', include('perms.urls.views_urls', namespace='perms')),
url(r'^audits/', include('audits.urls.views_urls', namespace='audits')),
url(r'^terminal/', include('terminal.urls.views_urls', namespace='terminal')),
url('^ops/', include('ops.urls.view_urls', namespace='ops')),
url(r'^api/users/', include('users.urls.api_urls', namespace='api-users')),
url(r'^api/assets/', include('assets.urls.api_urls', namespace='api-assets')),
url(r'^api/perms/', include('perms.urls.api_urls', namespace='api-perms')),
url(r'^api/audits/', include('audits.urls.api_urls', namespace='api-audits')),
url(r'^api/terminal/', include('terminal.urls.api_urls', namespace='api-terminal')),
url(r'^api/ops/', include('ops.urls.api_urls', namespace='api-ops')),
]
......
......@@ -8,220 +8,258 @@ msgid ""
msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-09-18 22:57+0800\n"
"POT-Creation-Date: 2016-11-22 21:30+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: Jumpserver team<ibuler@qq.com>\n"
"Language: zh_CN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh_CN\n"
#: assets/forms.py:26
msgid "Tags"
msgstr ""
#: assets/forms.py:57 assets/forms.py:201
#: assets/forms.py:53 assets/forms.py:208
#: assets/templates/assets/admin_user_detail.html:191 perms/forms.py:27
#: perms/templates/perms/asset_permission_asset.html:139 users/forms.py:115
#: perms/templates/perms/asset_permission_asset.html:139 users/forms.py:124
msgid "Select asset groups"
msgstr "添加到资产组"
#: assets/forms.py:59
#: assets/forms.py:56
#, fuzzy
#| msgid "Select assets"
msgid "Select asset tags"
msgstr "选择资产"
#: assets/forms.py:58
#, fuzzy
#| msgid "System user"
msgid "Select asset system users"
msgstr "系统"
#: assets/forms.py:60
#: assets/forms.py:59
#, fuzzy
#| msgid "Select assets"
msgid "Select asset admin user"
msgstr "选择资产"
#: assets/forms.py:71 assets/forms.py:102 assets/forms.py:133
#: assets/forms.py:191 assets/models.py:347
#: assets/forms.py:71 assets/forms.py:107 assets/forms.py:140
#: assets/forms.py:198 assets/forms.py:270
#: perms/templates/perms/asset_permission_create_update.html:40
#: templates/_nav.html:21
msgid "Asset"
msgstr "资产"
#: assets/forms.py:74 assets/forms.py:105 assets/forms.py:136
#: assets/forms.py:194 perms/forms.py:25 users/forms.py:113
#: assets/forms.py:74 assets/forms.py:110 assets/forms.py:143
#: assets/forms.py:201 assets/forms.py:273 perms/forms.py:25 users/forms.py:122
msgid "Select assets"
msgstr "选择资产"
#: assets/forms.py:124 assets/forms.py:179 assets/forms.py:251
#: assets/models.py:14 assets/models.py:89 assets/models.py:154
#: assets/models.py:238 assets/templates/assets/admin_user_detail.html:46
#: assets/forms.py:96
#, fuzzy
#| msgid "System user"
msgid "Select asset system user"
msgstr "系统"
#: assets/forms.py:129 assets/forms.py:186 assets/forms.py:258
#: assets/models.py:15 assets/models.py:94 assets/models.py:159
#: assets/models.py:243 assets/templates/assets/admin_user_detail.html:46
#: assets/templates/assets/admin_user_list.html:10
#: assets/templates/assets/asset_group_detail.html:46
#: assets/templates/assets/asset_group_list.html:12
#: assets/templates/assets/idc_list.html:10
#: assets/templates/assets/idc_list.html:12
#: assets/templates/assets/system_user_asset_group.html:53
#: assets/templates/assets/system_user_detail.html:51
#: assets/templates/assets/system_user_list.html:10 perms/models.py:19
#: assets/templates/assets/system_user_list.html:10 ops/models.py:18
#: ops/models.py:36 ops/models.py:48 ops/models.py:65 ops/models.py:397
#: ops/templates/cron/list.html:26 ops/templates/sudo/list.html:26
#: perms/models.py:19
#: perms/templates/perms/asset_permission_create_update.html:33
#: perms/templates/perms/asset_permission_detail.html:56
#: perms/templates/perms/asset_permission_list.html:12
#: perms/templates/perms/asset_permission_user.html:66 users/models.py:20
#: users/models.py:67 users/templates/users/user_asset_permission.html:66
#: perms/templates/perms/asset_permission_user.html:66 users/models.py:23
#: users/models.py:75 users/templates/users/_select_user_modal.html:13
#: users/templates/users/user_asset_permission.html:66
#: users/templates/users/user_detail.html:58
#: users/templates/users/user_granted_asset.html:129
#: users/templates/users/user_list.html:12
#: users/templates/users/user_group_detail.html:90
#: users/templates/users/user_group_list.html:25
#: users/templates/users/user_list.html:26
msgid "Name"
msgstr "名称"
#: assets/forms.py:141 assets/forms.py:206
#: assets/forms.py:148 assets/forms.py:213
msgid "If also set private key, use that first"
msgstr "如果设置私钥,则优先使用私钥"
#: assets/forms.py:180 assets/forms.py:252 assets/models.py:90
#: assets/models.py:155 assets/templates/assets/admin_user_detail.html:50
#: assets/forms.py:187 assets/forms.py:259 assets/models.py:95
#: assets/models.py:160 assets/templates/assets/admin_user_detail.html:50
#: assets/templates/assets/admin_user_list.html:11
#: assets/templates/assets/system_user_detail.html:55
#: assets/templates/assets/system_user_list.html:11
#: ops/templates/cron/list.html:27 ops/templates/sudo/list.html:27
#: perms/templates/perms/asset_permission_user.html:67 users/forms.py:13
#: users/models.py:66 users/templates/users/login.html:53
#: users/models.py:74 users/templates/users/_select_user_modal.html:14
#: users/templates/users/login.html:53
#: users/templates/users/user_detail.html:62
#: users/templates/users/user_list.html:13
#: users/templates/users/user_list.html:27
#: users/templates/users/user_update.html:6
msgid "Username"
msgstr "用户名"
#: assets/forms.py:197 assets/templates/assets/asset_detail.html:203
#: templates/_nav.html:22
#: assets/forms.py:204 templates/_nav.html:22
msgid "Asset group"
msgstr "资产组"
#: assets/models.py:15 assets/templates/assets/idc_list.html:12
#: assets/models.py:16
msgid "Bandwidth"
msgstr "带宽"
#: assets/models.py:16 assets/templates/assets/idc_list.html:13
#: assets/models.py:17 assets/templates/assets/idc_list.html:14
msgid "Contact"
msgstr "联系人"
#: assets/models.py:17 assets/templates/assets/idc_list.html:14
#: users/models.py:73 users/templates/users/user_detail.html:71
#: assets/models.py:18 assets/templates/assets/idc_list.html:15
#: users/models.py:81 users/templates/users/user_detail.html:71
msgid "Phone"
msgstr "手机"
#: assets/models.py:18 assets/templates/assets/idc_list.html:15
#: assets/models.py:19
msgid "Address"
msgstr "地址"
#: assets/models.py:19
msgid "Network"
msgstr "网络"
#: assets/models.py:20
msgid "Intranet"
msgstr ""
#: assets/models.py:21
msgid "Extranet"
msgstr ""
#: assets/models.py:20 assets/models.py:241 assets/models.py:307
#: assets/models.py:22 assets/models.py:246 assets/models.py:324
msgid "Date added"
msgstr "加入日期"
#: assets/models.py:21
#: assets/models.py:23
msgid "Operator"
msgstr "运营商"
#: assets/models.py:22 assets/models.py:59 assets/models.py:97
#: assets/models.py:168 assets/models.py:240 assets/models.py:305
#: assets/templates/assets/admin_user_detail.html:58
#: assets/templates/assets/asset_detail.html:127
#: assets/models.py:24 assets/models.py:64 assets/models.py:102
#: assets/models.py:173 assets/models.py:245 assets/models.py:322
#: assets/models.py:370 assets/templates/assets/admin_user_detail.html:58
#: assets/templates/assets/asset_detail.html:114
#: assets/templates/assets/asset_group_detail.html:54
#: assets/templates/assets/asset_tag_detail.html:49
#: assets/templates/assets/system_user_detail.html:101 perms/models.py:29
#: perms/templates/perms/asset_permission_detail.html:88 users/models.py:82
#: perms/templates/perms/asset_permission_detail.html:88 users/models.py:90
#: users/templates/users/user_detail.html:90
msgid "Created by"
msgstr "创建者"
#: assets/models.py:23 assets/models.py:61 assets/models.py:95
#: assets/models.py:169 assets/models.py:242 assets/models.py:308
#: assets/models.py:25 assets/models.py:66 assets/models.py:100
#: assets/models.py:174 assets/models.py:247 assets/models.py:325
#: assets/templates/assets/admin_user_detail.html:62
#: assets/templates/assets/admin_user_list.html:14
#: assets/templates/assets/asset_detail.html:135
#: assets/templates/assets/asset_detail.html:122
#: assets/templates/assets/asset_group_detail.html:58
#: assets/templates/assets/asset_group_list.html:14
#: assets/templates/assets/system_user_asset_group.html:56
#: assets/templates/assets/system_user_detail.html:105
#: assets/templates/assets/system_user_list.html:15 perms/models.py:31
#: perms/templates/perms/asset_permission_detail.html:92 users/models.py:21
#: users/models.py:78 users/templates/users/user_detail.html:102
#: perms/templates/perms/asset_permission_detail.html:92 users/models.py:24
#: users/models.py:86 users/templates/users/user_detail.html:102
#: users/templates/users/user_group_detail.html:94
#: users/templates/users/user_group_list.html:28
msgid "Comment"
msgstr "备注"
#: assets/models.py:57
#: assets/models.py:32 assets/models.py:257
#, fuzzy
#| msgid "As default"
msgid "Default"
msgstr "默认使用"
#: assets/models.py:32 users/models.py:224
msgid "System"
msgstr "系统"
#: assets/models.py:32
#, fuzzy
#| msgid "As default"
msgid "Default IDC"
msgstr "默认使用"
#: assets/models.py:62
msgid "KEY"
msgstr "KEY"
#: assets/models.py:58 assets/models.py:346
#: assets/models.py:63
msgid "VALUE"
msgstr "VALUE"
#: assets/models.py:69 assets/models.py:70
#: assets/models.py:74 assets/models.py:75
msgid "status"
msgstr "状态"
#: assets/models.py:69
#: assets/models.py:74
#, fuzzy
#| msgid "Admin user"
msgid "In use"
msgstr "管理用户"
#: assets/models.py:70
#: assets/models.py:75
#, fuzzy
#| msgid "Auto push"
msgid "Out of use"
msgstr "自动推送"
#: assets/models.py:71 assets/models.py:72 assets/models.py:73
#: assets/models.py:74 assets/models.py:75 assets/models.py:76
#: assets/models.py:76 assets/models.py:77 assets/models.py:78
#: assets/models.py:79 assets/models.py:80 assets/models.py:81
msgid "type"
msgstr ""
#: assets/models.py:71
#: assets/models.py:76
msgid "Server"
msgstr ""
#: assets/models.py:72
#: assets/models.py:77
msgid "VM"
msgstr ""
#: assets/models.py:73
#: assets/models.py:78
msgid "Switch"
msgstr ""
#: assets/models.py:74
#: assets/models.py:79
#, fuzzy
#| msgid "Role"
msgid "Router"
msgstr "角色"
#: assets/models.py:75
#: assets/models.py:80
msgid "Firewall"
msgstr ""
#: assets/models.py:76
#: assets/models.py:81
msgid "Storage"
msgstr ""
#: assets/models.py:77 assets/models.py:78 assets/models.py:79
#: assets/models.py:82 assets/models.py:83 assets/models.py:84
msgid "env"
msgstr ""
#: assets/models.py:77
#: assets/models.py:82
msgid "Production"
msgstr ""
#: assets/models.py:78
#: assets/models.py:83
msgid "Development"
msgstr ""
#: assets/models.py:79
#: assets/models.py:84
#, fuzzy
msgid "Testing"
msgstr "设置"
#: assets/models.py:91 assets/models.py:156 users/forms.py:15
#: assets/models.py:96 assets/models.py:161 users/forms.py:15
#: users/templates/users/login.html:56
#: users/templates/users/reset_password.html:52
#: users/templates/users/user_create.html:9
......@@ -231,172 +269,169 @@ msgstr "设置"
msgid "Password"
msgstr "密码"
#: assets/models.py:92 assets/models.py:158
#: assets/models.py:97 assets/models.py:163
msgid "SSH private key"
msgstr "ssh密钥"
#: assets/models.py:93 assets/models.py:159
#: assets/models.py:98 assets/models.py:164
msgid "SSH public key"
msgstr "ssh公钥"
#: assets/models.py:94 assets/models.py:160
#: assets/models.py:99 assets/models.py:165
#: assets/templates/assets/admin_user_create_update.html:43
#: assets/templates/assets/system_user_create_update.html:44
#: assets/templates/assets/system_user_detail.html:71
msgid "As default"
msgstr "默认使用"
#: assets/models.py:157 assets/templates/assets/system_user_detail.html:59
#: assets/models.py:162 assets/templates/assets/system_user_detail.html:59
msgid "Protocol"
msgstr "协议"
#: assets/models.py:161
#: assets/models.py:166
#: assets/templates/assets/system_user_create_update.html:50
#: assets/templates/assets/system_user_detail.html:63
msgid "Auto push"
msgstr "自动推送"
#: assets/models.py:162
#: assets/models.py:167
msgid "Auto update pass/key"
msgstr "自动更新密码/密钥"
#: assets/models.py:163 assets/templates/assets/system_user_detail.html:75
#: assets/models.py:168 assets/templates/assets/system_user_detail.html:75
#: templates/_nav.html:46
msgid "Sudo"
msgstr "Sudo"
#: assets/models.py:164 assets/templates/assets/system_user_detail.html:80
#: assets/models.py:169 assets/templates/assets/system_user_detail.html:80
msgid "Shell"
msgstr "Shell"
#: assets/models.py:165 assets/templates/assets/system_user_detail.html:86
#: assets/models.py:170 assets/templates/assets/system_user_detail.html:86
#: templates/_header_bar.html:41 templates/_nav.html:4
msgid "Home"
msgstr "仪表盘"
#: assets/models.py:166 assets/templates/assets/system_user_detail.html:92
#: assets/models.py:171 assets/templates/assets/system_user_detail.html:92
msgid "Uid"
msgstr "Uid"
#: assets/models.py:252
#, fuzzy
#| msgid "As default"
msgid "Default"
msgstr "默认使用"
#: assets/models.py:252
#: assets/models.py:257
#, fuzzy
#| msgid "Create asset group"
msgid "Default asset group"
msgstr "创建资产组"
#: assets/models.py:279 assets/templates/assets/admin_user_detail.html:92
#: assets/templates/assets/asset_detail.html:57
#: assets/models.py:291 assets/templates/assets/admin_user_detail.html:92
#: assets/templates/assets/asset_detail.html:54
#: assets/templates/assets/asset_group_detail.html:88
#: assets/templates/assets/asset_list.html:13
#: assets/templates/assets/asset_list.html:58
#: assets/templates/assets/asset_tag_detail.html:84
#: assets/templates/assets/system_user_asset.html:50
#: perms/templates/perms/asset_permission_asset.html:67
#: users/templates/users/user_granted_asset.html:67
msgid "IP"
msgstr "IP"
#: assets/models.py:280 assets/templates/assets/asset_detail.html:61
#: assets/models.py:292 assets/templates/assets/asset_detail.html:58
msgid "Other IP"
msgstr "其它IP"
#: assets/models.py:281 assets/templates/assets/asset_detail.html:65
#: assets/models.py:293 assets/templates/assets/asset_detail.html:62
msgid "Remote card IP"
msgstr "远控卡IP"
#: assets/models.py:282 assets/templates/assets/admin_user_detail.html:91
#: assets/templates/assets/asset_detail.html:53
#: assets/models.py:294 assets/templates/assets/admin_user_detail.html:91
#: assets/templates/assets/asset_detail.html:50
#: assets/templates/assets/asset_group_detail.html:87
#: assets/templates/assets/asset_list.html:12
#: assets/templates/assets/asset_list.html:57
#: assets/templates/assets/asset_tag_detail.html:83
#: assets/templates/assets/system_user_asset.html:49
#: perms/templates/perms/asset_permission_asset.html:66
#: users/templates/users/user_granted_asset.html:66
msgid "Hostname"
msgstr "主机名"
#: assets/models.py:283 assets/templates/assets/admin_user_detail.html:93
#: assets/templates/assets/asset_detail.html:69
#: assets/models.py:295 assets/templates/assets/admin_user_detail.html:93
#: assets/templates/assets/asset_detail.html:66
#: assets/templates/assets/asset_group_detail.html:89
#: assets/templates/assets/asset_list.html:14
#: assets/templates/assets/asset_list.html:59
#: assets/templates/assets/asset_tag_detail.html:85
#: assets/templates/assets/system_user_asset.html:51
#: perms/templates/perms/asset_permission_asset.html:68
#: users/templates/users/user_granted_asset.html:68
msgid "Port"
msgstr "端口"
#: assets/models.py:284
#: assets/models.py:296 assets/templates/assets/asset_detail.html:184
msgid "Asset groups"
msgstr "用户组"
#: assets/models.py:286 templates/_nav.html:24
#: assets/models.py:298 templates/_nav.html:24
msgid "Admin user"
msgstr "管理用户"
#: assets/models.py:287
#: assets/models.py:299
msgid "System User"
msgstr "系统用户"
#: assets/models.py:288 templates/_nav.html:23
#: assets/models.py:301 templates/_nav.html:23
msgid "IDC"
msgstr "机房"
#: assets/models.py:289 assets/templates/assets/asset_detail.html:73
#: assets/templates/assets/asset_detail.html:99
#: assets/models.py:303 assets/templates/assets/asset_detail.html:70
msgid "Mac address"
msgstr "Mac地址"
#: assets/models.py:290
#: assets/models.py:304
msgid "Brand"
msgstr "品牌"
#: assets/models.py:291 assets/templates/assets/asset_detail.html:77
#: assets/models.py:305 assets/templates/assets/asset_detail.html:74
msgid "CPU"
msgstr "CPU"
#: assets/models.py:292 assets/templates/assets/asset_detail.html:81
#: assets/models.py:306 assets/templates/assets/asset_detail.html:78
msgid "Memory"
msgstr "内存"
#: assets/models.py:293 assets/templates/assets/asset_detail.html:85
#: assets/models.py:307 assets/templates/assets/asset_detail.html:82
msgid "Disk"
msgstr "硬盘"
#: assets/models.py:294 assets/templates/assets/asset_detail.html:95
#: assets/models.py:308 assets/templates/assets/asset_detail.html:86
msgid "OS"
msgstr "操作系统"
#: assets/models.py:295
#: assets/models.py:309
msgid "Cabinet number"
msgstr "机柜编号"
#: assets/models.py:296
#: assets/models.py:310
msgid "Cabinet position"
msgstr "机柜层号"
#: assets/models.py:297 assets/templates/assets/asset_detail.html:123
#: assets/models.py:311 assets/templates/assets/asset_detail.html:110
msgid "Asset number"
msgstr "资产编号"
#: assets/models.py:299 assets/templates/assets/asset_detail.html:103
#: assets/models.py:313 assets/templates/assets/asset_detail.html:90
msgid "Asset status"
msgstr "资产状态"
#: assets/models.py:301 assets/templates/assets/asset_detail.html:111
#: assets/models.py:316 assets/templates/assets/asset_detail.html:98
msgid "Asset type"
msgstr "系统类型"
#: assets/models.py:303 assets/templates/assets/asset_detail.html:115
#: assets/models.py:319 assets/templates/assets/asset_detail.html:102
msgid "Asset environment"
msgstr "资产环境"
#: assets/models.py:304 assets/templates/assets/asset_detail.html:119
#: assets/models.py:321 assets/templates/assets/asset_detail.html:106
msgid "Serial number"
msgstr "序列号"
#: assets/models.py:306 assets/templates/assets/asset_detail.html:107
#: assets/models.py:323 assets/templates/assets/asset_detail.html:94
msgid "Is active"
msgstr "是否激活"
......@@ -416,11 +451,8 @@ msgstr "自动更新密码/密钥"
#: assets/templates/assets/admin_user_create_update.html:53
#: assets/templates/assets/admin_user_detail.html:144
#: assets/templates/assets/asset_create_update.html:45
#: assets/templates/assets/asset_detail.html:184
#: assets/templates/assets/asset_detail.html:192
#: assets/templates/assets/asset_group_create.html:38
#: assets/templates/assets/idc_create_update.html:44
#: assets/templates/assets/asset_create.html:33
#: assets/templates/assets/asset_update.html:61
#: assets/templates/assets/system_user_create_update.html:71
#: assets/templates/assets/system_user_detail.html:144
#: perms/templates/perms/asset_permission_create_update.html:67
......@@ -431,23 +463,26 @@ msgid "Reset"
msgstr "重置"
#: assets/templates/assets/admin_user_create_update.html:54
#: assets/templates/assets/asset_create_update.html:46
#: assets/templates/assets/asset_group_create.html:39
#: assets/templates/assets/asset_create.html:34
#: assets/templates/assets/asset_group_list.html:51
#: assets/templates/assets/asset_list.html:64
#: assets/templates/assets/idc_create_update.html:45
#: assets/templates/assets/asset_list.html:105
#: assets/templates/assets/asset_tags_list.html:50
#: assets/templates/assets/asset_update.html:62
#: assets/templates/assets/system_user_create_update.html:72
#: ops/templates/cron/list.html:47 ops/templates/sudo/list.html:47
#: perms/templates/perms/asset_permission_create_update.html:68
#: perms/templates/perms/asset_permission_list.html:65
#: users/templates/users/_user.html:71
#: users/templates/users/forgot_password.html:44
#: users/templates/users/user_asset_permission.html:144
#: users/templates/users/user_list.html:64
#: users/templates/users/user_group_list.html:40
#: users/templates/users/user_list.html:47
msgid "Submit"
msgstr "提交"
#: assets/templates/assets/admin_user_detail.html:19
#: assets/templates/assets/asset_group_detail.html:18
#: assets/templates/assets/asset_tag_detail.html:18
#: assets/templates/assets/system_user_asset.html:19
#: assets/templates/assets/system_user_asset_group.html:19
#: assets/templates/assets/system_user_detail.html:19
......@@ -459,6 +494,7 @@ msgstr ""
#: assets/templates/assets/admin_user_detail.html:54
#: assets/templates/assets/asset_group_detail.html:50
#: assets/templates/assets/asset_tag_detail.html:53
#: assets/templates/assets/system_user_detail.html:97 perms/models.py:30
#: perms/templates/perms/asset_permission_detail.html:84
#, fuzzy
......@@ -468,6 +504,7 @@ msgstr "加入日期"
#: assets/templates/assets/admin_user_detail.html:72
#: assets/templates/assets/asset_group_detail.html:68
#: assets/templates/assets/asset_tag_detail.html:64
#: assets/templates/assets/system_user_asset_group.html:34
#: perms/templates/perms/asset_permission_asset.html:47
#, fuzzy
......@@ -477,6 +514,7 @@ msgstr "资产组列表"
#: assets/templates/assets/admin_user_detail.html:94
#: assets/templates/assets/asset_group_detail.html:90
#: assets/templates/assets/asset_tag_detail.html:86
#, fuzzy
msgid "Alive"
msgstr "激活"
......@@ -536,7 +574,6 @@ msgid "Replace asset admin user with this admin user"
msgstr ""
#: assets/templates/assets/admin_user_list.html:9
#: assets/templates/assets/idc_list.html:9
#: assets/templates/assets/system_user_list.html:9
#, fuzzy
#| msgid "IDC"
......@@ -545,10 +582,13 @@ msgstr "机房"
#: assets/templates/assets/admin_user_list.html:12
#: assets/templates/assets/asset_group_list.html:13
#: assets/templates/assets/idc_list.html:11
#: assets/templates/assets/asset_tags_list.html:13
#: assets/templates/assets/idc_list.html:13
#: assets/templates/assets/system_user_asset_group.html:54
#: assets/templates/assets/system_user_list.html:12
#: users/templates/users/user_list.html:16
#: ops/templates/cron/list.html:30 ops/templates/sudo/list.html:30
#: users/templates/users/_select_user_modal.html:17
#: users/templates/users/user_list.html:30
msgid "Asset num"
msgstr "资产数量"
......@@ -562,128 +602,135 @@ msgid "Script"
msgstr ""
#: assets/templates/assets/admin_user_list.html:35
#: assets/templates/assets/asset_detail.html:156
#: assets/templates/assets/system_user_list.html:37
msgid "Refresh"
msgstr ""
#: assets/templates/assets/admin_user_list.html:36
#: assets/templates/assets/asset_group_list.html:32
#: assets/templates/assets/asset_list.html:44
#: assets/templates/assets/idc_list.html:30
#: assets/templates/assets/asset_list.html:83
#: assets/templates/assets/asset_tags_list.html:31
#: assets/templates/assets/idc_list.html:32
#: assets/templates/assets/system_user_list.html:38
#: ops/templates/cron/list.html:79 ops/templates/sudo/list.html:79
#: perms/templates/perms/asset_permission_list.html:46
#: users/templates/users/user_list.html:44
#: users/templates/users/user_detail.html:167
#: users/templates/users/user_group_list.html:64
#: users/templates/users/user_list.html:79
msgid "Update"
msgstr "更新"
#: assets/templates/assets/admin_user_list.html:37
#: assets/templates/assets/asset_group_list.html:33
#: assets/templates/assets/asset_list.html:45
#: assets/templates/assets/idc_list.html:31
#: assets/templates/assets/asset_list.html:84
#: assets/templates/assets/asset_tags_list.html:32
#: assets/templates/assets/idc_list.html:33
#: assets/templates/assets/system_user_list.html:39
#: ops/templates/cron/list.html:80 ops/templates/sudo/list.html:80
#: perms/templates/perms/asset_permission_list.html:47
#: users/templates/users/user_list.html:45
#: users/templates/users/user_list.html:46
#: users/templates/users/user_group_detail.html:129
#: users/templates/users/user_group_detail.html:132
#: users/templates/users/user_group_list.html:65
#: users/templates/users/user_list.html:80
msgid "Delete"
msgstr "删除"
#: assets/templates/assets/asset_create_update.html:14
#: assets/templates/assets/asset_create.html:9
#: assets/templates/assets/asset_update.html:14
msgid "Basic"
msgstr ""
#: assets/templates/assets/asset_create_update.html:21
#: assets/templates/assets/asset_create.html:16
#: assets/templates/assets/asset_update.html:21
msgid "Group"
msgstr ""
#: assets/templates/assets/asset_create_update.html:26
#: assets/templates/assets/asset_create.html:21
#: assets/templates/assets/asset_update.html:26
#, fuzzy
#| msgid "Asset number"
msgid "Asset user"
msgstr "资产编号"
#: assets/templates/assets/asset_create_update.html:31
#: assets/templates/assets/asset_create.html:26
#: assets/templates/assets/asset_update.html:52
#: perms/templates/perms/asset_permission_create_update.html:45
#, fuzzy
#| msgid "Other IP"
msgid "Other"
msgstr "其它IP"
#: assets/templates/assets/asset_detail.html:19
#: assets/templates/assets/asset_detail.html:20
#, fuzzy
#| msgid "Asset group list"
msgid "Asset detail"
msgstr "资产组列表"
#: assets/templates/assets/asset_detail.html:21
#, fuzzy
#| msgid "Asset number"
msgid "Asset users"
msgstr "资产编号"
#: assets/templates/assets/asset_detail.html:22
#: assets/templates/assets/asset_detail.html:23
#, fuzzy
#| msgid "Asset group list"
msgid "Asset login log"
msgstr "资产组列表"
#: assets/templates/assets/asset_detail.html:89 templates/_nav.html:26
msgid "Label"
msgstr "标签"
#: assets/templates/assets/asset_detail.html:131
#: assets/templates/assets/asset_detail.html:118
#: users/templates/users/user_detail.html:94
msgid "Date joined"
msgstr "创建日期"
#: assets/templates/assets/asset_detail.html:146
#: assets/templates/assets/asset_detail.html:133
#: users/templates/users/user_detail.html:113
#: users/templates/users/user_group_detail.html:115
msgid "Quick modify"
msgstr "快速修改"
#: assets/templates/assets/asset_detail.html:152 perms/models.py:27
#: assets/templates/assets/asset_detail.html:139
#: ops/templates/cron/list.html:31 ops/templates/sudo/list.html:31
#: perms/models.py:27
#: perms/templates/perms/asset_permission_create_update.html:47
#: users/templates/users/_select_user_modal.html:18
#: users/templates/users/user_detail.html:119
#: users/templates/users/user_list.html:17
#: users/templates/users/user_list.html:31
#, fuzzy
msgid "Active"
msgstr "激活"
#: assets/templates/assets/asset_detail.html:166 users/models.py:74
#: users/templates/users/_user.html:57
#: users/templates/users/user_detail.html:133
msgid "Enable OTP"
msgstr "二次验证"
#: assets/templates/assets/asset_detail.html:153
msgid "Rrefresh hardware"
msgstr ""
#: assets/templates/assets/asset_detail.html:181
#: users/templates/users/reset_password.html:45
#: users/templates/users/user_detail.html:148 users/utils.py:99
msgid "Reset password"
msgstr "重置密码"
#: assets/templates/assets/asset_detail.html:161
#, fuzzy
#| msgid "Create user"
msgid "Test admin user"
msgstr "创建用户"
#: assets/templates/assets/asset_detail.html:189
#: users/templates/users/user_detail.html:156
msgid "Reset ssh key"
msgstr "重置密钥"
#: assets/templates/assets/asset_detail.html:164
#: assets/templates/assets/asset_detail.html:172
#, fuzzy
msgid "Test"
msgstr "设置"
#: assets/templates/assets/asset_detail.html:211 users/forms.py:33
#: users/forms.py:51 users/templates/users/user_detail.html:178
msgid "Join user groups"
#: assets/templates/assets/asset_detail.html:169
#, fuzzy
#| msgid "System user"
msgid "Test system users"
msgstr "系统"
#: assets/templates/assets/asset_detail.html:192
#, fuzzy
#| msgid "Join user groups"
msgid "Join asset groups"
msgstr "添加到用户组"
#: assets/templates/assets/asset_detail.html:220
#: assets/templates/assets/asset_detail.html:201
#: perms/templates/perms/asset_permission_asset.html:148
#: perms/templates/perms/asset_permission_detail.html:164
#: perms/templates/perms/asset_permission_user.html:148
#: users/templates/users/user_detail.html:187
#: users/templates/users/user_detail.html:195
msgid "Join"
msgstr "加入"
#: assets/templates/assets/asset_group_create.html:16
#: assets/templates/assets/asset_group_list.html:5 assets/views.py:80
#: assets/views.py:150
msgid "Create asset group"
msgstr "创建资产组"
#: assets/templates/assets/asset_group_detail.html:20
#, fuzzy
#| msgid "Asset group"
......@@ -691,109 +738,154 @@ msgid "Asset group perm"
msgstr "资产组"
#: assets/templates/assets/asset_group_detail.html:113
#: assets/templates/assets/asset_tag_detail.html:109
#, fuzzy
#| msgid "System user"
msgid "Associate system user"
msgstr "系统"
#: assets/templates/assets/asset_group_detail.html:119
#: assets/templates/assets/asset_tag_detail.html:115
#, fuzzy
#| msgid "System user"
msgid "repush system user"
msgstr "系统"
#: assets/templates/assets/asset_group_detail.html:129
#: assets/templates/assets/asset_tag_detail.html:125
#, fuzzy
#| msgid "System user"
msgid "Select system user"
msgstr "系统"
#: assets/templates/assets/asset_group_detail.html:138
#: assets/templates/assets/asset_tag_detail.html:134
msgid "Associate"
msgstr ""
#: assets/templates/assets/asset_group_detail.html:157
#: assets/templates/assets/asset_tag_detail.html:153
#, fuzzy
#| msgid "Asset group"
msgid "Add asset to this group"
msgstr "资产组"
#: assets/templates/assets/asset_group_detail.html:165
#: assets/templates/assets/asset_tag_detail.html:161
#, fuzzy
#| msgid "Select assets"
msgid "Select asset user"
msgstr "选择资产"
#: assets/templates/assets/asset_group_detail.html:174
#: assets/templates/assets/asset_tag_detail.html:170
#: assets/templates/assets/system_user_asset_group.html:96
#: perms/templates/perms/asset_permission_asset.html:120
#: perms/templates/perms/asset_permission_user.html:120
#: users/templates/users/user_group_detail.html:124
#, fuzzy
#| msgid "Address"
msgid "Add"
msgstr "地址"
#: assets/templates/assets/asset_group_list.html:5 assets/views.py:157
#: assets/views.py:234
msgid "Create asset group"
msgstr "创建资产组"
#: assets/templates/assets/asset_group_list.html:43
#: assets/templates/assets/asset_list.html:56
#: assets/templates/assets/asset_list.html:98
#: assets/templates/assets/asset_tags_list.html:42
#: ops/templates/cron/list.html:41 ops/templates/sudo/list.html:41
#: perms/templates/perms/asset_permission_list.html:57
#: users/templates/users/user_list.html:56
#: users/templates/users/user_group_list.html:36
#: users/templates/users/user_list.html:41
msgid "Delete selected"
msgstr "批量删除"
#: assets/templates/assets/asset_group_list.html:44
#: assets/templates/assets/asset_list.html:57
#: assets/templates/assets/asset_list.html:99
#: assets/templates/assets/asset_tags_list.html:43
#: ops/templates/cron/list.html:42 ops/templates/sudo/list.html:42
#: perms/templates/perms/asset_permission_list.html:58
#: users/templates/users/user_list.html:57
#: users/templates/users/user_list.html:42
msgid "Update selected"
msgstr "批量更新"
#: assets/templates/assets/asset_group_list.html:45
#: assets/templates/assets/asset_list.html:58
#: assets/templates/assets/asset_list.html:100
#: assets/templates/assets/asset_tags_list.html:44
#: ops/templates/cron/list.html:43 ops/templates/sudo/list.html:43
#: perms/templates/perms/asset_permission_list.html:59
#: users/templates/users/user_list.html:58
#: users/templates/users/user_list.html:43
msgid "Deactive selected"
msgstr "禁用所选"
#: assets/templates/assets/asset_group_list.html:46
#: assets/templates/assets/asset_list.html:59
#: assets/templates/assets/asset_list.html:101
#: assets/templates/assets/asset_tags_list.html:45
#: perms/templates/perms/asset_permission_list.html:60
#: users/templates/users/user_list.html:59
msgid "Export selected"
msgstr "批量导出"
#: assets/templates/assets/asset_list.html:5
#: assets/templates/assets/asset_list.html:21
msgid "Create asset"
msgstr "创建资产"
#: assets/templates/assets/asset_list.html:15
#: assets/templates/assets/asset_list.html:60
msgid "Type"
msgstr ""
#: assets/templates/assets/asset_list.html:16
#: assets/templates/assets/asset_list.html:61
#: assets/templates/assets/asset_update.html:31
msgid "Hardware"
msgstr ""
#: assets/templates/assets/asset_list.html:17
#: assets/templates/assets/asset_list.html:62
msgid "Valid"
msgstr ""
#: assets/templates/assets/asset_tag_detail.html:45
#: assets/templates/assets/asset_tags_list.html:12
#, fuzzy
#| msgid "Name"
msgid "Tag Name"
msgstr "名称"
#: assets/templates/assets/asset_tags_list.html:5
#, fuzzy
#| msgid "Create asset"
msgid "Create tag"
msgstr "创建资产"
#: assets/templates/assets/asset_update.html:40
#, fuzzy
#| msgid "Confirm delete"
msgid "Configuration"
msgstr "确认删除"
#: assets/templates/assets/asset_update.html:47
#, fuzzy
msgid "Location"
msgstr "激活"
#: assets/templates/assets/delete_confirm.html:6
#: perms/templates/perms/delete_confirm.html:6
#: users/templates/users/user_delete_confirm.html:6
msgid "Confirm delete"
msgstr "确认删除"
#: assets/templates/assets/idc_create_update.html:16
#: assets/templates/assets/idc_list.html:5 assets/views.py:287
#, fuzzy
#| msgid "Created by"
msgid "Create idc"
msgid "Create IDC"
msgstr "创建者"
#: assets/templates/assets/idc_list.html:5
#: assets/templates/assets/idc_list.html:16
#, fuzzy
#| msgid "Created by"
msgid "Create IDC"
msgstr "创建者"
#| msgid "Operator"
msgid "operation"
msgstr "运营商"
#: assets/templates/assets/system_user_asset.html:22
#: assets/templates/assets/system_user_detail.html:23
......@@ -864,7 +956,7 @@ msgid "Select asset group"
msgstr "添加到资产组"
#: assets/templates/assets/system_user_create_update.html:16
#: assets/templates/assets/system_user_list.html:5 assets/views.py:368
#: assets/templates/assets/system_user_list.html:5 assets/views.py:469
#, fuzzy
#| msgid "Create user"
msgid "Create system user"
......@@ -887,67 +979,375 @@ msgstr ""
msgid "Asset group num"
msgstr "资产组"
#: assets/templates/assets/system_user_list.html:14
#: assets/templates/assets/system_user_list.html:14 ops/models.py:69
msgid "Unreachable"
msgstr ""
#: assets/views.py:79 assets/views.py:99 assets/views.py:133
#: assets/views.py:149 assets/views.py:171 assets/views.py:238
#: assets/views.py:337 assets/views.py:367 assets/views.py:390
#: assets/views.py:408 templates/_nav.html:18
#: assets/views.py:156 assets/views.py:182 assets/views.py:214
#: assets/views.py:233 assets/views.py:257 assets/views.py:339
#: assets/views.py:438 assets/views.py:468 assets/views.py:491
#: assets/views.py:509 templates/_nav.html:18
msgid "Assets"
msgstr "资产管理"
#: assets/views.py:100
#: assets/views.py:183
msgid "Asset group list"
msgstr "资产组列表"
#: assets/views.py:134
#: assets/views.py:215
#, fuzzy
#| msgid "Asset group list"
msgid "Asset group detail"
msgstr "资产组列表"
#: assets/views.py:172
#: assets/views.py:258
msgid "IDC list"
msgstr ""
#: assets/views.py:239
#: assets/views.py:286 assets/views.py:313
#, fuzzy
#| msgid "Assets"
msgid "assets"
msgstr "资产管理"
#: assets/views.py:314
#, fuzzy
#| msgid "Update"
msgid "Update IDC"
msgstr "更新"
#: assets/views.py:340
#, fuzzy
#| msgid "Admin user"
msgid "Admin user list"
msgstr "管理用户"
#: assets/views.py:275
#: assets/views.py:376
#, fuzzy, python-format
#| msgid "Create user <a href=\"%s\">%s</a> success."
msgid "Create admin user <a href=\"%s\">%s</a> successfully."
msgstr "创建用户 <a href=\"%s\">%s</a> 成功"
#: assets/views.py:338
#: assets/views.py:439
#, fuzzy
#| msgid "System user"
msgid "System user list"
msgstr "系统"
#: assets/views.py:374
#: assets/views.py:475
#, fuzzy, python-format
#| msgid "Create user <a href=\"%s\">%s</a> success."
msgid "Create system user <a href=\"%s\">%s</a> successfully."
msgstr "创建用户 <a href=\"%s\">%s</a> 成功"
#: assets/views.py:391
#: assets/views.py:492
#, fuzzy
#| msgid "Update user"
msgid "Update system user"
msgstr "编辑用户"
#: assets/views.py:409
#: assets/views.py:510
#, fuzzy
#| msgid "System user"
msgid "System user detail"
msgstr "系统"
#: assets/views.py:601 assets/views.py:619 assets/views.py:648
#: assets/views.py:667
msgid "Tag"
msgstr ""
#: assets/views.py:602 assets/views.py:620
#, fuzzy
#| msgid "Asset group list"
msgid "Asset Tags list"
msgstr "资产组列表"
#: assets/views.py:649 assets/views.py:668
#, fuzzy
#| msgid "Asset group list"
msgid "Asset Tags detail"
msgstr "资产组列表"
#: common/mixins.py:28
msgid "is discard"
msgstr ""
#: common/mixins.py:29
msgid "discard time"
msgstr ""
#: ops/api/exc.py:10
msgid "Service temporarily unavailable, try again later."
msgstr ""
#: ops/api/exc.py:15
msgid "This service maybe implemented in the future, but now not implemented!"
msgstr ""
#: ops/models.py:17 ops/models.py:35 ops/models.py:47
msgid "UUID"
msgstr ""
#: ops/models.py:19
msgid "Start Time"
msgstr ""
#: ops/models.py:20
msgid "End Time"
msgstr ""
#: ops/models.py:21
msgid "Exit Code"
msgstr ""
#: ops/models.py:22
msgid "Is Completed"
msgstr ""
#: ops/models.py:23
#, fuzzy
#| msgid "Hostname"
msgid "Hosts"
msgstr "主机名"
#: ops/models.py:66
msgid "Success"
msgstr ""
#: ops/models.py:67
msgid "Skipped"
msgstr ""
#: ops/models.py:68
msgid "Failed"
msgstr ""
#: ops/models.py:70
msgid "NoHost"
msgstr ""
#: ops/models.py:217
msgid "Host_Alias"
msgstr ""
#: ops/models.py:218 ops/models.py:226 ops/models.py:234 ops/models.py:242
msgid "Host_Items"
msgstr ""
#: ops/models.py:225
#, fuzzy
#| msgid "User list"
msgid "User_Alias"
msgstr "用户列表"
#: ops/models.py:233
msgid "Command_Alias"
msgstr ""
#: ops/models.py:241
msgid "Runas_Alias"
msgstr ""
#: ops/models.py:253
#, fuzzy
#| msgid "Password"
msgid "Is_NoPassword"
msgstr "密码"
#: ops/models.py:267
msgid "Extra_Item"
msgstr ""
#: ops/models.py:398
msgid "Description of a crontab entry"
msgstr ""
#: ops/models.py:399
msgid "Month"
msgstr ""
#: ops/models.py:400
msgid "Month of the year the job should run ( 1-12, *, */2, etc )"
msgstr ""
#: ops/models.py:401
msgid "WeekDay"
msgstr ""
#: ops/models.py:402
msgid ""
"Day of the week that the job should run ( 0-6 for Sunday-Saturday, *, etc )"
msgstr ""
#: ops/models.py:404
msgid "Day"
msgstr ""
#: ops/models.py:405
msgid "Day of the month the job should run ( 1-31, *, */2, etc )"
msgstr ""
#: ops/models.py:406
msgid "Hour"
msgstr ""
#: ops/models.py:407
msgid "Hour when the job should run ( 0-23, *, */2, etc )"
msgstr ""
#: ops/models.py:408
msgid "Minute"
msgstr ""
#: ops/models.py:409
msgid "Minute when the job should run ( 0-59, *, */2, etc )"
msgstr ""
#: ops/models.py:410
msgid "Job"
msgstr ""
#: ops/models.py:411
msgid ""
"The command to execute or, if env is set, the value of environment variable. "
"Required if state=present."
msgstr ""
#: ops/models.py:413
#: perms/templates/perms/asset_permission_create_update.html:36
#: templates/_nav.html:12 templates/_user_profile.html:14 users/models.py:71
#: users/templates/users/_user_bulk_update_modal.html:14
msgid "User"
msgstr "用户"
#: ops/models.py:414
msgid "The specific user whose crontab should be modified."
msgstr ""
#: ops/templates/cron/list.html:18 ops/templates/sudo/list.html:18
#: users/templates/users/user_list.html:18
#, fuzzy
#| msgid "Create user"
msgid "Import user"
msgstr "创建用户"
#: ops/templates/cron/list.html:19 ops/templates/sudo/list.html:19
#: users/templates/users/_user.html:17 users/templates/users/user_create.html:4
#: users/templates/users/user_list.html:19 users/views.py:102
msgid "Create user"
msgstr "创建用户"
#: ops/templates/cron/list.html:28 ops/templates/sudo/list.html:28
#: users/models.py:78 users/templates/users/_select_user_modal.html:15
#: users/templates/users/_user_bulk_update_modal.html:9
#: users/templates/users/user_detail.html:82
#: users/templates/users/user_list.html:28
msgid "Role"
msgstr "角色"
#: ops/templates/cron/list.html:29 ops/templates/sudo/list.html:29
#: templates/_nav.html:13 users/models.py:77
#: users/templates/users/_select_user_modal.html:16
#: users/templates/users/user_detail.html:178
#: users/templates/users/user_list.html:29
msgid "User group"
msgstr "用户组"
#: ops/templates/cron/list.html:32 ops/templates/sudo/list.html:32
#: users/templates/users/user_group_list.html:29
#: users/templates/users/user_list.html:32
#, fuzzy
msgid "Action"
msgstr "激活"
#: ops/templates/cron/list.html:116 ops/templates/cron/list.html:178
#: ops/templates/sudo/list.html:116 ops/templates/sudo/list.html:178
#: users/templates/users/user_detail.html:329
#: users/templates/users/user_detail.html:354
#: users/templates/users/user_group_detail.html:169
#: users/templates/users/user_group_list.html:102
#: users/templates/users/user_group_list.html:125
#: users/templates/users/user_list.html:116
#: users/templates/users/user_list.html:178
msgid "Are you sure?"
msgstr ""
#: ops/templates/cron/list.html:117 ops/templates/sudo/list.html:117
#: users/templates/users/user_list.html:117
msgid "This will delete the selected users !!!"
msgstr ""
#: ops/templates/cron/list.html:121 ops/templates/cron/list.html:183
#: ops/templates/sudo/list.html:121 ops/templates/sudo/list.html:183
#: templates/_modal.html:16 users/templates/users/user_detail.html:334
#: users/templates/users/user_detail.html:359
#: users/templates/users/user_detail.html:383
#: users/templates/users/user_group_detail.html:174
#: users/templates/users/user_group_list.html:107
#: users/templates/users/user_group_list.html:130
#: users/templates/users/user_list.html:121
#: users/templates/users/user_list.html:183
#, fuzzy
#| msgid "Confirm delete"
msgid "Confirm"
msgstr "确认删除"
# msgid "Deleted!"
# msgstr "删除"
#: ops/templates/cron/list.html:125 ops/templates/cron/list.html:161
#: ops/templates/sudo/list.html:125 ops/templates/sudo/list.html:161
#: users/templates/users/user_list.html:125
#: users/templates/users/user_list.html:161
#, fuzzy
#| msgid "has been deleted."
msgid "User Deleted."
msgstr "已被删除"
#: ops/templates/cron/list.html:126 ops/templates/cron/list.html:131
#: ops/templates/cron/list.html:162 ops/templates/cron/list.html:167
#: ops/templates/sudo/list.html:126 ops/templates/sudo/list.html:131
#: ops/templates/sudo/list.html:162 ops/templates/sudo/list.html:167
#: users/templates/users/user_list.html:126
#: users/templates/users/user_list.html:131
#: users/templates/users/user_list.html:162
#: users/templates/users/user_list.html:167
#, fuzzy
#| msgid "Delete"
msgid "User Delete"
msgstr "删除"
#: ops/templates/cron/list.html:130 ops/templates/cron/list.html:166
#: ops/templates/sudo/list.html:130 ops/templates/sudo/list.html:166
#: users/templates/users/user_list.html:130
#: users/templates/users/user_list.html:166
#, fuzzy
#| msgid "User detail"
msgid "User Deleting failed."
msgstr "用户详情"
#: ops/templates/cron/list.html:179 ops/templates/sudo/list.html:179
#: users/templates/users/user_list.html:179
msgid "This will delete the selected user."
msgstr ""
#: ops/templates/cron/list.html:215 ops/templates/sudo/list.html:215
#: users/templates/users/user_list.html:215
msgid "The selected users has been updated successfully."
msgstr ""
#: ops/templates/cron/list.html:216 ops/templates/sudo/list.html:216
#: users/templates/users/user_list.html:216
#, fuzzy
#| msgid "Update"
msgid "User Updated"
msgstr "更新"
#: ops/templates/cron/list.html:232 ops/templates/sudo/list.html:232
#: users/templates/users/user_list.html:232
msgid "Import User Success."
msgstr ""
#: perms/forms.py:21
#, fuzzy
#| msgid "Select assets"
......@@ -961,7 +1361,7 @@ msgid "Select user groups"
msgstr "添加到资产组"
#: perms/forms.py:29 perms/templates/perms/asset_permission_detail.html:155
#: users/forms.py:117
#: users/forms.py:126
#, fuzzy
#| msgid "System user"
msgid "Select system users"
......@@ -974,7 +1374,7 @@ msgid "Private for"
msgstr "ssh密钥"
#: perms/models.py:28 perms/templates/perms/asset_permission_detail.html:80
#: users/models.py:81 users/templates/users/user_detail.html:86
#: users/models.py:89 users/templates/users/user_detail.html:86
msgid "Date expired"
msgstr "失效日期"
......@@ -1022,11 +1422,6 @@ msgstr ""
msgid "Create asset permission "
msgstr "创建资产组"
#: perms/templates/perms/asset_permission_create_update.html:36
#: templates/_nav.html:12 templates/_user_profile.html:14 users/models.py:63
msgid "User"
msgstr "用户"
#: perms/templates/perms/asset_permission_detail.html:60
#: perms/templates/perms/asset_permission_list.html:13
#, fuzzy
......@@ -1091,7 +1486,7 @@ msgstr "创建权限"
msgid "User list of "
msgstr "用户列表"
#: perms/templates/perms/asset_permission_user.html:68 users/models.py:68
#: perms/templates/perms/asset_permission_user.html:68 users/models.py:76
#: users/templates/users/user_detail.html:66
msgid "Email"
msgstr "邮件"
......@@ -1188,24 +1583,16 @@ msgstr "登录"
msgid "Close"
msgstr ""
#: templates/_modal.html:16 users/templates/users/user_detail.html:304
#: users/templates/users/user_detail.html:326
#, fuzzy
#| msgid "Confirm delete"
msgid "Confirm"
msgstr "确认删除"
#: templates/_nav.html:9 users/views.py:102 users/views.py:115
#: users/views.py:155 users/views.py:186 users/views.py:211 users/views.py:224
#: users/views.py:340
#: templates/_nav.html:9 users/templates/users/user_group_create.html:24
#: users/templates/users/user_group_detail.html:102 users/views.py:89
#: users/views.py:102 users/views.py:138 users/views.py:149 users/views.py:159
#: users/views.py:172 users/views.py:197 users/views.py:219 users/views.py:320
msgid "Users"
msgstr "用户管理"
#: templates/_nav.html:13 users/models.py:69
#: users/templates/users/user_detail.html:170
#: users/templates/users/user_list.html:15
msgid "User group"
msgstr "用户组"
#: templates/_nav.html:26
msgid "Label"
msgstr "标签"
#: templates/_nav.html:33 users/templates/users/user_asset_permission.html:23
#: users/templates/users/user_detail.html:24
......@@ -1215,27 +1602,35 @@ msgstr "用户组"
msgid "Asset permission"
msgstr "系统类型"
#: templates/_nav.html:42
#: templates/_nav.html:43
msgid "Job Center"
msgstr "作业中心"
#: templates/_nav.html:47
msgid "Cron"
msgstr "Cron"
#: templates/_nav.html:53
msgid "Audits"
msgstr "审计"
#: templates/_nav.html:47
#: templates/_nav.html:58
msgid "File"
msgstr "文件"
#: templates/_nav.html:50
#: templates/_nav.html:61
msgid "File upload"
msgstr "文件上传"
#: templates/_nav.html:51
#: templates/_nav.html:62
msgid "File download"
msgstr "文件下载"
#: templates/_nav.html:56
#: templates/_nav.html:67
msgid "Settings"
msgstr "设置"
#: templates/_nav.html:61
#: templates/_nav.html:72
msgid "Visit us"
msgstr "访问官网"
......@@ -1243,6 +1638,23 @@ msgstr "访问官网"
msgid "Profile"
msgstr "个人信息"
#: templates/base.html:27
#, python-format
msgid ""
"\n"
" Your information was incomplete. Please click <a href="
"\"%(the_url)s\"> this link </a>to complete your information.\n"
" "
msgstr ""
#: templates/base.html:36
msgid ""
"\n"
" Your ssh-public-key has been expired. Please click <a href="
"\"#\"> this link </a>to update your ssh-public-key.\n"
" "
msgstr ""
#: templates/captcha/image.html:3
msgid "Play CAPTCHA as audio file"
msgstr ""
......@@ -1256,94 +1668,94 @@ msgstr "验证码"
msgid "Filters"
msgstr "过滤"
#: users/forms.py:68
#: users/forms.py:33 users/forms.py:58
#: users/templates/users/user_detail.html:186
msgid "Join user groups"
msgstr "添加到用户组"
#: users/forms.py:75
#, fuzzy
#| msgid "Name"
msgid "name"
msgstr "名称"
#: users/forms.py:69
#: users/forms.py:76
#, fuzzy
#| msgid "Avatar"
msgid "avatar"
msgstr "头像"
#: users/forms.py:70
#: users/forms.py:77
#, fuzzy
#| msgid "Wechat"
msgid "wechat"
msgstr "微信"
#: users/forms.py:71
#: users/forms.py:78
#, fuzzy
#| msgid "Phone"
msgid "phone"
msgstr "手机"
#: users/forms.py:72
#: users/forms.py:79
#, fuzzy
#| msgid "Enable OTP"
msgid "enable otp"
msgstr "二次验证"
#: users/forms.py:77 users/models.py:77
#: users/forms.py:84 users/models.py:85
msgid "ssh public key"
msgstr "ssh公钥"
#: users/forms.py:78
#: users/forms.py:85
msgid "ssh-rsa AAAA..."
msgstr ""
#: users/forms.py:79
#: users/forms.py:86
msgid "Paste your id_ras.pub here."
msgstr ""
#: users/forms.py:90 users/forms.py:93
#: users/forms.py:91
msgid "Public key should not be the same as your old one."
msgstr ""
#: users/forms.py:99 users/forms.py:102 users/serializers.py:32
#: users/serializers.py:35
#, fuzzy
#| msgid "ssh private key"
msgid "Not a valid ssh public key"
msgstr "ssh密钥"
#: users/models.py:62 users/models.py:206
#: users/models.py:70 users/models.py:220
msgid "Administrator"
msgstr "管理员"
#: users/models.py:70 users/templates/users/user_detail.html:82
#: users/templates/users/user_list.html:14
msgid "Role"
msgstr "角色"
#: users/models.py:71
#: users/models.py:79
msgid "Avatar"
msgstr "头像"
#: users/models.py:72 users/templates/users/user_detail.html:77
#: users/models.py:80 users/templates/users/user_detail.html:77
msgid "Wechat"
msgstr "微信"
#: users/models.py:76
#: users/models.py:82 users/templates/users/_user.html:57
#: users/templates/users/user_detail.html:133
msgid "Enable OTP"
msgstr "二次验证"
#: users/models.py:84
msgid "ssh private key"
msgstr "ssh密钥"
#: users/models.py:209
#: users/models.py:223
msgid "Administrator is the super user of system"
msgstr "Administrator是初始的超级管理员"
#: users/models.py:210
msgid "System"
msgstr "系统"
#: users/serializers.py:55
#: users/templates/users/_select_user_modal.html:5
#, fuzzy
#| msgid "ssh private key"
msgid "Not a valid ssh private key."
msgstr "ssh密钥"
#: users/templates/users/_user.html:17
#: users/templates/users/user_create.html:4
#: users/templates/users/user_list.html:5 users/views.py:115
msgid "Create user"
msgstr "创建用户"
#| msgid "Select assets"
msgid "Please Select User"
msgstr "选择资产"
#: users/templates/users/_user.html:33
msgid "Account"
......@@ -1353,33 +1765,87 @@ msgstr "账户"
msgid "Security and Role"
msgstr "角色安全"
#: users/templates/users/_user_reset_pk_modal.html:4
#: users/templates/users/_user_bulk_update_modal.html:4
#, fuzzy
#| msgid "Update user"
msgid "Update User"
msgstr "编辑用户"
#: users/templates/users/_user_bulk_update_modal.html:6
msgid "Hint: only change the field you want to update."
msgstr ""
#: users/templates/users/_user_bulk_update_modal.html:13
#, fuzzy
#| msgid "Admin user"
msgid "Admin"
msgstr "管理用户"
#: users/templates/users/_user_bulk_update_modal.html:19
msgid "Groups"
msgstr ""
#: users/templates/users/_user_bulk_update_modal.html:21
#, fuzzy
#| msgid "Select asset groups"
msgid "Select Group"
msgstr "添加到资产组"
#: users/templates/users/_user_bulk_update_modal.html:31
#, fuzzy
#| msgid "Enable OTP"
msgid "Enable-OTP"
msgstr "二次验证"
#: users/templates/users/_user_import_modal.html:4
msgid "Import User"
msgstr ""
#: users/templates/users/_user_import_modal.html:6
msgid "Hint: your excel should organized in the following format."
msgstr ""
#: users/templates/users/_user_import_modal.html:7
msgid "* You should have a very worksheet named `users`."
msgstr ""
#: users/templates/users/_user_import_modal.html:8
msgid ""
"* Rows in this worksheet: username, email, enable_opt(0, 1), role(one of "
"['Admin', 'User'])"
msgstr ""
#: users/templates/users/_user_import_modal.html:12
msgid "Excel"
msgstr ""
#: users/templates/users/_user_update_pk_modal.html:4
#, fuzzy
#| msgid "SSH private key"
msgid "Reset User SSH Private Key"
msgid "Update User SSH Public Key"
msgstr "ssh密钥"
#: users/templates/users/first_login.html:16 users/views.py:340
#: users/templates/users/first_login.html:18 users/views.py:320
#, fuzzy
#| msgid "Last login"
msgid "First Login"
msgstr "最后登录"
#: users/templates/users/first_login.html:33
#: users/templates/users/first_login.html:35
#, fuzzy
#| msgid "System"
msgid "Step"
msgstr "系统"
#: users/templates/users/first_login.html:55
#: users/templates/users/first_login.html:57
msgid "first step"
msgstr ""
#: users/templates/users/first_login.html:56
#: users/templates/users/first_login.html:58
msgid "prev step"
msgstr ""
#: users/templates/users/first_login.html:58
#: users/templates/users/first_login.html:60
#, fuzzy
#| msgid "Submit"
msgid "submit"
......@@ -1398,6 +1864,11 @@ msgstr "输入您的邮箱, 将会发一封重置短信邮件到您的邮箱中"
msgid "Captcha invalid"
msgstr "验证码错误"
#: users/templates/users/reset_password.html:45
#: users/templates/users/user_detail.html:148 users/utils.py:99
msgid "Reset password"
msgstr "重置密码"
#: users/templates/users/reset_password.html:55
msgid "Password again"
msgstr "再次输入密码"
......@@ -1409,7 +1880,7 @@ msgstr "设置"
#: users/templates/users/user_asset_permission.html:20
#: users/templates/users/user_detail.html:21
#: users/templates/users/user_granted_asset.html:20 users/views.py:186
#: users/templates/users/user_granted_asset.html:20 users/views.py:149
msgid "User detail"
msgstr "用户详情"
......@@ -1477,49 +1948,69 @@ msgstr "生成重置密码连接,通过邮件发送给用户"
msgid "Last login"
msgstr "最后登录"
#: users/templates/users/user_detail.html:236
#: users/templates/users/user_detail.html:156
msgid "Reset ssh key"
msgstr "重置密钥"
#: users/templates/users/user_detail.html:164
#, fuzzy
#| msgid "Update user"
msgid "Update ssh key"
msgstr "编辑用户"
#: users/templates/users/user_detail.html:246
msgid "UserGroup Update Success!"
msgstr ""
#: users/templates/users/user_detail.html:254
#: users/templates/users/user_detail.html:260
#: users/templates/users/user_detail.html:270
#: users/templates/users/user_detail.html:282
#, fuzzy
#| msgid "Create account successfully"
msgid "Update Successfully!"
msgstr "创建账户成功"
#: users/templates/users/user_detail.html:293
#: users/templates/users/user_detail.html:319
msgid "E-mail sent successfully. An e-mail has been sent to the user\\"
msgstr ""
#: users/templates/users/user_detail.html:294
#: users/templates/users/user_detail.html:320
#, fuzzy
#| msgid "Password"
msgid "Password-Reset"
msgstr "密码"
#: users/templates/users/user_detail.html:299
#: users/templates/users/user_detail.html:321
msgid "Are you sure?"
msgstr ""
#: users/templates/users/user_detail.html:300
#: users/templates/users/user_detail.html:322
#: users/templates/users/user_detail.html:330
#: users/templates/users/user_detail.html:355
msgid "This will reset the user\\"
msgstr ""
#: users/templates/users/user_detail.html:315
#: users/templates/users/user_detail.html:344
msgid ""
"The reset-ssh-public-key E-mail has been sent successfully. Please inform "
"the user to update his new ssh public key."
msgstr ""
#: users/templates/users/user_detail.html:316
#: users/templates/users/user_detail.html:345
#, fuzzy
#| msgid "SSH private key"
msgid "SSH-Public-Key Reset"
msgstr "ssh密钥"
#: users/templates/users/user_detail.html:372
msgid "Successfully updated the SSH public key."
msgstr ""
#: users/templates/users/user_detail.html:373
#: users/templates/users/user_detail.html:378
#, fuzzy
#| msgid "SSH private key"
msgid "User SSH Public Key Update"
msgstr "ssh密钥"
#: users/templates/users/user_detail.html:376
msgid "Failed to update the user\\"
msgstr ""
#: users/templates/users/user_granted_asset.html:47
#, fuzzy
#| msgid "Create asset group"
......@@ -1532,11 +2023,111 @@ msgstr "创建资产组"
msgid "Asset groups granted of "
msgstr "资产组列表"
#: users/templates/users/user_group_create.html:16 users/views.py:224
msgid "Create user group"
msgstr "创建用户组"
#: users/templates/users/user_group_create.html:26
#, fuzzy
#| msgid "Select assets"
msgid "Select User"
msgstr "选择资产"
#: users/templates/users/user_group_create.html:38
msgid "Cancel"
msgstr ""
#: users/templates/users/user_group_create.html:39
#, fuzzy
#| msgid "Confirm delete"
msgid "confirm"
msgstr "确认删除"
#: users/templates/users/user_group_detail.html:70 users/views.py:219
#, fuzzy
#| msgid "Asset group list"
msgid "User Group Detail"
msgstr "资产组列表"
#: users/templates/users/user_group_detail.html:98
#, fuzzy
#| msgid "Create asset"
msgid "Created at"
msgstr "创建资产"
#: users/templates/users/user_group_detail.html:121
#, fuzzy
#| msgid "User"
msgid "Add User"
msgstr "用户"
#: users/templates/users/user_group_detail.html:170
msgid "This will delete the current group, but will not delete any user of it."
msgstr ""
#: users/templates/users/user_group_detail.html:223
msgid "The selected users has been added to current group."
msgstr ""
#: users/templates/users/user_group_list.html:18
#, fuzzy
#| msgid "Asset group"
msgid "Add User Group"
msgstr "资产组"
#: users/templates/users/user_group_list.html:26
#, fuzzy
#| msgid "User group"
msgid "User Amount"
msgstr "用户组"
#: users/templates/users/user_group_list.html:27
#, fuzzy
#| msgid "Asset group"
msgid "Asset Amount"
msgstr "资产组"
#: users/templates/users/user_group_list.html:85
#, fuzzy
#| msgid "Delete"
msgid "Group Deleted."
msgstr "删除"
#: users/templates/users/user_group_list.html:86
#: users/templates/users/user_group_list.html:91
#, fuzzy
#| msgid "Delete"
msgid "Group Delete"
msgstr "删除"
#: users/templates/users/user_group_list.html:90
msgid "Group Deleting failed."
msgstr ""
#: users/templates/users/user_group_list.html:103
msgid ""
"This will delete the selected group, but will not remove any user in this "
"group."
msgstr ""
#: users/templates/users/user_group_list.html:126
msgid "This will delete the selected groups !!!"
msgstr ""
#: users/templates/users/user_group_list.html:134
#, fuzzy
#| msgid "User group list"
msgid "UserGroups Deleted."
msgstr "用户组列表"
#: users/templates/users/user_group_list.html:135
#: users/templates/users/user_group_list.html:140
#, fuzzy
#| msgid "User group list"
msgid "UserGroups Delete"
msgstr "用户组列表"
#: users/templates/users/user_update.html:3 users/views.py:155
#: users/templates/users/user_group_list.html:139
msgid "UserGroup Deleting failed."
msgstr ""
#: users/templates/users/user_update.html:3 users/views.py:138
msgid "Update user"
msgstr "编辑用户"
......@@ -1567,8 +2158,8 @@ msgid ""
" <a href=\"%(rest_password_url)s?token=%(rest_password_token)s\">click "
"here to set your password</a>\n"
" </br>\n"
" This link is valid for 1 hour. After it expires, <a href=\"%"
"(forget_password_url)s?email=%(email)s\">request new one</a>\n"
" This link is valid for 1 hour. After it expires, <a href="
"\"%(forget_password_url)s?email=%(email)s\">request new one</a>\n"
"\n"
" </br>\n"
" ---\n"
......@@ -1609,8 +2200,8 @@ msgid ""
" <a href=\"%(rest_password_url)s?token=%(rest_password_token)s\">Click "
"here reset password</a>\n"
" </br>\n"
" This link is valid for 1 hour. After it expires, <a href=\"%"
"(forget_password_url)s?email=%(email)s\">request new one</a>\n"
" This link is valid for 1 hour. After it expires, <a href="
"\"%(forget_password_url)s?email=%(email)s\">request new one<</a>\n"
"\n"
" </br>\n"
" ---\n"
......@@ -1662,57 +2253,90 @@ msgid ""
" "
msgstr ""
#: users/views.py:73
#: users/views.py:75
msgid "Logout success"
msgstr "退出登录成功"
#: users/views.py:74
#: users/views.py:76
msgid "Logout success, return login page"
msgstr "退出登录成功,返回到登录页面"
#: users/views.py:102
#: users/views.py:89
msgid "User list"
msgstr "用户列表"
#: users/views.py:111
#: users/views.py:98
#, fuzzy, python-format
#| msgid "Create user <a href=\"%s\">%s</a> success."
msgid "Create user <a href=\"%s\">%s</a> successfully."
msgstr "创建用户 <a href=\"%s\">%s</a> 成功"
#: users/views.py:211
#: users/views.py:159
msgid "User group list"
msgstr "用户组列表"
#: users/views.py:256
#: users/views.py:172
msgid "Create user group"
msgstr "创建用户组"
#: users/views.py:198
#, fuzzy
#| msgid "Update user"
msgid "Update User Group"
msgstr "编辑用户"
#: users/views.py:235
msgid "Email address invalid, input again"
msgstr "邮箱地址错误,重新输入"
#: users/views.py:267
#: users/views.py:246
msgid "Send reset password message"
msgstr "发送重置密码邮件"
#: users/views.py:268
#: users/views.py:247
msgid "Send reset password mail success, login your mail box and follow it "
msgstr ""
"发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)"
#: users/views.py:280
#: users/views.py:259
msgid "Reset password success"
msgstr "重置密码成功"
#: users/views.py:281
#: users/views.py:260
msgid "Reset password success, return to login page"
msgstr "重置密码成功,返回到登录页面"
#: users/views.py:297 users/views.py:310
#: users/views.py:276 users/views.py:289
msgid "Token invalid or expired"
msgstr "Token错误或失效"
#: users/views.py:306
#: users/views.py:285
msgid "Password not same"
msgstr "密码不一致"
#: users/views.py:447
msgid "Invalid file."
msgstr ""
#: users/views.py:461
#, fuzzy
#| msgid "ssh private key"
msgid "Not a valid Excel file."
msgstr "ssh密钥"
#~ msgid "Network"
#~ msgstr "网络"
#, fuzzy
#~| msgid "Asset number"
#~ msgid "Asset users"
#~ msgstr "资产编号"
#, fuzzy
#~| msgid "Created by"
#~ msgid "Create idc"
#~ msgstr "创建者"
#~ msgid "Admin password"
#~ msgstr "管理员密码"
......@@ -1721,10 +2345,6 @@ msgstr "密码不一致"
#~ msgid "Update system user <a href=\"%s\">%s</a> successfully."
#~ msgstr "创建用户 <a href=\"%s\">%s</a> 成功"
#, fuzzy
#~ msgid "Action"
#~ msgstr "激活"
#, fuzzy
#~| msgid "Create perm"
#~ msgid "Create perm "
......@@ -1733,11 +2353,6 @@ msgstr "密码不一致"
#~ msgid "Create perm"
#~ msgstr "创建权限"
# msgid "Deleted!"
# msgstr "删除"
#~ msgid "has been deleted."
#~ msgstr "已被删除"
#~ msgid "User assets"
#~ msgstr "用户资产"
......
# ~*~ coding: utf-8 ~*~
#
from views import *
\ No newline at end of file
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals, print_function
from rest_framework.exceptions import APIException
from django.utils.translation import ugettext as _
class ServiceUnavailable(APIException):
status_code = default_code = 503
default_detail = _('Service temporarily unavailable, try again later.')
class ServiceNotImplemented(APIException):
status_code = default_code = 501
default_detail = _('This service maybe implemented in the future, but now not implemented!')
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals
from rest_framework import permissions
class AdminUserRequired(permissions.BasePermission):
"""
Custom permission to only allow admin user to access the resource.
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True
# Write permissions are only allowed to the admin role.
return request.user.is_staff
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals
from ops.models import *
from rest_framework import serializers
class HostAliaSerializer(serializers.ModelSerializer):
class Meta:
model = HostAlia
class CmdAliaSerializer(serializers.ModelSerializer):
class Meta:
model = CmdAlia
class UserAliaSerializer(serializers.ModelSerializer):
class Meta:
model = UserAlia
class RunasAliaSerializer(serializers.ModelSerializer):
class Meta:
model = RunasAlia
class ExtraconfSerializer(serializers.ModelSerializer):
class Meta:
model = Extra_conf
class PrivilegeSerializer(serializers.ModelSerializer):
class Meta:
model = Privilege
class SudoSerializer(serializers.ModelSerializer):
class Meta:
model = Sudo
class CronTableSerializer(serializers.ModelSerializer):
class Meta:
model = CronTable
class TaskSerializer(serializers.ModelSerializer):
sub_tasks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Task
read_only_fields = ('record',)
class SubTaskSerializer(serializers.ModelSerializer):
class Meta:
model = SubTask
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals
from rest_framework import viewsets
from serializers import *
from permissions import *
__all__ = ["HostAliaViewSet",
"CmdAliaViewSet",
"UserAliaViewSet",
"RunasAliaViewSet",
"ExtraconfViewSet",
"PrivilegeViewSet",
"SudoViewSet",
"CronTableViewSet",
"TaskViewSet",
"SubTaskViewSet",
]
class HostAliaViewSet(viewsets.ModelViewSet):
queryset = HostAlia.objects.all()
serializer_class = HostAliaSerializer
permission_classes = (AdminUserRequired,)
class CmdAliaViewSet(viewsets.ModelViewSet):
queryset = CmdAlia.objects.all()
serializer_class = CmdAliaSerializer
permission_classes = (AdminUserRequired,)
class UserAliaViewSet(viewsets.ModelViewSet):
queryset = UserAlia.objects.all()
serializer_class = UserAliaSerializer
permission_classes = (AdminUserRequired,)
class RunasAliaViewSet(viewsets.ModelViewSet):
queryset = RunasAlia.objects.all()
serializer_class = RunasAliaSerializer
permission_classes = (AdminUserRequired,)
class ExtraconfViewSet(viewsets.ModelViewSet):
queryset = Extra_conf.objects.all()
serializer_class = ExtraconfSerializer
permission_classes = (AdminUserRequired,)
class PrivilegeViewSet(viewsets.ModelViewSet):
queryset = Privilege.objects.all()
serializer_class = PrivilegeSerializer
permission_classes = (AdminUserRequired,)
class SudoViewSet(viewsets.ModelViewSet):
queryset = Sudo.objects.all()
serializer_class = SudoSerializer
permission_classes = (AdminUserRequired,)
class CronTableViewSet(viewsets.ModelViewSet):
queryset = CronTable.objects.all()
serializer_class = CronTableSerializer
permission_classes = (AdminUserRequired,)
class TaskViewSet(viewsets.ModelViewSet):
queryset = Task.objects.all()
serializer_class = TaskSerializer
permission_classes = (AdminUserRequired,)
class SubTaskViewSet(viewsets.ModelViewSet):
queryset = SubTask.objects.all()
serializer_class = SubTaskSerializer
permission_classes = (AdminUserRequired,)
from __future__ import unicode_literals
from django.db import models
# Create your models here.
from ansible import *
from cron import *
from sudo import *
from utils import *
from task import *
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals, absolute_import
import logging
import json
from django.db import models
from django.utils.translation import ugettext_lazy as _
__all__ = ["TaskRecord", "AnsiblePlay", "AnsibleTask", "AnsibleHostResult"]
logger = logging.getLogger(__name__)
class TaskRecord(models.Model):
uuid = models.CharField(max_length=128, verbose_name=_('UUID'), primary_key=True)
name = models.CharField(max_length=128, blank=True, verbose_name=_('Name'))
start = models.DateTimeField(auto_now_add=True, verbose_name=_('Start Time'))
end = models.DateTimeField(blank=True, null=True, verbose_name=_('End Time'))
exit_code = models.IntegerField(default=0, verbose_name=_('Exit Code'))
completed = models.BooleanField(default=False, verbose_name=_('Is Completed'))
hosts = models.TextField(blank=True, null=True, verbose_name=_('Hosts'))
def __unicode__(self):
return "%s" % self.uuid
@property
def total_hosts(self):
return self.hosts.split(',')
@classmethod
def generate_fake(cls, count=20):
from random import seed
from uuid import uuid4
import forgery_py
seed()
for i in range(count):
tasker = cls(uuid=str(uuid4()),
name=forgery_py.name.full_name(),
)
try:
tasker.save()
logger.debug('Generate fake tasker: %s' % tasker.name)
except Exception as e:
print('Error: %s, continue...' % e.message)
continue
class AnsiblePlay(models.Model):
tasker = models.ForeignKey(TaskRecord, related_name='plays', blank=True, null=True)
uuid = models.CharField(max_length=128, verbose_name=_('UUID'), primary_key=True)
name = models.CharField(max_length=128, verbose_name=_('Name'))
def __unicode__(self):
return "%s<%s>" % (self.name, self.uuid)
def to_dict(self):
return {"uuid": self.uuid, "name": self.name}
@classmethod
def generate_fake(cls, count=20):
from random import seed, choice
from uuid import uuid4
import forgery_py
seed()
for i in range(count):
play = cls(uuid=str(uuid4()),
name=forgery_py.name.full_name(),
)
try:
play.tasker = choice(TaskRecord.objects.all())
play.save()
logger.debug('Generate fake play: %s' % play.name)
except Exception as e:
print('Error: %s, continue...' % e.message)
continue
class AnsibleTask(models.Model):
play = models.ForeignKey(AnsiblePlay, related_name='tasks', blank=True, null=True)
uuid = models.CharField(max_length=128, verbose_name=_('UUID'), primary_key=True)
name = models.CharField(max_length=128, blank=True, verbose_name=_('Name'))
def __unicode__(self):
return "%s<%s>" % (self.name, self.uuid)
def to_dict(self):
return {"uuid": self.uuid, "name": self.name}
def failed(self):
pass
def success(self):
pass
@classmethod
def generate_fake(cls, count=20):
from random import seed, choice
from uuid import uuid4
import forgery_py
seed()
for i in range(count):
task = cls(uuid=str(uuid4()),
name=forgery_py.name.full_name(),
)
try:
task.play = choice(AnsiblePlay.objects.all())
task.save()
logger.debug('Generate fake play: %s' % task.name)
except Exception as e:
print('Error: %s, continue...' % e.message)
continue
class AnsibleHostResult(models.Model):
task = models.ForeignKey(AnsibleTask, related_name='host_results', blank=True, null=True)
name = models.CharField(max_length=128, blank=True, verbose_name=_('Name'))
success = models.TextField(blank=True, verbose_name=_('Success'))
skipped = models.TextField(blank=True, verbose_name=_('Skipped'))
failed = models.TextField(blank=True, verbose_name=_('Failed'))
unreachable = models.TextField(blank=True, verbose_name=_('Unreachable'))
no_host = models.TextField(blank=True, verbose_name=_('NoHost'))
def __unicode__(self):
return "%s %s<%s>" % (self.name, str(self.is_success), self.task.uuid)
@property
def is_failed(self):
if self.failed or self.unreachable or self.no_host:
return True
return False
@property
def is_success(self):
return not self.is_failed
@property
def _success_data(self):
if self.success:
return json.loads(self.success)
elif self.skipped:
return json.loads(self.skipped)
@property
def _failed_data(self):
if self.failed:
return json.loads(self.failed)
elif self.unreachable:
return json.loads(self.unreachable)
elif self.no_host:
return {"msg": self.no_host}
@property
def failed_msg(self):
return self._failed_data.get("msg")
@staticmethod
def __filter_disk(ansible_devices, exclude_devices):
"""
过滤磁盘设备,丢弃掉不需要的设备
:param ansible_devices: 对应的facts字段
:param exclude_devices: <list> 一个需要被丢弃的设备,匹配规则是startwith, 比如需要丢弃sr0子类的 ['sr']
:return: <dict> 过滤获取的结果
"""
for start_str in exclude_devices:
for key in ansible_devices.keys():
if key.startswith(start_str):
ansible_devices.pop(key)
return ansible_devices
@staticmethod
def __filter_interface(ansible_interfaces, exclude_interface):
"""
过滤网卡设备,丢弃掉不需要的网卡, 比如lo
:param ansible_interface: 对应的facts字段
:param exclude_interface: <list> 一个需要被丢弃的设备,匹配规则是startwith, 比如需要丢弃lo子类的 ['lo']
:return: <dict> 过滤获取的结果
"""
for interface in ansible_interfaces:
for start_str in exclude_interface:
if interface.startswith(start_str):
i = ansible_interfaces.index(interface)
ansible_interfaces.pop(i)
return ansible_interfaces
@staticmethod
def __gather_interface(facts, interfaces):
"""
收集所有interface的具体信息
:param facts: ansible faces
:param interfaces: 需要收集的intreface列表
:return: <dict> interface的详情
"""
result = {}
for key in interfaces:
gather_key = "ansible_" + key
if gather_key in facts.keys():
result[key] = facts.get(gather_key)
return result
def __deal_setup(self):
"""
处理ansible setup模块收集到的数据,提取资产需要的部分
:return: <dict> {"msg": <str>, "data": <dict>}, 注意msg是异常信息, 有msg时 data为None
"""
result = self._success_data
module_name = result['invocation'].get('module_name') if result.get('invocation') else None
if module_name is not None:
if module_name != "setup":
return {"msg": "the property only for ansible setup module result!, can't support other module", "data":None}
else:
data = {}
facts =result.get('ansible_facts')
interfaces = self.__filter_interface(facts.get('ansible_interfaces'), ['lo'])
cpu_describe = "%s %s" % (facts.get('ansible_processor')[0], facts.get('ansible_processor')[1]) if len(facts.get('ansible_processor')) >= 2 else ""
data['sn'] = facts.get('ansible_product_serial')
data['env'] = facts.get('ansible_env')
data['os'] = "%s %s(%s)" % (facts.get('ansible_distribution'),
facts.get('ansible_distribution_version'),
facts.get('ansible_distribution_release'))
data['mem'] = facts.get('ansible_memtotal_mb')
data['cpu'] = "%s %d核" % (cpu_describe, facts.get('ansible_processor_count'))
data['disk'] = self.__filter_disk(facts.get('ansible_devices'), ['sr'])
data['interface'] = self.__gather_interface(facts, interfaces)
return {"msg": None, "data": data}
else:
return {"msg": "there result isn't ansible setup module result! can't process this data format", "data": None}
@property
def deal_setup(self):
try:
return self.__deal_setup()
except Exception as e:
return {"msg": "deal with setup data failed, %s" % e.message, "data": None}
def __deal_ping(self):
"""
处理ansible ping模块收集到的数据
:return: <dict> {"msg": <str>, "data": {"success": <bool>}}, 注意msg是异常信息, 有msg时 data为None
"""
result = self._success_data
module_name = result['invocation'].get('module_name') if result.get('invocation') else None
if module_name is not None:
if module_name != "ping":
return {"msg": "the property only for ansible setup module result!, can't support other module", "data":None}
else:
ping = True if result.get('ping') == "pong" else False
return {"msg": None, "data": {"success": ping}}
else:
return {"msg": "there isn't module_name field! can't process this data format", "data": None}
@property
def deal_ping(self):
try:
return self.__deal_ping()
except Exception as e:
return {"msg": "deal with ping data failed, %s" % e.message, "data": None}
@classmethod
def generate_fake(cls, count=20):
from random import seed, choice
import forgery_py
seed()
for i in range(count):
result = cls(name=forgery_py.name.full_name(),
success=forgery_py.lorem_ipsum.sentence(),
failed=forgery_py.lorem_ipsum.sentence(),
skipped=forgery_py.lorem_ipsum.sentence(),
unreachable=forgery_py.lorem_ipsum.sentence(),
no_host=forgery_py.lorem_ipsum.sentence(),
)
try:
result.task = choice(AnsibleTask.objects.all())
result.save()
logger.debug('Generate fake HostResult: %s' % result.name)
except Exception as e:
print('Error: %s, continue...' % e.message)
continue
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals, absolute_import
import logging
from django.db import models
from assets.models import Asset
from django.utils.translation import ugettext_lazy as _
logger = logging.getLogger(__name__)
__all__ = ["CronTable"]
class CronTable(models.Model):
name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('Name'),
help_text=_("Description of a crontab entry"))
month = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Month'),
help_text=_("Month of the year the job should run ( 1-12, *, */2, etc )"))
weekday = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('WeekDay'),
help_text=_("Day of the week that the job should run"
" ( 0-6 for Sunday-Saturday, *, etc )"))
day = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Day'),
help_text=_("Day of the month the job should run ( 1-31, *, */2, etc )"))
hour = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Hour'),
help_text=_("Hour when the job should run ( 0-23, *, */2, etc )"))
minute = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Minute'),
help_text=_("Minute when the job should run ( 0-59, *, */2, etc )"))
job = models.CharField(max_length=4096, blank=True, null=True, verbose_name=_('Job'),
help_text=_("The command to execute or, if env is set, the value of "
"environment variable. Required if state=present."))
user = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('User'),
help_text=_("The specific user whose crontab should be modified."))
asset = models.ForeignKey(Asset, null=True, blank=True, related_name='crontables')
@property
def describe(self):
return "http://docs.ansible.com/ansible/cron_module.html"
@classmethod
def generate_fake(cls, count=20):
from random import seed, choice
import forgery_py
seed()
for i in range(count):
cron = cls(name=forgery_py.name.full_name(),
month=str(choice(range(1,13))),
weekday=str(choice(range(0,7))),
day=str(choice(range(1,32))),
hour=str(choice(range(0,24))),
minute=str(choice(range(0,60))),
job=forgery_py.lorem_ipsum.sentence(),
user=forgery_py.name.first_name(),
)
try:
cron.save()
logger.debug('Generate fake cron: %s' % cron.name)
except Exception as e:
print('Error: %s, continue...' % e.message)
continue
\ No newline at end of file
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals, absolute_import
import logging
from jinja2 import Template
from django.db import models
from django.utils.timezone import now
from assets.models import Asset, AssetGroup
from django.utils.translation import ugettext_lazy as _
logger = logging.getLogger(__name__)
__all__ = ["HostAlia", "UserAlia", "CmdAlia", "RunasAlia", "Privilege", "Extra_conf", "Sudo"]
class HostAlia(models.Model):
name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('Host_Alias'))
host_items = models.TextField(blank=True, null=True, verbose_name=_('Host_Items'))
def __unicode__(self):
return self.name
@classmethod
def generate_fake(cls, count=20):
from random import seed
import forgery_py
seed()
for i in range(count):
hostA = cls(name=forgery_py.name.full_name(),
host_items=forgery_py.lorem_ipsum.sentence(),
)
try:
hostA.save()
logger.debug('Generate fake host alia: %s' % hostA.name)
except Exception as e:
print('Error: %s, continue...' % e.message)
continue
class UserAlia(models.Model):
name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('User_Alias'))
user_items = models.TextField(blank=True, null=True, verbose_name=_('Host_Items'))
def __unicode__(self):
return self.name
@classmethod
def generate_fake(cls, count=20):
from random import seed
import forgery_py
seed()
for i in range(count):
userA = cls(name=forgery_py.name.full_name(),
user_items=forgery_py.lorem_ipsum.sentence(),
)
try:
userA.save()
logger.debug('Generate fake host alia: %s' % userA.name)
except Exception as e:
print('Error: %s, continue...' % e.message)
continue
class CmdAlia(models.Model):
name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('Command_Alias'))
cmd_items = models.TextField(blank=True, null=True, verbose_name=_('Host_Items'))
def __unicode__(self):
return self.name
@classmethod
def generate_fake(cls, count=20):
from random import seed
import forgery_py
seed()
for i in range(count):
cmdA = cls(name=forgery_py.name.full_name(),
cmd_items=forgery_py.lorem_ipsum.sentence(),
)
try:
cmdA.save()
logger.debug('Generate fake command alia: %s' % cmdA.name)
except Exception as e:
print('Error: %s, continue...' % e.message)
continue
class RunasAlia(models.Model):
name = models.CharField(max_length=128, blank=True, null=True, unique=True, verbose_name=_('Runas_Alias'))
runas_items = models.TextField(blank=True, null=True, verbose_name=_('Host_Items'))
def __unicode__(self):
return self.name
@classmethod
def generate_fake(cls, count=20):
from random import seed
import forgery_py
seed()
for i in range(count):
runas = cls(name=forgery_py.name.full_name(),
runas_items=forgery_py.lorem_ipsum.sentence(),
)
try:
runas.save()
logger.debug('Generate fake RunAs alia: %s' % runas.name)
except Exception as e:
print('Error: %s, continue...' % e.message)
continue
class Privilege(models.Model):
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
user = models.ForeignKey(UserAlia, blank=True, null=True, related_name='privileges')
host = models.ForeignKey(HostAlia, blank=True, null=True, related_name='privileges')
runas = models.ForeignKey(RunasAlia, blank=True, null=True, related_name='privileges')
command = models.ForeignKey(CmdAlia, blank=True, null=True, related_name='privileges')
nopassword = models.BooleanField(default=True, verbose_name=_('Is_NoPassword'))
comment = models.TextField(blank=True, null=True, verbose_name=_('Comment'))
def __unicode__(self):
return "[%s %s %s %s %s]" % (self.user.name,
self.host.name,
self.runas.name,
self.command.name,
self.nopassword)
def to_tuple(self):
return self.user.name, self.host.name, self.runas.name, self.command.name, self.nopassword
@classmethod
def generate_fake(cls, count=20):
from random import seed, choice
import forgery_py
seed()
for i in range(count):
pri = cls(name=forgery_py.name.full_name(),
comment=forgery_py.lorem_ipsum.sentence(),
)
try:
pri.user = choice(UserAlia.objects.all())
pri.host = choice(HostAlia.objects.all())
pri.runas = choice(RunasAlia.objects.all())
pri.command = choice(CmdAlia.objects.all())
pri.save()
logger.debug('Generate fake privileges: %s' % pri.name)
except Exception as e:
print('Error: %s, continue...' % e.message)
continue
class Extra_conf(models.Model):
line = models.TextField(blank=True, null=True, verbose_name=_('Extra_Item'),
help_text=_('The extra sudo config line.'))
def __unicode__(self):
return self.line
class Sudo(models.Model):
"""
Sudo配置文件对象, 用于配置sudo的配置文件
:param extra_lines: <list> [<line1>, <line2>,...]
:param privileges: <list> [(user, host, runas, command, nopassword),]
"""
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'),
help_text=_('Name for this sudo'))
created_time = models.DateTimeField(verbose_name=_('Created Time'), auto_created=True,
help_text=_('The create time of this sudo'))
modify_time = models.DateTimeField(auto_now=True, verbose_name=_('Modify Time'),
help_text=_('The recent modify time of this sudo'))
assets = models.ManyToManyField(Asset, blank=True, related_name='sudos')
asset_groups = models.ManyToManyField(AssetGroup, blank=True, related_name='sudos')
extra_lines = models.ManyToManyField(Extra_conf, related_name='sudos', blank=True)
privilege_items = models.ManyToManyField(Privilege, related_name='sudos', blank=True)
@property
def all_assets(self):
assets = list(self.assets.all())
for group in self.asset_groups.all():
for asset in group.assets.all():
if asset not in assets:
assets.append(asset)
return assets
@property
def users(self):
return {privilege.user.name: privilege.user.user_items.split(',') for privilege in self.privilege_items.all()}
@property
def commands(self):
return {privilege.command.name: privilege.command.cmd_items.split(',') for privilege in self.privilege_items.all()}
@property
def hosts(self):
return {privilege.host.name: privilege.host.host_items.split(',') for privilege in self.privilege_items.all()}
@property
def runas(self):
return {privilege.runas.name: privilege.runas.runas_items.split(',') for privilege in self.privilege_items.all()}
@property
def extras(self):
return [extra.line for extra in self.extra_lines.all()]
@property
def privileges(self):
return [privilege.to_tuple() for privilege in self.privilege_items.all()]
@property
def content(self):
template = Template(self.__sudoers_jinja2_tmp__)
context = {"User_Alias": self.users,
"Cmnd_Alias": self.commands,
"Host_Alias": self.hosts,
"Runas_Alias": self.runas,
"Extra_Lines": self.extras,
"Privileges": self.privileges}
return template.render(context)
@property
def __sudoers_jinja2_tmp__(self):
return """# management by JumpServer
# This file MUST be edited with the 'visudo' command as root.
#
# Please consider adding local content in /etc/sudoers.d/ instead of
# directly modifying this file.
#
# See the man page for details on how to write a sudoers file.
#
Defaults env_reset
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
# JumpServer Generate Other Configure is here
{% if Extra_Lines -%}
{% for line in Extra_Lines -%}
{{ line }}
{% endfor %}
{%- endif %}
# Host alias specification
{% if Host_Alias -%}
{% for flag, items in Host_Alias.iteritems() -%}
Host_Alias {{ flag }} = {{ items|join(', ') }}
{% endfor %}
{%- endif %}
# User alias specification
{% if User_Alias -%}
{% for flag, items in User_Alias.iteritems() -%}
User_Alias {{ flag }} = {{ items|join(', ') }}
{% endfor %}
{%- endif %}
# Cmnd alias specification
{% if Cmnd_Alias -%}
{% for flag, items in Cmnd_Alias.iteritems() -%}
Cmnd_Alias {{ flag }} = {{ items|join(', ') }}
{% endfor %}
{%- endif %}
# Run as alias specification
{% if Runas_Alias -%}
{% for flag, items in Runas_Alias.iteritems() -%}
Runas_Alias {{ flag }} = {{ items|join(', ') }}
{% endfor %}
{%- endif %}
# User privilege specification
root ALL=(ALL:ALL) ALL
# JumpServer Generate User privilege is here.
# Note privileges is a tuple list like [(user, host, runas, command, nopassword),]
{% if Privileges -%}
{% for User_Flag, Host_Flag, Runas_Flag, Command_Flag, NopassWord in Privileges -%}
{% if NopassWord -%}
{{ User_Flag }} {{ Host_Flag }}=({{ Runas_Flag }}) NOPASSWD: {{ Command_Flag }}
{%- else -%}
{{ User_Flag }} {{ Host_Flag }}=({{ Runas_Flag }}) {{ Command_Flag }}
{%- endif %}
{% endfor %}
{%- endif %}
# Members of the admin group may gain root privileges
%admin ALL=(ALL) ALL
# Allow members of group sudo to execute any command
%sudo ALL=(ALL:ALL) ALL
# See sudoers(5) for more information on "#include" directives:
#includedir /etc/sudoers.d
"""
@classmethod
def generate_fake(cls, count=20):
from random import seed, choice
import forgery_py
seed()
for i in range(count):
sudo = cls(name=forgery_py.name.full_name(),
created_time=now()
)
try:
sudo.save()
sudo.privilege_items = [choice(Privilege.objects.all())]
sudo.save()
logger.debug('Generate fake cron: %s' % sudo.name)
except Exception as e:
print('Error: %s, continue...' % e.message)
continue
\ No newline at end of file
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals, absolute_import
import logging
from uuid import uuid4
from assets.models import Asset
from ops.models import TaskRecord
from ops.utils.ansible_api import ADHocRunner, Config
from django.db import models
from django.utils.translation import ugettext_lazy as _
__all__ = ["Task", "SubTask"]
logger = logging.getLogger(__name__)
class Task(models.Model):
record = models.OneToOneField(TaskRecord)
name = models.CharField(max_length=128, blank=True, verbose_name=_('Name'))
is_gather_facts = models.BooleanField(default=False,verbose_name=_('Is Gather Ansible Facts'))
assets = models.ManyToManyField(Asset, related_name='tasks')
def __unicode__(self):
return "%s" % self.name
@property
def ansible_assets(self):
return []
def run(self):
conf = Config()
gather_facts = "yes" if self.is_gather_facts else "no"
play_source = {
"name": "Ansible Play",
"hosts": "default",
"gather_facts": gather_facts,
"tasks": [
dict(action=dict(module='ping')),
]
}
hoc = ADHocRunner(conf, play_source, *self.ansible_assets)
uuid = "tasker-" + uuid4().hex
ext_code, result = hoc.run("test_task", uuid)
print(ext_code)
print(result)
class SubTask(models.Model):
task = models.ForeignKey(Task, related_name='sub_tasks', verbose_name=_('Ansible Task'))
module_name = models.CharField(max_length=128, verbose_name=_('Ansible Module Name'))
module_args = models.CharField(max_length=512, blank=True, verbose_name=_("Ansible Module Args"))
register = models.CharField(max_length=128, blank=True, verbose_name=_('Ansible Task Register'))
def __unicode__(self):
return "%s %s" % (self.module_name, self.module_args)
\ No newline at end of file
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals
from ansible import *
from cron import *
from sudo import *
__all__ = ["generate_fake"]
def generate_fake():
for cls in (TaskRecord, AnsiblePlay, AnsibleTask, AnsibleHostResult, CronTable,
HostAlia, UserAlia, CmdAlia, RunasAlia, Privilege, Sudo):
cls.generate_fake()
\ No newline at end of file
from .tasks import longtime_add
import time
result = longtime_add.delay(1,2)
print 'Task finished? ', result.ready()
print 'Task result: ', result.result
time.sleep(10)
print 'Task finished? ', result.ready()
print 'Task result: ', result.result
\ No newline at end of file
from __future__ import absolute_import
import time
from celery import shared_task
from common import celery_app
@shared_task
def longtime_add(x, y):
print 'long time task begins'
# sleep 5 seconds
time.sleep(5)
print 'long time task finished'
return x + y
@celery_app.task(name='hello-world')
def hello():
print('hello world!')
from taskers import *
\ No newline at end of file
from __future__ import absolute_import, unicode_literals
from celery import shared_task
from common import celery_app
from ops.utils.ansible_api import Config, ADHocRunner
@shared_task(name="get_asset_hardware_info")
def get_asset_hardware_info(task_name, task_uuid, *assets):
conf = Config()
play_source = {
"name": "Get host hardware information",
"hosts": "default",
"gather_facts": "no",
"tasks": [
dict(action=dict(module='setup'))
]
}
hoc = ADHocRunner(conf, play_source, *assets)
ext_code, result = hoc.run(task_name, task_uuid)
return ext_code, result
@shared_task(name="asset_test_ping_check")
def asset_test_ping_check(task_name, task_uuid, *assets):
conf = Config()
play_source = {
"name": "Test host connection use ping",
"hosts": "default",
"gather_facts": "no",
"tasks": [
dict(action=dict(module='ping'))
]
}
hoc = ADHocRunner(conf, play_source, *assets)
ext_code, result = hoc.run(task_name, task_uuid)
return ext_code, result
@shared_task(name="add_user_to_assert")
def add_user_to_asset():
pass
@celery_app.task(name='hello-world')
def hello():
print('hello world!')
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals
from ops.tasks import _celery_tasks
from ops.models import TaskRecord
from uuid import uuid1
from celery.result import AsyncResult
__all__ = ["get_result",
"start_get_hardware_info",
"start_ping_test",
"get_hardware_info",
"get_ping_test"]
def get_result(task_id):
result = AsyncResult(task_id)
if result.ready():
return {"Completed": True, "data": result.get()}
else:
return {"Completed": False, "data": None}
def __get_result_by_tasker_id(tasker_uuid, deal_method):
tasker = TaskRecord.objects.get(uuid=tasker_uuid)
total = tasker.total_hosts
total_len = len(total)
host_results = []
# 存储数据
for play in tasker.plays.all():
for t in play.tasks.all():
task = {'name': t.name, 'uuid': t.uuid, 'percentage': 0, 'completed': {'success': {}, 'failed': {}}}
completed = []
count = 0
for h in t.host_results.all():
completed.append(h.name)
count += 1
if h.is_success:
result = getattr(h, deal_method)
if result.get('msg') is None:
task['completed']['success'][h.name] = result.get('data')
else:
task['completed']['failed'][h.name] = result.get('msg')
else:
task['completed']['failed'][h.name] = h.failed_msg
# 计算进度
task['percentage'] = float(count * 100 / total_len)
task['waited'] = list(set(total) - set(completed))
host_results.append(task)
return host_results
def start_get_hardware_info(*assets):
name = "Get host hardware information"
uuid = "tasker-" + uuid1().hex
_celery_tasks.get_asset_hardware_info.delay(name, uuid, *assets)
return uuid
def __get_hardware_info(tasker_uuid):
return __get_result_by_tasker_id(tasker_uuid, 'deal_setup')
def get_hardware_info(tasker_uuid):
"""
:param assets: 资产列表
:return: 返回数据结构样列
{u'data': [{u'completed': {
u'failed': {u'192.168.232.135': u'Authentication failure.'},
u'success': {u'192.168.1.119': {u'cpu': u'GenuineIntel Intel Xeon E312xx (Sandy Bridge) 6\u6838',
u'disk': {<device_name>: <device_detail_dict>},
u'env': {<env_name>: <env_value>},
u'interface': {<interface_name>: <interface_detail_dict>},
u'mem': 3951,
u'os': u'Ubuntu 16.04(xenial)',
u'sn': u'NA'}}},
u'name': u'',
u'percentage': 100.0,
u'uuid': u'87cfedfe-ba55-44ff-bc43-e7e73b869ca1',
u'waited': []}
],
u'msg': None}
"""
try:
return {"msg": None, "data": __get_hardware_info(tasker_uuid)}
except Exception as e:
return {"msg": "query data failed!, %s" % e.message, "data": None}
def start_ping_test(*assets):
name = "Test host connection"
uuid = "tasker-" + uuid1().hex
_celery_tasks.asset_test_ping_check.delay(name, uuid, *assets)
return uuid
def __get_ping_test(tasker_uuid):
return __get_result_by_tasker_id(tasker_uuid, 'deal_ping')
def get_ping_test(tasker_uuid):
"""
:param assets: 资产列表
:return: 返回数据结构样列
{u'data': [{u'completed': {
u'failed': {u'192.168.232.135': u'Authentication failure.'},
u'success': {u'192.168.1.119': {u'success': True}}},
u'name': u'',
u'percentage': 100.0,
u'uuid': u'3e6e0d3b-bee0-4383-b19e-bec6ba55d346',
u'waited': []}
],
u'msg': None}
"""
try:
return {"msg": None, "data": __get_ping_test(tasker_uuid)}
except Exception as e:
return {"msg": "query data failed!, %s" % e.message, "data": None}
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load bootstrap %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<link href="{% static "css/plugins/datepicker/datepicker3.css" %}" rel="stylesheet">
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>{% block user_template_title %}{% trans 'Create user' %}{% endblock %}</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<form method="post" class="form-horizontal" action="" enctype="multipart/form-data">
{% csrf_token %}
<h3>{% trans 'Account' %}</h3>
{% block username %} {% endblock %}
{{ form.email|bootstrap_horizontal }}
{{ form.name|bootstrap_horizontal }}
{{ form.groups|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
{% block password %} {% endblock %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Security and Role' %}</h3>
{{ form.role|bootstrap_horizontal }}
<div class="form-group {% if form.date_expired.errors %} has-error {% endif %}" id="date_5">
<label for="{{ form.date_expired.id_for_label }}" class="col-sm-2 control-label">{{ form.date_expired.label }}</label>
<div class="col-sm-9">
<div class="input-group date">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input id="{{ form.date_expired.id_for_label }}" name="{{ form.date_expired.html_name }}" type="text" class="form-control" value="{{ form.date_expired.value|date:'Y-m-d' }}">
</div>
<span class="help-block ">{{ form.date_expired.errors }}</span>
</div>
</div>
{# {{ form.date_expired|bootstrap_horizontal }}#}
<div class="form-group">
<label for="{{ form.enable_otp.id_for_label }}" class="col-sm-2 control-label">{% trans 'Enable OTP' %}</label>
<div class="col-sm-8">
{{ form.enable_otp }}
</div>
</div>
<div class="hr-line-dashed"></div>
<h3>{% trans 'Profile' %}</h3>
{{ form.phone|bootstrap_horizontal }}
{{ form.wechat|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/datapicker/bootstrap-datepicker.js' %}"></script>
<script>
$(document).ready(function () {
$('.select2').select2();
$('.input-group.date').datepicker({
format: "yyyy-mm-dd",
todayBtn: "linked",
keyboardNavigation: false,
forceParse: false,
calendarWeeks: true,
autoclose: true
});
})
</script>
{% endblock %}
{% extends 'cron/_cron.html' %}
{% load i18n %}
{% load bootstrap %}
{% block user_template_title %}{% trans "Create user" %}{% endblock %}
{% block username %}
{{ form.username|bootstrap_horizontal }}
{% endblock %}
{% block password %}
<h3>{% trans 'Password' %}</h3>
<div class="form-group">
<label class="col-sm-2 control-label">{% trans 'Password' %}</label>
<div class="col-sm-8 controls" >
{% trans 'Reset link will be generated and sent to the user. ' %}
</div>
</div>
{% endblock %}
\ No newline at end of file
{% extends 'base.html' %}
{% load common_tags %}
{% load users_tags %}
{% load static %}
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li class="active">
<a href="{% url 'ops:page-cron-detail' pk=cron.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Cron detail' %} </a>
</li>
<li>
<a href="{% url 'ops:page-cron-detail' pk=cron.id %}" class="text-center"><i class="fa fa-bar-chart-o"></i> {% trans 'Asset permission' %}</a>
</li>
<li><a href="{% url 'ops:page-cron-detail' pk=cron.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a></li>
<li><a href="{% url 'ops:page-cron-detail' pk=cron.id %}" class="text-center"><i class="fa fa-calculator-o"></i> {% trans 'Login history' %}</a></li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-7" style="padding-left: 0">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ cron.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table">
<tbody>
<tr>
<td width="20%">{% trans 'Name' %}:</td>
<td><b>{{ cron.name }}</b></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Quick modify' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td width="50%">{% trans 'Active' %}:</td>
<td><span class="pull-right">
<div class="switch">
<div class="onoffswitch">
<input type="checkbox" {% if cron.is_active %} checked {% endif %} class="onoffswitch-checkbox" id="is_active">
<label class="onoffswitch-label" for="is_active">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
</span></td>
</tr>
<tr>
<td>{% trans 'Enable OTP' %}:</td>
<td><span class="pull-right">
<div class="switch">
<div class="onoffswitch">
<input type="checkbox" class="onoffswitch-checkbox" {% if cron.enable_otp %} checked {% endif %}
id="enable_otp">
<label class="onoffswitch-label" for="enable_otp">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
</span></td>
</tr>
<tr>
<td>{% trans 'Reset password' %}:</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" id="btn_reset_password" style="width: 54px">{% trans 'Reset' %}</button>
</span>
</td>
</tr>
<tr>
<td>{% trans 'Reset ssh key' %}:</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" id="btn_reset_pk" style="width: 54px;">{% trans 'Reset' %}</button>
</span>
</td>
</tr>
<tr>
<td>{% trans 'Update ssh key' %}:</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" id="btn_update_pk" style="width: 54px;" data-toggle="modal" data-target="#user_update_pk_modal">{% trans 'Update' %}</button>
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'User group' %}
</div>
<div class="panel-body">
<table class="table group_edit">
<tbody>
<form>
<tr>
<td colspan="2" class="no-borders">
<select data-placeholder="{% trans 'Join user groups' %}" id="slct_groups" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for group in groups %}
<option value="{{ group.id }}" id="opt_{{ group.id }}">{{ group.name }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-info btn-small" id="btn_add_user_group">{% trans 'Join' %}</button>
</td>
</tr>
</form>
{% for group in user_object.groups.all %}
<tr>
<td ><b class="bdg_user_group" data-gid={{ group.id }}>{{ group.name }}</b></td>
<td>
<button class="btn btn-danger pull-right btn-xs btn_delete_user_group" type="button"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% include 'users/_user_update_pk_modal.html' %}
{% endblock %}
{% block custom_foot_js %}
<script>
jumpserver.selected_groups = {};
function updateCrons(crons) {
var the_url = "{% url 'api-ops:crontable-detail' pk=cron.id %}";
var body = {
id: {{ user_object.id }},
groups: Object.assign([], crons)
};
var success = function(data) {
// remove all the selected groups from select > option and rendered ul element;
$('.select2-selection__rendered').empty();
$('#slct_groups').val('');
$.map(jumpserver.selected_groups, function(group_name, index) {
$('#opt_' + index).remove();
// change tr html of user groups.
$('.group_edit tbody').append(
'<tr>' +
'<td><b class="bdg_user_group" data-gid="' + index + '">' + group_name + '</b></td>' +
'<td><button class="btn btn-danger btn-xs pull-right btn_delete_user_group" type="button"><i class="fa fa-minus"></i></button></td>' +
'</tr>'
)
});
// clear jumpserver.selected_groups
jumpserver.selected_groups = {};
toastr.success('{% trans "UserGroup Update Success!" %}')
};
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
success: success,
method: 'PUT'
});
}
$(document).ready(function() {
$('.select2').select2()
.on('select2:select', function(evt) {
var data = evt.params.data;
jumpserver.selected_groups[data.id] = data.text;
}).on('select2:unselect', function(evt) {
var data = evt.params.data;
delete jumpserver.selected_groups[data.id]
})
}).on('click', '#is_active', function() {
var the_url = "{% url 'api-ops:crontable-detail' pk=cron.id %}";
var checked = !$(this).prop('checked');
var body = {
'is_active': checked
};
var success = '{% trans "Update Successfully!" %}';
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
success_message: success
});
}).on('click', '#btn_add_user_group', function() {
if (Object.keys(jumpserver.selected_groups).length === 0) {
return false;
}
var user_groups = $('.bdg_user_group').map(function() {
return $(this).data('gid');
}).get();
$.map(jumpserver.selected_groups, function(value, index) {
user_groups.push(parseInt(index));
$('#opt_' + index).remove();
});
updateCrons(user_groups)
}).on('click', '.btn_delete_user_group', function() {
var $this = $(this);
var $tr = $this.closest('tr');
var $badge = $tr.find('.bdg_user_group');
var gid = $badge.data('gid');
var group_name = $badge.html() || $badge.text();
$('#slct_groups').append(
'<option value="' + gid + '" id="opt_' + gid + '">' + group_name + '</option>'
);
$tr.remove();
var user_groups = $('.bdg_user_group').map(function() {
return $(this).data('gid');
}).get();
updateCrons(user_groups)
}).on('click', '#btn_user_update_pk', function(){
var $this = $(this);
var pk = $('#txt_pk').val();
var the_url = '{% url "api-ops:crontable-detail" pk=cron.id %}';
var body = {'_public_key': pk};
var success = function() {
$('#txt_pk').val('');
$this.closest('.modal').modal('hide');
var msg = "{% trans 'Successfully updated the SSH public key.' %}";
swal("{% trans 'User SSH Public Key Update' %}", msg, "success");
};
var fail = function() {
var msg = "{% trans 'Failed to update the user\'s SSH public key.' %}";
swal({
title: "{% trans 'User SSH Public Key Update' %}",
text: msg,
type: "error",
showCancelButton: false,
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: true
}, function () {
$('#txt_pk').focus();
}
);
}
APIUpdateAttr({ url: the_url, body: JSON.stringify(body), success: success, error: fail});
});
</script>
{% endblock %}
{% extends '_base_list.html' %}
{% load i18n static %}
{% block table_search %}
{% endblock %}
{% block table_container %}
<div class="uc pull-left m-l-5 m-r-5"><a href="{% url "users:user-create" %}" class="btn btn-sm btn-primary"> {% trans "Create cron" %} </a></div>
{#<div class="uc pull-left"><a href="javascript:void(0);" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#user_import_modal"> {% trans "Import user" %} </a></div>#}
<table class="table table-striped table-bordered table-hover " id="cron_list_table">
<thead>
<tr>
<th class="text-center">
{# <div><input id="" type="checkbox" class="ipt_check_all"><label></label></div>#}
<input id="" type="checkbox" class="ipt_check_all">
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Time(minute-hour-day-month-weekday)' %}</th>
<th class="text-center">{% trans 'Job' %}</th>
<th class="text-center">{% trans 'User' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div id="actions" class="hide">
<div class="input-group">
<select class="form-control m-b" style="width: auto" id="slct_bulk_update">
<option value="delete">{% trans 'Delete selected' %}</option>
<option value="update">{% trans 'Update selected' %}</option>
<option value="deactive">{% trans 'Deactive selected' %}</option>
</select>
<div class="input-group-btn pull-left" style="padding-left: 5px;">
<button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary">
{% trans 'Submit' %}
</button>
</div>
</div>
</div>
{% include "users/_user_bulk_update_modal.html" %}
{#{% include "users/_user_import_modal.html" %}#}
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/jquery.form.min.js' %}"></script>
<script>
$(document).ready(function(){
var options = {
ele: $('#cron_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "ops:page-cron-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{targets: 2, createdCell: function (td, cellData, rowData) {
var cron_time_tmp = "{0}-{1}-{2}-{3}-{4}";
var cron_time = cron_time_tmp.format(rowData.minute, rowData.hour, rowData.day, rowData.month, rowData.weekday);
var innerHtml = '<span>' + cron_time + '</span>';
$(td).html(innerHtml.replace('99991937', rowData.id));
}},
{targets: 5, createdCell: function (td, cellData, rowData) {
var job_btn = '<a href="{% url "users:user-update" pk=99991937 %}" class="btn btn-xs btn-primary m-l-xs">{% trans "Job" %}</a>'.replace('99991937', cellData);
var update_btn = '<a href="{% url "users:user-update" pk=99991937 %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('99991937', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_user_delete" data-uid="99991937">{% trans "Delete" %}</a>'.replace('99991937', cellData);
if (rowData.id === 1 || rowData.username == "admin") {
$(td).html(update_btn)
} else {
$(td).html(job_btn + update_btn + del_btn)
}
}}],
ajax_url: '{% url "api-ops:crontable-list" %}',
columns: [{data: "id"}, {data: "name" }, {data: "month" }, {data: "job" }, {data: "user" }, {data: "id" }],
op_html: $('#actions').html()
};
var table = jumpserver.initDataTable(options);
$('.buttons-pdf').click(function () {
var users = [];
var rows = table.rows('.selected').data();
$.each(rows, function (index, obj) {
users.push(obj.id)
})
});
}).on('click', '#btn_bulk_update', function(){
var action = $('#slct_bulk_update').val();
var $data_table = $('#cron_list_table').DataTable();
var id_list = [];
var plain_id_list = [];
$data_table.rows({selected: true}).every(function(){
id_list.push({id: this.data().id});
plain_id_list.push(this.data().id);
});
if (id_list === []) {
return false;
}
var the_url = "{% url 'api-users:user-list' %}";
function doDeactive() {
var body = $.each(id_list, function(index, user_object) {
user_object['is_active'] = false;
});
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(body)});
$data_table.ajax.reload();
jumpserver.checked = false;
}
function doDelete() {
swal({
title: "{% trans 'Are you sure?' %}",
text: "{% trans 'This will delete the selected users !!!' %}",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false
}, function() {
var success = function() {
var msg = "{% trans 'User Deleted.' %}";
swal("{% trans 'User Delete' %}", msg, "success");
$('#cron_list_table').DataTable().ajax.reload();
};
var fail = function() {
var msg = "{% trans 'User Deleting failed.' %}";
swal("{% trans 'User Delete' %}", msg, "error");
};
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
APIUpdateAttr({url: url_delete, method: 'DELETE', success: success, error: fail});
jumpserver.checked = false;
});
}
function doUpdate() {
$('#user_bulk_update_modal').modal('show');
}
switch(action) {
case 'deactive':
doDeactive();
break;
case 'delete':
doDelete();
break;
case 'update':
doUpdate();
break;
default:
break;
}
}).on('click', '.btn_user_delete', function(){
var $this = $(this);
function doDelete() {
var uid = $this.data('uid');
var the_url = '{% url "api-users:user-detail" pk=99991937 %}'.replace('99991937', uid);
var body = {};
var success = function() {
var msg = "{% trans 'User Deleted.' %}";
swal("{% trans 'User Delete' %}", msg, "success");
$('#cron_list_table').DataTable().ajax.reload();
};
var fail = function() {
var msg = "{% trans 'User Deleting failed.' %}";
swal("{% trans 'User Delete' %}", msg, "error");
};
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
method: 'DELETE',
success: success,
error: fail
});
}
swal({
title: "{% trans 'Are you sure?' %}",
text: "{% trans 'This will delete the selected user.' %}",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false
}, function() {
doDelete();
});
}).on('click', '#btn_user_bulk_update', function(){
var json_data = $('#fm_user_bulk_update').serializeObject();
var body = {};
body.enable_otp = (json_data.enable_otp === 'on')? true: false;
if (json_data.role != '') {
body.role = json_data.role;
}
if (json_data.groups != undefined) {
body.groups = json_data.groups;
}
if (typeof body.groups === 'string') {
body.groups = [parseInt(body.groups)]
} else if(typeof body.groups === 'array') {
new_groups = body.groups.map(Number);
body.groups = new_groups;
}
var $data_table = $('#cron_list_table').DataTable()
var post_list = [];
$data_table.rows({selected: true}).every(function(){
var content = Object.assign({id: this.data().id}, body);
post_list.push(content);
});
if (post_list === []) {
return false
}
var the_url = "{% url 'api-users:user-list' %}";
var success = function() {
var msg = "{% trans 'The selected users has been updated successfully.' %}";
swal("{% trans 'User Updated' %}", msg, "success");
$('#cron_list_table').DataTable().ajax.reload();
jumpserver.checked = false;
};
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success});
$('#user_bulk_update_modal').modal('hide');
}).on('click', '#btn_user_import', function() {
var $form = $('#fm_user_import');
$form.find('.help-block').remove();
function success (data) {
if (data.success === false) {
var $help = $form.find('.help-block');
$('<span />', {class: 'help-block text-danger'}).html(data.msg).insertAfter($('#id_excel'));
} else {
$('#user_import_modal').modal('hide');
var $data_table = $('#cron_list_table').DataTable();
toastr.success("{% trans 'Import User Success.' %}");
$data_table.ajax.reload();
}
}
$form.ajaxSubmit({success: success});
})
</script>
{% endblock %}
{% extends 'cron/_cron.html' %}
{% load i18n %}
{% block user_template_title %}{% trans "Update user" %}{% endblock %}
{% block username %}
<div class="form-group">
<label for="{{ form.username.id_for_label }}" class="col-sm-2 control-label">{% trans 'Username' %}</label>
<div class="col-sm-9 controls" >
<input id="{{ form.username.id_for_label }}" name="{{ form.username.html_name }}" type="text" value="{{ user_object.username }}" readonly class="form-control">
</div>
</div>
{% endblock %}
{% block password %}
<h3>{% trans 'Password' %}</h3>
<div class="form-group">
<label for="password" class="col-sm-2 control-label">{% trans 'Password' %}</label>
<div class="col-sm-9 controls" >
<input id="password" name="password" type="password" class="form-control">
</div>
</div>
{% endblock %}
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load bootstrap %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<link href="{% static "css/plugins/datepicker/datepicker3.css" %}" rel="stylesheet">
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>{% block user_template_title %}{% trans 'Create user' %}{% endblock %}</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<form method="post" class="form-horizontal" action="" enctype="multipart/form-data">
{% csrf_token %}
<h3>{% trans 'Account' %}</h3>
{% block username %} {% endblock %}
{{ form.email|bootstrap_horizontal }}
{{ form.name|bootstrap_horizontal }}
{{ form.groups|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
{% block password %} {% endblock %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Security and Role' %}</h3>
{{ form.role|bootstrap_horizontal }}
<div class="form-group {% if form.date_expired.errors %} has-error {% endif %}" id="date_5">
<label for="{{ form.date_expired.id_for_label }}" class="col-sm-2 control-label">{{ form.date_expired.label }}</label>
<div class="col-sm-9">
<div class="input-group date">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input id="{{ form.date_expired.id_for_label }}" name="{{ form.date_expired.html_name }}" type="text" class="form-control" value="{{ form.date_expired.value|date:'Y-m-d' }}">
</div>
<span class="help-block ">{{ form.date_expired.errors }}</span>
</div>
</div>
{# {{ form.date_expired|bootstrap_horizontal }}#}
<div class="form-group">
<label for="{{ form.enable_otp.id_for_label }}" class="col-sm-2 control-label">{% trans 'Enable OTP' %}</label>
<div class="col-sm-8">
{{ form.enable_otp }}
</div>
</div>
<div class="hr-line-dashed"></div>
<h3>{% trans 'Profile' %}</h3>
{{ form.phone|bootstrap_horizontal }}
{{ form.wechat|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/datapicker/bootstrap-datepicker.js' %}"></script>
<script>
$(document).ready(function () {
$('.select2').select2();
$('.input-group.date').datepicker({
format: "yyyy-mm-dd",
todayBtn: "linked",
keyboardNavigation: false,
forceParse: false,
calendarWeeks: true,
autoclose: true
});
})
</script>
{% endblock %}
{% extends 'sudo/_sudo.html' %}
{% load i18n %}
{% load bootstrap %}
{% block user_template_title %}{% trans "Create user" %}{% endblock %}
{% block username %}
{{ form.username|bootstrap_horizontal }}
{% endblock %}
{% block password %}
<h3>{% trans 'Password' %}</h3>
<div class="form-group">
<label class="col-sm-2 control-label">{% trans 'Password' %}</label>
<div class="col-sm-8 controls" >
{% trans 'Reset link will be generated and sent to the user. ' %}
</div>
</div>
{% endblock %}
\ No newline at end of file
{% extends 'base.html' %}
{% load common_tags %}
{% load users_tags %}
{% load static %}
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li class="active">
<a href="{% url 'ops:page-sudo-detail' pk=sudo.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Cron detail' %} </a>
</li>
<li>
<a href="{% url 'ops:page-sudo-detail' pk=sudo.id %}" class="text-center"><i class="fa fa-bar-chart-o"></i> {% trans 'Asset permission' %}</a>
</li>
<li><a href="{% url 'ops:page-sudo-detail' pk=sudo.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a></li>
<li><a href="{% url 'ops:page-sudo-detail' pk=sudo.id %}" class="text-center"><i class="fa fa-calculator-o"></i> {% trans 'Login history' %}</a></li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-7" style="padding-left: 0">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ sudo.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table">
<tbody>
<tr>
<td width="20%">{% trans 'Name' %}:</td>
<td><b>{{ sudo.name }}</b></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Quick modify' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td width="50%">{% trans 'Active' %}:</td>
<td><span class="pull-right">
<div class="switch">
<div class="onoffswitch">
<input type="checkbox" {% if sudo.is_active %} checked {% endif %} class="onoffswitch-checkbox" id="is_active">
<label class="onoffswitch-label" for="is_active">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
</span></td>
</tr>
<tr>
<td>{% trans 'Enable OTP' %}:</td>
<td><span class="pull-right">
<div class="switch">
<div class="onoffswitch">
<input type="checkbox" class="onoffswitch-checkbox" {% if sudo.enable_otp %} checked {% endif %}
id="enable_otp">
<label class="onoffswitch-label" for="enable_otp">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
</span></td>
</tr>
<tr>
<td>{% trans 'Reset password' %}:</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" id="btn_reset_password" style="width: 54px">{% trans 'Reset' %}</button>
</span>
</td>
</tr>
<tr>
<td>{% trans 'Reset ssh key' %}:</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" id="btn_reset_pk" style="width: 54px;">{% trans 'Reset' %}</button>
</span>
</td>
</tr>
<tr>
<td>{% trans 'Update ssh key' %}:</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" id="btn_update_pk" style="width: 54px;" data-toggle="modal" data-target="#user_update_pk_modal">{% trans 'Update' %}</button>
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'User group' %}
</div>
<div class="panel-body">
<table class="table group_edit">
<tbody>
<form>
<tr>
<td colspan="2" class="no-borders">
<select data-placeholder="{% trans 'Join user groups' %}" id="slct_groups" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for group in groups %}
<option value="{{ group.id }}" id="opt_{{ group.id }}">{{ group.name }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-info btn-small" id="btn_add_user_group">{% trans 'Join' %}</button>
</td>
</tr>
</form>
{% for group in user_object.groups.all %}
<tr>
<td ><b class="bdg_user_group" data-gid={{ group.id }}>{{ group.name }}</b></td>
<td>
<button class="btn btn-danger pull-right btn-xs btn_delete_user_group" type="button"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% include 'users/_user_update_pk_modal.html' %}
{% endblock %}
{% block custom_foot_js %}
<script>
jumpserver.selected_groups = {};
function updateCrons(crons) {
var the_url = "{% url 'api-ops:sudo-detail' pk=sudo.id %}";
var body = {
id: {{ user_object.id }},
groups: Object.assign([], crons)
};
var success = function(data) {
// remove all the selected groups from select > option and rendered ul element;
$('.select2-selection__rendered').empty();
$('#slct_groups').val('');
$.map(jumpserver.selected_groups, function(group_name, index) {
$('#opt_' + index).remove();
// change tr html of user groups.
$('.group_edit tbody').append(
'<tr>' +
'<td><b class="bdg_user_group" data-gid="' + index + '">' + group_name + '</b></td>' +
'<td><button class="btn btn-danger btn-xs pull-right btn_delete_user_group" type="button"><i class="fa fa-minus"></i></button></td>' +
'</tr>'
)
});
// clear jumpserver.selected_groups
jumpserver.selected_groups = {};
toastr.success('{% trans "UserGroup Update Success!" %}')
};
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
success: success,
method: 'PUT'
});
}
$(document).ready(function() {
$('.select2').select2()
.on('select2:select', function(evt) {
var data = evt.params.data;
jumpserver.selected_groups[data.id] = data.text;
}).on('select2:unselect', function(evt) {
var data = evt.params.data;
delete jumpserver.selected_groups[data.id]
})
}).on('click', '#is_active', function() {
var the_url = "{% url 'api-ops:sudo-detail' pk=sudo.id %}";
var checked = !$(this).prop('checked');
var body = {
'is_active': checked
};
var success = '{% trans "Update Successfully!" %}';
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
success_message: success
});
}).on('click', '#btn_add_user_group', function() {
if (Object.keys(jumpserver.selected_groups).length === 0) {
return false;
}
var user_groups = $('.bdg_user_group').map(function() {
return $(this).data('gid');
}).get();
$.map(jumpserver.selected_groups, function(value, index) {
user_groups.push(parseInt(index));
$('#opt_' + index).remove();
});
updateCrons(user_groups)
}).on('click', '.btn_delete_user_group', function() {
var $this = $(this);
var $tr = $this.closest('tr');
var $badge = $tr.find('.bdg_user_group');
var gid = $badge.data('gid');
var group_name = $badge.html() || $badge.text();
$('#slct_groups').append(
'<option value="' + gid + '" id="opt_' + gid + '">' + group_name + '</option>'
);
$tr.remove();
var user_groups = $('.bdg_user_group').map(function() {
return $(this).data('gid');
}).get();
updateCrons(user_groups)
}).on('click', '#btn_user_update_pk', function(){
var $this = $(this);
var pk = $('#txt_pk').val();
var the_url = '{% url "api-ops:sudo-detail" pk=sudo.id %}';
var body = {'_public_key': pk};
var success = function() {
$('#txt_pk').val('');
$this.closest('.modal').modal('hide');
var msg = "{% trans 'Successfully updated the SSH public key.' %}";
swal("{% trans 'User SSH Public Key Update' %}", msg, "success");
};
var fail = function() {
var msg = "{% trans 'Failed to update the user\'s SSH public key.' %}";
swal({
title: "{% trans 'User SSH Public Key Update' %}",
text: msg,
type: "error",
showCancelButton: false,
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: true
}, function () {
$('#txt_pk').focus();
}
);
}
APIUpdateAttr({ url: the_url, body: JSON.stringify(body), success: success, error: fail});
});
</script>
{% endblock %}
{% extends '_base_list.html' %}
{% load i18n static %}
{% block table_search %}
{% endblock %}
{% block table_container %}
<div class="uc pull-left m-l-5 m-r-5"><a href="{% url "users:user-create" %}" class="btn btn-sm btn-primary"> {% trans "Create sudo" %} </a></div>
{#<div class="uc pull-left"><a href="javascript:void(0);" class="btn btnbtn-sm btn-primary" data-toggle="modal" data-target="#user_import_modal"> {% trans "Import user" %} </a></div>#}
<table class="table table-striped table-bordered table-hover " id="sudo_list_table">
<thead>
<tr>
<th class="text-center">
<input id="" type="checkbox" class="ipt_check_all">
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Privileges' %}</th>
<th class="text-center">{% trans 'Extra Lines' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div id="actions" class="hide">
<div class="input-group">
<select class="form-control m-b" style="width: auto" id="slct_bulk_update">
<option value="delete">{% trans 'Delete selected' %}</option>
<option value="update">{% trans 'Update selected' %}</option>
<option value="deactive">{% trans 'Deactive selected' %}</option>
</select>
<div class="input-group-btn pull-left" style="padding-left: 5px;">
<button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary">
{% trans 'Submit' %}
</button>
</div>
</div>
</div>
{#{% include "users/_user_bulk_update_modal.html" %}#}
{#{% include "users/_user_import_modal.html" %}#}
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/jquery.form.min.js' %}"></script>
<script>
$(document).ready(function(){
var options = {
ele: $('#sudo_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "ops:page-sudo-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{targets: 4, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "users:user-update" pk=99991937 %}" class="btn btn-xs btn-info m-l-xs">{% trans "Update" %}</a>'.replace('99991937', cellData);
var preview_btn = '<a href="{% url "users:user-update" pk=99991937 %}" class="btn btn-xs btn-info m-l-xs">{% trans "Preview" %}</a>'.replace('99991937', cellData);
var job_btn = '<a href="{% url "users:user-update" pk=99991937 %}" class="btn btn-xs btn-primary m-l-xs">{% trans "Job" %}</a>'.replace('99991937', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_user_delete" data-uid="99991937">{% trans "Delete" %}</a>'.replace('99991937', cellData);
if (rowData.id === 1 || rowData.username == "admin") {
$(td).html(update_btn)
} else {
$(td).html(preview_btn + job_btn + update_btn + del_btn)
}
}}],
ajax_url: '{% url "api-ops:sudo-list" %}',
columns: [{data: "id"}, {data: "name" }, {data: "privilege_items" }, {data: "extra_lines" }, {data: "id" }],
op_html: $('#actions').html()
};
var table = jumpserver.initDataTable(options);
$('.buttons-pdf').click(function () {
var users = [];
var rows = table.rows('.selected').data();
$.each(rows, function (index, obj) {
users.push(obj.id)
})
});
}).on('click', '#btn_bulk_update', function(){
var action = $('#slct_bulk_update').val();
var $data_table = $('#sudo_list_table').DataTable();
var id_list = [];
var plain_id_list = [];
$data_table.rows({selected: true}).every(function(){
id_list.push({id: this.data().id});
plain_id_list.push(this.data().id);
});
if (id_list === []) {
return false;
}
var the_url = "{% url 'api-users:user-list' %}";
function doDeactive() {
var body = $.each(id_list, function(index, user_object) {
user_object['is_active'] = false;
});
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(body)});
$data_table.ajax.reload();
jumpserver.checked = false;
}
function doDelete() {
swal({
title: "{% trans 'Are you sure?' %}",
text: "{% trans 'This will delete the selected users !!!' %}",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false
}, function() {
var success = function() {
var msg = "{% trans 'User Deleted.' %}";
swal("{% trans 'User Delete' %}", msg, "success");
$('#sudo_list_table').DataTable().ajax.reload();
};
var fail = function() {
var msg = "{% trans 'User Deleting failed.' %}";
swal("{% trans 'User Delete' %}", msg, "error");
};
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
APIUpdateAttr({url: url_delete, method: 'DELETE', success: success, error: fail});
jumpserver.checked = false;
});
}
function doUpdate() {
$('#user_bulk_update_modal').modal('show');
}
switch(action) {
case 'deactive':
doDeactive();
break;
case 'delete':
doDelete();
break;
case 'update':
doUpdate();
break;
default:
break;
}
}).on('click', '.btn_user_delete', function(){
var $this = $(this);
function doDelete() {
var uid = $this.data('uid');
var the_url = '{% url "api-users:user-detail" pk=99991937 %}'.replace('99991937', uid);
var body = {};
var success = function() {
var msg = "{% trans 'User Deleted.' %}";
swal("{% trans 'User Delete' %}", msg, "success");
$('#sudo_list_table').DataTable().ajax.reload();
};
var fail = function() {
var msg = "{% trans 'User Deleting failed.' %}";
swal("{% trans 'User Delete' %}", msg, "error");
};
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
method: 'DELETE',
success: success,
error: fail
});
}
swal({
title: "{% trans 'Are you sure?' %}",
text: "{% trans 'This will delete the selected user.' %}",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false
}, function() {
doDelete();
});
}).on('click', '#btn_user_bulk_update', function(){
var json_data = $('#fm_user_bulk_update').serializeObject();
var body = {};
body.enable_otp = (json_data.enable_otp === 'on')? true: false;
if (json_data.role != '') {
body.role = json_data.role;
}
if (json_data.groups != undefined) {
body.groups = json_data.groups;
}
if (typeof body.groups === 'string') {
body.groups = [parseInt(body.groups)]
} else if(typeof body.groups === 'array') {
new_groups = body.groups.map(Number);
body.groups = new_groups;
}
var $data_table = $('#sudo_list_table').DataTable()
var post_list = [];
$data_table.rows({selected: true}).every(function(){
var content = Object.assign({id: this.data().id}, body);
post_list.push(content);
});
if (post_list === []) {
return false
}
var the_url = "{% url 'api-users:user-list' %}";
var success = function() {
var msg = "{% trans 'The selected users has been updated successfully.' %}";
swal("{% trans 'User Updated' %}", msg, "success");
$('#sudo_list_table').DataTable().ajax.reload();
jumpserver.checked = false;
};
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success});
$('#user_bulk_update_modal').modal('hide');
}).on('click', '#btn_user_import', function() {
var $form = $('#fm_user_import');
$form.find('.help-block').remove();
function success (data) {
if (data.success === false) {
var $help = $form.find('.help-block');
$('<span />', {class: 'help-block text-danger'}).html(data.msg).insertAfter($('#id_excel'));
} else {
$('#user_import_modal').modal('hide');
var $data_table = $('#sudo_list_table').DataTable();
toastr.success("{% trans 'Import User Success.' %}");
$data_table.ajax.reload();
}
}
$form.ajaxSubmit({success: success});
})
</script>
{% endblock %}
{% extends 'sudo/_sudo.html' %}
{% load i18n %}
{% block user_template_title %}{% trans "Update user" %}{% endblock %}
{% block username %}
<div class="form-group">
<label for="{{ form.username.id_for_label }}" class="col-sm-2 control-label">{% trans 'Username' %}</label>
<div class="col-sm-9 controls" >
<input id="{{ form.username.id_for_label }}" name="{{ form.username.html_name }}" type="text" value="{{ user_object.username }}" readonly class="form-control">
</div>
</div>
{% endblock %}
{% block password %}
<h3>{% trans 'Password' %}</h3>
<div class="form-group">
<label for="password" class="col-sm-2 control-label">{% trans 'Password' %}</label>
<div class="col-sm-9 controls" >
<input id="password" name="password" type="password" class="form-control">
</div>
</div>
{% endblock %}
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load bootstrap %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<link href="{% static "css/plugins/datepicker/datepicker3.css" %}" rel="stylesheet">
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>{% block user_template_title %}{% trans 'Create user' %}{% endblock %}</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<form method="post" class="form-horizontal" action="" enctype="multipart/form-data">
{% csrf_token %}
<h3>{% trans 'Account' %}</h3>
{% block username %} {% endblock %}
{{ form.email|bootstrap_horizontal }}
{{ form.name|bootstrap_horizontal }}
{{ form.groups|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
{% block password %} {% endblock %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Security and Role' %}</h3>
{{ form.role|bootstrap_horizontal }}
<div class="form-group {% if form.date_expired.errors %} has-error {% endif %}" id="date_5">
<label for="{{ form.date_expired.id_for_label }}" class="col-sm-2 control-label">{{ form.date_expired.label }}</label>
<div class="col-sm-9">
<div class="input-group date">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input id="{{ form.date_expired.id_for_label }}" name="{{ form.date_expired.html_name }}" type="text" class="form-control" value="{{ form.date_expired.value|date:'Y-m-d' }}">
</div>
<span class="help-block ">{{ form.date_expired.errors }}</span>
</div>
</div>
{# {{ form.date_expired|bootstrap_horizontal }}#}
<div class="form-group">
<label for="{{ form.enable_otp.id_for_label }}" class="col-sm-2 control-label">{% trans 'Enable OTP' %}</label>
<div class="col-sm-8">
{{ form.enable_otp }}
</div>
</div>
<div class="hr-line-dashed"></div>
<h3>{% trans 'Profile' %}</h3>
{{ form.phone|bootstrap_horizontal }}
{{ form.wechat|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/datapicker/bootstrap-datepicker.js' %}"></script>
<script>
$(document).ready(function () {
$('.select2').select2();
$('.input-group.date').datepicker({
format: "yyyy-mm-dd",
todayBtn: "linked",
keyboardNavigation: false,
forceParse: false,
calendarWeeks: true,
autoclose: true
});
})
</script>
{% endblock %}
{% extends 'sudo/_sudo.html' %}
{% load i18n %}
{% load bootstrap %}
{% block user_template_title %}{% trans "Create user" %}{% endblock %}
{% block username %}
{{ form.username|bootstrap_horizontal }}
{% endblock %}
{% block password %}
<h3>{% trans 'Password' %}</h3>
<div class="form-group">
<label class="col-sm-2 control-label">{% trans 'Password' %}</label>
<div class="col-sm-8 controls" >
{% trans 'Reset link will be generated and sent to the user. ' %}
</div>
</div>
{% endblock %}
\ No newline at end of file
{% extends 'base.html' %}
{% load common_tags %}
{% load users_tags %}
{% load static %}
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li class="active">
<a href="{% url 'ops:page-sudo-detail' pk=sudo.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Cron detail' %} </a>
</li>
<li>
<a href="{% url 'ops:page-sudo-detail' pk=sudo.id %}" class="text-center"><i class="fa fa-bar-chart-o"></i> {% trans 'Asset permission' %}</a>
</li>
<li><a href="{% url 'ops:page-sudo-detail' pk=sudo.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a></li>
<li><a href="{% url 'ops:page-sudo-detail' pk=sudo.id %}" class="text-center"><i class="fa fa-calculator-o"></i> {% trans 'Login history' %}</a></li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-7" style="padding-left: 0">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ sudo.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table">
<tbody>
<tr>
<td width="20%">{% trans 'Name' %}:</td>
<td><b>{{ sudo.name }}</b></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
<div class="panel panel-primary">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Quick modify' %}
</div>
<div class="panel-body">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td width="50%">{% trans 'Active' %}:</td>
<td><span class="pull-right">
<div class="switch">
<div class="onoffswitch">
<input type="checkbox" {% if sudo.is_active %} checked {% endif %} class="onoffswitch-checkbox" id="is_active">
<label class="onoffswitch-label" for="is_active">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
</span></td>
</tr>
<tr>
<td>{% trans 'Enable OTP' %}:</td>
<td><span class="pull-right">
<div class="switch">
<div class="onoffswitch">
<input type="checkbox" class="onoffswitch-checkbox" {% if sudo.enable_otp %} checked {% endif %}
id="enable_otp">
<label class="onoffswitch-label" for="enable_otp">
<span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span>
</label>
</div>
</div>
</span></td>
</tr>
<tr>
<td>{% trans 'Reset password' %}:</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" id="btn_reset_password" style="width: 54px">{% trans 'Reset' %}</button>
</span>
</td>
</tr>
<tr>
<td>{% trans 'Reset ssh key' %}:</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" id="btn_reset_pk" style="width: 54px;">{% trans 'Reset' %}</button>
</span>
</td>
</tr>
<tr>
<td>{% trans 'Update ssh key' %}:</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" id="btn_update_pk" style="width: 54px;" data-toggle="modal" data-target="#user_update_pk_modal">{% trans 'Update' %}</button>
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'User group' %}
</div>
<div class="panel-body">
<table class="table group_edit">
<tbody>
<form>
<tr>
<td colspan="2" class="no-borders">
<select data-placeholder="{% trans 'Join user groups' %}" id="slct_groups" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for group in groups %}
<option value="{{ group.id }}" id="opt_{{ group.id }}">{{ group.name }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-info btn-small" id="btn_add_user_group">{% trans 'Join' %}</button>
</td>
</tr>
</form>
{% for group in user_object.groups.all %}
<tr>
<td ><b class="bdg_user_group" data-gid={{ group.id }}>{{ group.name }}</b></td>
<td>
<button class="btn btn-danger pull-right btn-xs btn_delete_user_group" type="button"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% include 'users/_user_update_pk_modal.html' %}
{% endblock %}
{% block custom_foot_js %}
<script>
jumpserver.selected_groups = {};
function updateCrons(crons) {
var the_url = "{% url 'api-ops:sudo-detail' pk=sudo.id %}";
var body = {
id: {{ user_object.id }},
groups: Object.assign([], crons)
};
var success = function(data) {
// remove all the selected groups from select > option and rendered ul element;
$('.select2-selection__rendered').empty();
$('#slct_groups').val('');
$.map(jumpserver.selected_groups, function(group_name, index) {
$('#opt_' + index).remove();
// change tr html of user groups.
$('.group_edit tbody').append(
'<tr>' +
'<td><b class="bdg_user_group" data-gid="' + index + '">' + group_name + '</b></td>' +
'<td><button class="btn btn-danger btn-xs pull-right btn_delete_user_group" type="button"><i class="fa fa-minus"></i></button></td>' +
'</tr>'
)
});
// clear jumpserver.selected_groups
jumpserver.selected_groups = {};
toastr.success('{% trans "UserGroup Update Success!" %}')
};
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
success: success,
method: 'PUT'
});
}
$(document).ready(function() {
$('.select2').select2()
.on('select2:select', function(evt) {
var data = evt.params.data;
jumpserver.selected_groups[data.id] = data.text;
}).on('select2:unselect', function(evt) {
var data = evt.params.data;
delete jumpserver.selected_groups[data.id]
})
}).on('click', '#is_active', function() {
var the_url = "{% url 'api-ops:sudo-detail' pk=sudo.id %}";
var checked = !$(this).prop('checked');
var body = {
'is_active': checked
};
var success = '{% trans "Update Successfully!" %}';
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
success_message: success
});
}).on('click', '#btn_add_user_group', function() {
if (Object.keys(jumpserver.selected_groups).length === 0) {
return false;
}
var user_groups = $('.bdg_user_group').map(function() {
return $(this).data('gid');
}).get();
$.map(jumpserver.selected_groups, function(value, index) {
user_groups.push(parseInt(index));
$('#opt_' + index).remove();
});
updateCrons(user_groups)
}).on('click', '.btn_delete_user_group', function() {
var $this = $(this);
var $tr = $this.closest('tr');
var $badge = $tr.find('.bdg_user_group');
var gid = $badge.data('gid');
var group_name = $badge.html() || $badge.text();
$('#slct_groups').append(
'<option value="' + gid + '" id="opt_' + gid + '">' + group_name + '</option>'
);
$tr.remove();
var user_groups = $('.bdg_user_group').map(function() {
return $(this).data('gid');
}).get();
updateCrons(user_groups)
}).on('click', '#btn_user_update_pk', function(){
var $this = $(this);
var pk = $('#txt_pk').val();
var the_url = '{% url "api-ops:sudo-detail" pk=sudo.id %}';
var body = {'_public_key': pk};
var success = function() {
$('#txt_pk').val('');
$this.closest('.modal').modal('hide');
var msg = "{% trans 'Successfully updated the SSH public key.' %}";
swal("{% trans 'User SSH Public Key Update' %}", msg, "success");
};
var fail = function() {
var msg = "{% trans 'Failed to update the user\'s SSH public key.' %}";
swal({
title: "{% trans 'User SSH Public Key Update' %}",
text: msg,
type: "error",
showCancelButton: false,
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: true
}, function () {
$('#txt_pk').focus();
}
);
}
APIUpdateAttr({ url: the_url, body: JSON.stringify(body), success: success, error: fail});
});
</script>
{% endblock %}
{% extends '_base_list.html' %}
{% load i18n static %}
{% block table_search %}
{% endblock %}
{% block table_container %}
<div class="uc pull-left m-l-5 m-r-5"><a href="{% url "ops:page-task-create" %}" class="btn btn-sm btn-primary"> {% trans "Create task" %} </a></div>
{#<div class="uc pull-left"><a href="javascript:void(0);" class="btn btnbtn-sm btn-primary" data-toggle="modal" data-target="#user_import_modal"> {% trans "Import user" %} </a></div>#}
<table class="table table-striped table-bordered table-hover " id="sudo_list_table">
<thead>
<tr>
<th class="text-center">
<input id="" type="checkbox" class="ipt_check_all">
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'UUID' %}</th>
<th class="text-center">{% trans 'Start' %}</th>
<th class="text-center">{% trans 'Completed' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div id="actions" class="hide">
<div class="input-group">
<select class="form-control m-b" style="width: auto" id="slct_bulk_update">
<option value="delete">{% trans 'Delete selected' %}</option>
<option value="update">{% trans 'Update selected' %}</option>
<option value="deactive">{% trans 'Deactive selected' %}</option>
</select>
<div class="input-group-btn pull-left" style="padding-left: 5px;">
<button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary">
{% trans 'Submit' %}
</button>
</div>
</div>
</div>
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/jquery.form.min.js' %}"></script>
<script>
$(document).ready(function(){
var options = {
ele: $('#sudo_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "ops:page-task-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{targets: 3, createdCell: function (td, cellData) {
if (!cellData) {
$(td).html('<i class="fa fa-times text-danger"></i>')
} else {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}},
{targets: 4, createdCell: function (td, cellData) {
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_user_delete" data-uid="99991937">{% trans "Delete" %}</a>'.replace('99991937', cellData)
$(td).html(del_btn)
}}],
ajax_url: '{% url "api-ops:task-list" %}',
columns: [{data: "name"}, {data: "uuid" }, {data: "start" }, {data: "completed" }, {data: "id" }],
op_html: $('#actions').html()
};
var table = jumpserver.initDataTable(options);
$('.buttons-pdf').click(function () {
var users = [];
var rows = table.rows('.selected').data();
$.each(rows, function (index, obj) {
users.push(obj.id)
})
});
}).on('click', '#btn_bulk_update', function(){
var action = $('#slct_bulk_update').val();
var $data_table = $('#sudo_list_table').DataTable();
var id_list = [];
var plain_id_list = [];
$data_table.rows({selected: true}).every(function(){
id_list.push({id: this.data().id});
plain_id_list.push(this.data().id);
});
if (id_list === []) {
return false;
}
var the_url = "{% url 'api-users:user-list' %}";
function doDeactive() {
var body = $.each(id_list, function(index, user_object) {
user_object['is_active'] = false;
});
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(body)});
$data_table.ajax.reload();
jumpserver.checked = false;
}
function doDelete() {
swal({
title: "{% trans 'Are you sure?' %}",
text: "{% trans 'This will delete the selected users !!!' %}",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false
}, function() {
var success = function() {
var msg = "{% trans 'User Deleted.' %}";
swal("{% trans 'User Delete' %}", msg, "success");
$('#sudo_list_table').DataTable().ajax.reload();
};
var fail = function() {
var msg = "{% trans 'User Deleting failed.' %}";
swal("{% trans 'User Delete' %}", msg, "error");
};
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
APIUpdateAttr({url: url_delete, method: 'DELETE', success: success, error: fail});
jumpserver.checked = false;
});
}
function doUpdate() {
$('#user_bulk_update_modal').modal('show');
}
switch(action) {
case 'deactive':
doDeactive();
break;
case 'delete':
doDelete();
break;
case 'update':
doUpdate();
break;
default:
break;
}
}).on('click', '.btn_user_delete', function(){
var $this = $(this);
function doDelete() {
var uid = $this.data('uid');
var the_url = '{% url "api-users:user-detail" pk=99991937 %}'.replace('99991937', uid);
var body = {};
var success = function() {
var msg = "{% trans 'User Deleted.' %}";
swal("{% trans 'User Delete' %}", msg, "success");
$('#sudo_list_table').DataTable().ajax.reload();
};
var fail = function() {
var msg = "{% trans 'User Deleting failed.' %}";
swal("{% trans 'User Delete' %}", msg, "error");
};
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
method: 'DELETE',
success: success,
error: fail
});
}
swal({
title: "{% trans 'Are you sure?' %}",
text: "{% trans 'This will delete the selected user.' %}",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false
}, function() {
doDelete();
});
}).on('click', '#btn_user_bulk_update', function(){
var json_data = $('#fm_user_bulk_update').serializeObject();
var body = {};
body.enable_otp = (json_data.enable_otp === 'on')? true: false;
if (json_data.role != '') {
body.role = json_data.role;
}
if (json_data.groups != undefined) {
body.groups = json_data.groups;
}
if (typeof body.groups === 'string') {
body.groups = [parseInt(body.groups)]
} else if(typeof body.groups === 'array') {
new_groups = body.groups.map(Number);
body.groups = new_groups;
}
var $data_table = $('#sudo_list_table').DataTable()
var post_list = [];
$data_table.rows({selected: true}).every(function(){
var content = Object.assign({id: this.data().id}, body);
post_list.push(content);
});
if (post_list === []) {
return false
}
var the_url = "{% url 'api-users:user-list' %}";
var success = function() {
var msg = "{% trans 'The selected users has been updated successfully.' %}";
swal("{% trans 'User Updated' %}", msg, "success");
$('#sudo_list_table').DataTable().ajax.reload();
jumpserver.checked = false;
};
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success});
$('#user_bulk_update_modal').modal('hide');
}).on('click', '#btn_user_import', function() {
var $form = $('#fm_user_import');
$form.find('.help-block').remove();
function success (data) {
if (data.success === false) {
var $help = $form.find('.help-block');
$('<span />', {class: 'help-block text-danger'}).html(data.msg).insertAfter($('#id_excel'));
} else {
$('#user_import_modal').modal('hide');
var $data_table = $('#sudo_list_table').DataTable();
toastr.success("{% trans 'Import User Success.' %}");
$data_table.ajax.reload();
}
}
$form.ajaxSubmit({success: success});
})
</script>
{% endblock %}
{% extends 'sudo/_sudo.html' %}
{% load i18n %}
{% block user_template_title %}{% trans "Update user" %}{% endblock %}
{% block username %}
<div class="form-group">
<label for="{{ form.username.id_for_label }}" class="col-sm-2 control-label">{% trans 'Username' %}</label>
<div class="col-sm-9 controls" >
<input id="{{ form.username.id_for_label }}" name="{{ form.username.html_name }}" type="text" value="{{ user_object.username }}" readonly class="form-control">
</div>
</div>
{% endblock %}
{% block password %}
<h3>{% trans 'Password' %}</h3>
<div class="form-group">
<label for="password" class="col-sm-2 control-label">{% trans 'Password' %}</label>
<div class="col-sm-9 controls" >
<input id="password" name="password" type="password" class="form-control">
</div>
</div>
{% endblock %}
# coding:utf-8
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals
from django.conf.urls import url
from rest_framework import routers
from rest_framework.routers import DefaultRouter
from ops import api as v1_api
__all__ = ["urlpatterns"]
api_router = DefaultRouter()
api_router.register(r'v1/host_alia', v1_api.HostAliaViewSet)
api_router.register(r'v1/user_alia', v1_api.UserAliaViewSet)
api_router.register(r'v1/cmd_alia', v1_api.CmdAliaViewSet)
api_router.register(r'v1/runas_alia', v1_api.RunasAliaViewSet)
api_router.register(r'v1/extra_conf', v1_api.ExtraconfViewSet)
api_router.register(r'v1/privilege', v1_api.PrivilegeViewSet)
api_router.register(r'v1/sudo', v1_api.SudoViewSet)
api_router.register(r'v1/cron', v1_api.CronTableViewSet)
api_router.register(r'v1/task', v1_api.TaskViewSet)
api_router.register(r'v1/subtask', v1_api.SubTaskViewSet)
urlpatterns = api_router.urls
\ No newline at end of file
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals
from django.conf.urls import url
from ops import views as page_view
__all__ = ["urlpatterns"]
urlpatterns = [
# Resource Sudo url
url(r'^sudo/list$', page_view.SudoListView.as_view(), name='page-sudo-list'),
url(r'^sudo/create$', page_view.SudoCreateView.as_view(), name='page-sudo-create'),
url(r'^sudo/(?P<pk>[0-9]+)/detail$', page_view.SudoDetailView.as_view(), name='page-sudo-detail'),
url(r'^sudo/(?P<pk>[0-9]+)/update$', page_view.SudoUpdateView.as_view(), name='page-sudo-update'),
# Resource Cron url
url(r'^cron/list$', page_view.CronListView.as_view(), name='page-cron-list'),
url(r'^cron/create$', page_view.CronCreateView.as_view(), name='page-cron-create'),
url(r'^cron/(?P<pk>[0-9]+)/detail$', page_view.CronDetailView.as_view(), name='page-cron-detail'),
url(r'^cron/(?P<pk>[0-9]+)/update$', page_view.CronUpdateView.as_view(), name='page-cron-update'),
# TResource Task url
url(r'^task/list$', page_view.TaskListView.as_view(), name='page-task-list'),
url(r'^task/create$', page_view.TaskCreateView.as_view(), name='page-task-create'),
url(r'^task/(?P<pk>[0-9]+)/detail$', page_view.TaskDetailView.as_view(), name='page-task-detail'),
url(r'^task/(?P<pk>[0-9]+)/update$', page_view.TaskUpdateView.as_view(), name='page-task-update'),
]
\ No newline at end of file
# coding:utf-8
from django.conf.urls import url
from .. import views
app_name = 'ops'
# ~*~ coding: utf-8 ~*~
#
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals
from __future__ import unicode_literals, print_function
import os
import json
import logging
import traceback
import ansible.constants as default_config
from uuid import uuid4
from django.utils import timezone
from ansible.executor.task_queue_manager import TaskQueueManager
from ansible.inventory import Inventory, Host, Group
from ansible.vars import VariableManager
......@@ -10,23 +16,29 @@ from ansible.parsing.dataloader import DataLoader
from ansible.executor import playbook_executor
from ansible.utils.display import Display
from ansible.playbook.play import Play
import ansible.constants as default_config
from ansible.plugins.callback import CallbackBase
from ops.models import TaskRecord, AnsiblePlay, AnsibleTask, AnsibleHostResult
__all__ = ["ADHocRunner", "Config"]
logger = logging.getLogger(__name__)
class AnsibleError(StandardError):
pass
class Config(object):
"""Ansible运行时配置类, 用于初始化Ansible.
"""Ansible运行时配置类, 用于初始化Ansible的一些默认配置.
"""
def __init__(self, verbosity=None, inventory=None, listhosts=None, subset=None, module_paths=None, extra_vars=None,
forks=None, ask_vault_pass=None, vault_password_files=None, new_vault_password_file=None,
output_file=None, tags=None, skip_tags=None, one_line=None, tree=None, ask_sudo_pass=None, ask_su_pass=None,
sudo=None, sudo_user=None, become=None, become_method=None, become_user=None, become_ask_pass=None,
ask_pass=None, private_key_file=None, remote_user=None, connection=None, timeout=None, ssh_common_args=None,
sftp_extra_args=None, scp_extra_args=None, ssh_extra_args=None, poll_interval=None, seconds=None, check=None,
forks=10, ask_vault_pass=False, vault_password_files=None, new_vault_password_file=None,
output_file=None, tags=None, skip_tags=None, one_line=None, tree=None, ask_sudo_pass=False, ask_su_pass=False,
sudo=None, sudo_user=None, become=None, become_method=None, become_user=None, become_ask_pass=False,
ask_pass=False, private_key_file=None, remote_user=None, connection="smart", timeout=10, ssh_common_args=None,
sftp_extra_args=None, scp_extra_args=None, ssh_extra_args=None, poll_interval=None, seconds=None, check=False,
syntax=None, diff=None, force_handlers=None, flush_cache=None, listtasks=None, listtags=None, module_path=None):
self.verbosity = verbosity
self.inventory = inventory
......@@ -80,103 +92,251 @@ class Config(object):
default_config.HOST_KEY_CHECKING = False
class MyInventory(object):
"""Ansible Inventory对象的封装, Inventory是Ansbile中的核心概念(资产清单),
这个概念和CMDB很像,都是对资产的抽象. 为了简化Inventory的使用, 通过传入资产列表即可初始化Inventory.
class InventoryMixin(object):
"""提供生成Ansible inventory对象的方法
"""
def __init__(self, *assets, **group):
"""初始化Inventory对象, args为一个资产列表, kwargs是资产组变量列表, 比如
args:
[{
"name": "asset_name",
"ip": "asset_ip",
"port": "asset_port",
"username": "asset_user",
"password": "asset_pass",
"key": "asset_private_key",
"group": "asset_group_name",
...
}]
kwargs:
"groupName1": {"group_variable1": "value1",...}
"groupName2": {"group_variable1": "value1",...}
"""
self.assets = assets
self.assets_group = group
self.loader = DataLoader()
self.variable_manager = VariableManager()
self.groups = []
self.inventory = self.gen_inventory()
def gen_inventory(self):
"""用于生成动态构建Ansible Inventory.
self.hosts: [
{"host": <ip>,
"port": <port>,
"user": <user>,
"pass": <pass>,
"key": <sshKey>,
"group": <default>
"other_host_var": <other>},
{...},
]
self.group_vars: {
"groupName1": {"var1": <value>, "var2": <value>, ...},
"groupName2": {"var1": <value>, "var2": <value>, ...},
}
def __gen_group(self):
"""初始化Ansible Group, 将资产添加到Inventory里面
:return: None
:return: 返回一个Ansible的inventory对象
"""
# 创建Ansible Group.
for asset in self.assets:
# TODO: 验证输入
# 创建Ansible Group,如果没有则创建default组
for asset in self.hosts:
g_name = asset.get('group', 'default')
if g_name not in [g.name for g in self.groups]:
group = Group(name=asset.get('group', 'default'))
group = Group(name=g_name)
self.groups.append(group)
# 初始化组变量
for group_name, variables in self.assets_group.iteritems():
# 添加组变量到相应的组上
for group_name, variables in self.group_vars.iteritems():
for g in self.groups:
if g.name == group_name:
for v_name, v_value in variables:
for v_name, v_value in variables.iteritems():
g.set_variable(v_name, v_value)
# 往组里面添加Host
for asset in self.assets:
for asset in self.hosts:
# 添加Host链接的常用变量(host,port,user,pass,key)
host = Host(name=asset['name'], port=asset['port'])
host.set_variable('ansible_ssh_host', asset['ip'])
host.set_variable('ansible_ssh_port', asset['port'])
host.set_variable('ansible_ssh_user', asset['username'])
host.set_variable('ansible_host', asset['ip'])
host.set_variable('ansible_port', asset['port'])
host.set_variable('ansible_user', asset['username'])
# 添加密码和秘钥
if asset.get('password'):
host.set_variable('ansible_ssh_pass', asset['password'])
if asset.get('key'):
host.set_variable('ansible_ssh_private_key_file', asset['key'])
# 添加become支持
become = asset.get("become", None)
if become is not None:
host.set_variable("ansible_become", True)
host.set_variable("ansible_become_method", become.get('method'))
host.set_variable("ansible_become_user", become.get('user'))
host.set_variable("ansible_become_pass", become.get('pass'))
else:
host.set_variable("ansible_become", False)
# 添加其他Host的额外变量
for key, value in asset.iteritems():
if key not in ["name", "port", "ip", "username", "password", "key"]:
host.set_variable(key, value)
# 将host添加到组里面
for g in self.groups:
if g.name == asset.get('group', 'default'):
g.add_host(host)
def validate(self):
pass
def gen_inventory(self):
self.validate()
i = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list=[])
self.__gen_group()
# 将组添加到Inventory里面,生成真正的inventory对象
inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list=[])
for g in self.groups:
i.add_group(g)
self.variable_manager.set_inventory(i)
return i
inventory.add_group(g)
self.variable_manager.set_inventory(inventory)
return inventory
class PlayBookRunner(object):
"""用于执行AnsiblePlaybook的接口.简化Playbook对象的使用
class CallbackModule(CallbackBase):
"""处理和分析Ansible运行结果,并保存数据.
"""
CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'stdout'
CALLBACK_NAME = 'json'
def __init__(self, tasker_id, display=None):
super(CallbackModule, self).__init__(display)
self.results = []
self.output = {}
self.tasker_id = tasker_id
def _new_play(self, play):
"""将Play保持到数据里面
"""
ret = {
'tasker': self.tasker_id,
'name': play.name,
'uuid': str(play._uuid),
'tasks': []
}
try:
tasker = TaskRecord.objects.get(uuid=self.tasker_id)
play = AnsiblePlay(tasker, name=ret['name'], uuid=ret['uuid'])
play.save()
except Exception as e:
traceback.print_exc()
logger.error("Save ansible play uuid to database error!, %s" % e.message)
return ret
def __init__(self, inventory, config, palybook_path, playbook_var, become_pass, verbosity=0):
def _new_task(self, task):
"""将Task保持到数据库里,需要和Play进行关联
"""
:param inventory: myinventory实例
ret = {
'name': task.name,
'uuid': str(task._uuid),
'failed': {},
'unreachable': {},
'skipped': {},
'no_hosts': {},
'success': {}
}
try:
play = AnsiblePlay.objects.get(uuid=self.__play_uuid)
task = AnsibleTask(play=play, uuid=ret['uuid'], name=ret['name'])
task.save()
except Exception as e:
logger.error("Save ansible task uuid to database error!, %s" % e.message)
return ret
@property
def __task_uuid(self):
return self.results[-1]['tasks'][-1]['uuid']
@property
def __play_uuid(self):
return self.results[-1]['uuid']
def save_task_result(self, result, status):
try:
task = AnsibleTask.objects.get(uuid=self.__task_uuid)
host_result = AnsibleHostResult(task=task, name=result._host)
if status == "failed":
host_result.failed = json.dumps(result._result)
elif status == "unreachable":
host_result.unreachable = json.dumps(result._result)
elif status == "skipped":
host_result.skipped = json.dumps(result._result)
elif status == "success":
host_result.success = json.dumps(result._result)
else:
logger.error("No such status(failed|unreachable|skipped|success), please check!")
host_result.save()
except Exception as e:
logger.error("Save Ansible host result to database error!, %s" % e.message)
@staticmethod
def save_no_host_result(task):
try:
task = AnsibleTask.objects.get(uuid=task._uuid)
host_result = AnsibleHostResult(task=task, no_host="no host to run this task")
host_result.save()
except Exception as e:
logger.error("Save Ansible host result to database error!, %s" % e.message)
def v2_runner_on_failed(self, result, ignore_errors=False):
self.save_task_result(result, "failed")
host = result._host
self.results[-1]['tasks'][-1]['failed'][host.name] = result._result
def v2_runner_on_unreachable(self, result):
self.save_task_result(result, "unreachable")
host = result._host
self.results[-1]['tasks'][-1]['unreachable'][host.name] = result._result
def v2_runner_on_skipped(self, result):
self.save_task_result(result, "skipped")
host = result._host
self.results[-1]['tasks'][-1]['skipped'][host.name] = result._result
def v2_runner_on_no_hosts(self, task):
self.save_no_host_result(task)
self.results[-1]['tasks'][-1]['no_hosts']['msg'] = "no host to run this task"
def v2_runner_on_ok(self, result):
self.save_task_result(result, "success")
host = result._host
self.results[-1]['tasks'][-1]['success'][host.name] = result._result
def v2_playbook_on_play_start(self, play):
self.results.append(self._new_play(play))
def v2_playbook_on_task_start(self, task, is_conditional):
self.results[-1]['tasks'].append(self._new_task(task))
def v2_playbook_on_stats(self, stats):
"""AdHoc模式下这个钩子不会执行
"""
hosts = sorted(stats.processed.keys())
summary = {}
for h in hosts:
s = stats.summarize(h)
summary[h] = s
self.output['plays'] = self.results
self.output['stats'] = summary
print("summary: %s", summary)
class PlayBookRunner(InventoryMixin):
"""用于执行AnsiblePlaybook的接口.简化Playbook对象的使用.
"""
def __init__(self, config, palybook_path, playbook_var, become_pass, *hosts, **group_vars):
"""
:param config: Config实例
:param palybook_path: playbook的路径
:param playbook_var: 执行Playbook时的变量
:param become_pass: sudo passsword
:param verbosity: --verbosity
:param hosts: 可变位置参数, 为一个资产列表, 每一个资产用dict表示, 以下是这个dict必须包含的key
[{
"name": "asset_name",
"ip": "asset_ip",
"port": "asset_port",
"username": "asset_user",
"password": "asset_pass",
"key": "asset_private_key",
"group": "asset_group_name",
...
}]
:param group_vars: 可变关键字参数, 是资产组变量, 记录对应的资产组变量
"groupName1": {"group_variable1": "value1",...}
"groupName2": {"group_variable1": "value1",...}
"""
self.options = config
self.options.verbosity = verbosity
self.options.connection = 'smart'
# 设置verbosity级别, 及命令行的--verbose选项
self.display = Display()
......@@ -190,16 +350,24 @@ class PlayBookRunner(object):
passwords = {'become_pass': become_pass}
# 传入playbook的路径,以及执行需要的变量
inventory.variable_manager.extra_vars = playbook_var
pb_dir = os.path.dirname(__file__)
playbook = "%s/%s" % (pb_dir, palybook_path)
# 生成Ansible inventory, 这些变量Mixin都会用到
self.hosts = hosts
self.group_vars = group_vars
self.loader = DataLoader()
self.variable_manager = VariableManager()
self.groups = []
self.variable_manager.extra_vars = playbook_var
self.inventory = self.gen_inventory()
# 初始化playbook的executor
self.pbex = playbook_executor.PlaybookExecutor(
playbooks=[playbook],
inventory=inventory,
variable_manager=inventory.variable_manager,
loader=inventory.loader,
inventory=self.inventory,
variable_manager=self.variable_manager,
loader=self.loader,
options=self.options,
passwords=passwords)
......@@ -223,13 +391,15 @@ class PlayBookRunner(object):
return stats
class ADHocRunner(object):
class ADHocRunner(InventoryMixin):
"""ADHoc接口
"""
def __init__(self, inventory, config, become_pass=None, verbosity=0):
def __init__(self, play_data, config=None, *hosts, **group_vars):
"""
:param inventory: myinventory实例
:param hosts: 见PlaybookRunner参数
:param group_vars: 见PlaybookRunner参数
:param config: Config实例
:param play_data:
play_data = dict(
name="Ansible Ad-Hoc",
......@@ -238,73 +408,115 @@ class ADHocRunner(object):
tasks=[dict(action=dict(module='service', args={'name': 'vsftpd', 'state': 'restarted'}), async=async, poll=poll)]
)
"""
self.options = config
self.options.verbosity = verbosity
self.options.connection = 'smart'
self.options = config if config != None else Config()
# 设置verbosity级别, 及命令行的--verbose选项
self.display = Display()
self.display.verbosity = self.options.verbosity
playbook_executor.verbosity = self.options.verbosity
# sudo成其他用户的配置
self.options.become = True
self.options.become_method = 'sudo'
self.options.become_user = 'root'
self.passwords = {'become_pass': become_pass}
# sudo的配置移到了Host级别去了,因此这里不再需要处理
self.passwords = None
# 初始化callback插件
# self.results_callback = ResultCallback()
# 生成Ansible inventory, 这些变量Mixin都会用到
self.hosts = hosts
self.group_vars = group_vars
self.loader = DataLoader()
self.variable_manager = VariableManager()
self.groups = []
self.inventory = self.gen_inventory()
# 初始化Play
play_source = {
"name": "Ansible Play",
"hosts": "*",
"gather_facts": "no",
"tasks": [
dict(action=dict(module='shell', args='id'), register='shell_out'),
dict(action=dict(module='debug', args=dict(msg='{{shell_out.stdout}}')))
]
}
self.play = Play().load(play_data, variable_manager=self.variable_manager, loader=self.loader)
self.play = Play().load(play_source, variable_manager=inventory.variable_manager, loader=inventory.loader)
self.inventory = inventory
@staticmethod
def update_db_tasker(tasker_id, ext_code):
try:
tasker = TaskRecord.objects.get(uuid=tasker_id)
tasker.end = timezone.now()
tasker.completed = True
tasker.exit_code = ext_code
tasker.save()
except Exception as e:
logger.error("Update Tasker Status into database error!, %s" % e.message)
def create_db_tasker(self, name, uuid):
try:
hosts = [host.get('name') for host in self.hosts]
tasker = TaskRecord(name=name, uuid=uuid, hosts=','.join(hosts), start=timezone.now())
tasker.save()
except Exception as e:
logger.error("Save Tasker to database error!, %s" % e.message)
def run(self):
"""执行ADHoc 记录日志, 处理结果
def run(self, tasker_name, tasker_uuid):
"""执行ADHoc, 执行完后, 修改AnsiblePlay的状态为完成状态.
:param tasker_uuid <str> 用于标示此次task
"""
# 初始化callback插件,以及Tasker
self.create_db_tasker(tasker_name, tasker_uuid)
self.results_callback = CallbackModule(tasker_uuid)
tqm = None
# TODO:日志和结果分析
try:
tqm = TaskQueueManager(
inventory=self.inventory.inventory,
variable_manager=self.inventory.variable_manager,
loader=self.inventory.loader,
stdout_callback=default_config.DEFAULT_STDOUT_CALLBACK,
inventory=self.inventory,
variable_manager=self.variable_manager,
loader=self.loader,
stdout_callback=self.results_callback,
options=self.options,
passwords=self.passwords
)
ext_code = tqm.run(self.play)
result = self.results_callback.results
# 任务运行结束, 标示任务完成
self.update_db_tasker(tasker_uuid, ext_code)
ret = json.dumps(result)
return ext_code, ret
result = tqm.run(self.play)
return result
finally:
if tqm:
tqm.cleanup()
if __name__ == "__main__":
def test_run():
conf = Config()
assets = [{
"name": "localhost",
"ip": "localhost",
assets = [
{
"name": "192.168.1.119",
"ip": "192.168.1.119",
"port": "22",
"username": "root",
"password": "tongfang_test",
"key": "asset_private_key",
},
{
"name": "192.168.232.135",
"ip": "192.168.232.135",
"port": "22",
"username": "yumaojun",
"password": "xxx",
"key": "asset_private_key",
}]
inv = MyInventory(*assets)
print inv.inventory.get_group('default').get_hosts()
hoc = ADHocRunner(inv, conf, 'xxx')
hoc.run()
"become": {"method": "sudo", "user": "root", "pass": "xxx"}
},
]
# 初始化Play
play_source = {
"name": "Ansible Play",
"hosts": "default",
"gather_facts": "no",
"tasks": [
dict(action=dict(module='ping')),
]
}
hoc = ADHocRunner(conf, play_source, *assets)
uuid = "tasker-" + uuid4().hex
ext_code, result = hoc.run("test_task", uuid)
print(ext_code)
print(result)
if __name__ == "__main__":
test_run()
# ~*~ coding: utf-8 ~*~
class CreateSudoPrivilegesMixin(object):
def create_privilege(self):
pass
class ListSudoPrivilegesMixin(object):
def get_all_privilege(self):
pass
\ No newline at end of file
from django.shortcuts import render
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals
# Create your views here.
from django.conf import settings
from django.views.generic.list import ListView, MultipleObjectMixin
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from django.views.generic.detail import DetailView, SingleObjectMixin
from users.utils import AdminUserRequiredMixin
from ops.utils.mixins import CreateSudoPrivilegesMixin, ListSudoPrivilegesMixin
from ops.models import *
class SudoListView(AdminUserRequiredMixin, ListSudoPrivilegesMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
model = Sudo
context_object_name = 'sudos'
template_name = 'sudo/list.html'
class SudoCreateView(AdminUserRequiredMixin, CreateSudoPrivilegesMixin, CreateView):
model = Sudo
template_name = 'sudo/create.html'
class SudoUpdateView(AdminUserRequiredMixin, UpdateView):
model = Sudo
template_name = 'sudo/update.html'
class SudoDetailView(DetailView):
model = Sudo
context_object_name = 'sudo'
template_name = 'sudo/detail.html'
class CronListView(AdminUserRequiredMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
model = CronTable
context_object_name = 'crons'
template_name = 'cron/list.html'
class CronCreateView(AdminUserRequiredMixin, CreateView):
model = CronTable
template_name = 'cron/create.html'
class CronUpdateView(AdminUserRequiredMixin, UpdateView):
model = CronTable
template_name = 'cron/update.html'
class CronDetailView(DetailView):
model = CronTable
context_object_name = 'cron'
template_name = 'cron/detail.html'
class TaskListView(AdminUserRequiredMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
model = Task
context_object_name = 'tasks'
template_name = 'task/list.html'
def get_context_data(self, **kwargs):
context = {
'task': 'Assets',
'action': 'Create asset',
}
kwargs.update(context)
return super(TaskListView, self).get_context_data(**kwargs)
class TaskCreateView(AdminUserRequiredMixin, CreateView):
model = Task
template_name = 'task/create.html'
class TaskUpdateView(AdminUserRequiredMixin, UpdateView):
model = Task
template_name = 'task/update.html'
class TaskDetailView(DetailView):
model = Task
context_object_name = 'task'
template_name = 'task/detail.html'
......@@ -331,3 +331,35 @@ jumpserver.initDataTable = function (options) {
return table;
};
/**
* 替换所有匹配exp的字符串为指定字符串
* @param exp 被替换部分的正则
* @param newStr 替换成的字符串
*/
String.prototype.replaceAll = function (exp, newStr) {
return this.replace(new RegExp(exp, "gm"), newStr);
};
/**
* 原型:字符串格式化
* @param args 格式化参数值
*/
String.prototype.format = function(args) {
var result = this;
if (arguments.length < 1) {
return result;
}
var data = arguments;
if (arguments.length == 1 && typeof (args) == "object") {
data = args;
}
for ( var key in data) {
var value = data[key];
if (undefined != value) {
result = result.replaceAll("\\{" + key + "\\}", value);
}
}
return result;
}
......@@ -34,11 +34,24 @@
</li>
</ul>
</li>
<li id="terminal">
<a href="{% url 'terminal:terminal-list' %}">
<i class="fa fa-desktop"></i><span class="nav-label">{% trans 'Terminal' %}</span><span class="label label-info pull-right"></span>
  </a>
</li>
<li id="ops">
<a>
<i class="fa fa-coffee"></i> <span class="nav-label">{% trans 'Job Center' %}</span><span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level">
<li id="task"><a href="{% url 'ops:page-task-list' %}">{% trans 'Task' %}</a></li>
<li id="sudo"><a href="{% url 'ops:page-sudo-list' %}">{% trans 'Sudo' %}</a></li>
<li id="cron"><a href="{% url 'ops:page-cron-list' %}">{% trans 'Cron' %}</a></li>
</ul>
</li>
<li id="audits">
<a href="#"><i class="fa fa-files-o"></i> <span class="nav-label">{% trans 'Audits' %}</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
......
......@@ -6,7 +6,7 @@ djangorestframework==3.4.5
ForgeryPy==0.1
openpyxl==2.4.0
celery==3.1.23
ansible==2.1.1.0
ansible==2.1.2.0
django-simple-captcha==0.5.2
django-formtools==1.0
sshpubkeys==2.2.0
......@@ -15,5 +15,8 @@ paramiko==2.0.2
django-redis-cache==1.7.1
requests==2.11.1
itsdangerous==0.24
python-gssapi==0.6.4
tornado==4.4.2
eventlet==0.19.0
unicodecsv==0.14.1
django-filter==1.0.0
......@@ -30,7 +30,7 @@ def start_celery():
os.chdir(apps_dir)
os.environ.setdefault('C_FORCE_ROOT', '1')
print('start celery')
subprocess.call('celery -A common worker -B -s /tmp/celerybeat-schedule -l info ', shell=True)
subprocess.call('celery -A common worker -P eventlet -s /tmp/celerybeat-schedule -l info ', shell=True)
def main():
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment