Merge branch 'v3' of github.com:jumpserver/lina into v3

This commit is contained in:
ibuler 2022-10-27 20:00:04 +08:00
commit 32ec05e04c
8 changed files with 247 additions and 440 deletions

View File

@ -627,7 +627,11 @@
"versionDetail": "Version detail",
"versionRunExecution": "Version run execution",
"Unkown": "Unkown",
"Unknown": "Unknown"
"Unknown": "Unknown",
"PENDING": "Pending",
"RUNNING": "Running",
"SUCCESS": "Success",
"FAILURE": "Failure"
},
"perms": {
"": "",
@ -1228,7 +1232,6 @@
"OrgUser": "Org User",
"OrgAdmin": "Org Admin",
"OrgAuditor": "Org Auditor",
"HelpText": {
"MFAOfUserFirstLoginPersonalInformationImprovementPage": "Enable multi-factor authentication to make the account more secure <br/> After is enabled, you will enter the multi-factor authentication binding process on your next login <br/> You can also bind directly in (personal information -> fast modifier -> modifier multiple factor Settings)",
"MFAOfUserFirstLoginUserGuidePage": "To protect the security of you and the company <br/> please properly keep your account, password, key and other important and sensitive information <br/> (e.g., set a complex password and enable multi-factor authentication)",

View File

@ -644,7 +644,11 @@
"versionDetail": "版本详情",
"versionRunExecution": "执行历史",
"Unkown": "未知",
"Unknown": "未知"
"Unknown": "未知",
"PENDING": "等待中",
"RUNNING": "运行中",
"SUCCESS": "成功",
"FAILURE": "失败"
},
"perms": {
"": "",

View File

@ -12,14 +12,14 @@ export default [
path: '',
name: 'TaskList',
component: () => import('@/views/ops/TaskList'),
meta: { title: i18n.t('route.TaskList') }
meta: { title: i18n.t('route.TaskList'), permissions: [] }
},
{
path: ':id',
component: () => import('@/views/ops/TaskDetail'),
name: 'TaskDetail',
hidden: true,
meta: { title: i18n.t('route.TaskDetail') }
meta: { title: i18n.t('route.TaskDetail'), permissions: [] }
}
]
},

View File

@ -1,103 +0,0 @@
<template>
<ListTable :table-config="tableConfig" :header-actions="headerActions" />
</template>
<script>
import ListTable from '@/components/ListTable'
import { toSafeLocalDateStr } from '@/utils/common'
import { DetailFormatter } from '@/components/TableFormatters'
export default {
name: 'TaskAdhoc',
components: {
ListTable
},
props: {
object: {
type: Object,
default: () => ({})
}
},
data() {
return {
tableConfig: {
url: `/api/v1/ops/adhoc/?task=${this.object.id}`,
columns: [
'short_id', 'hosts', 'pattern', 'run_as', 'become_display', 'date_created', 'actions'
],
columnsMeta: {
short_id: {
label: this.$t('ops.version'),
formatter: DetailFormatter,
formatterArgs: {
route: 'AdhocDetail'
}
},
hosts: {
label: this.$t('ops.hosts'),
width: '80px',
formatter: (row, column, cellValue) => {
if (cellValue instanceof Array) {
return cellValue.length
}
return cellValue
},
showOverflowTooltip: true
},
pattern: {
label: this.$t('ops.pattern'),
width: '80px'
},
run_as: {
label: this.$t('ops.runAs'),
formatter: function(row) {
if (row.run_as_admin) {
return 'Admin'
}
if (row.run_as) {
return row.run_as
}
}
},
become_display: {
label: this.$t('ops.become')
},
date_created: {
label: this.$t('ops.datetime'),
formatter: function(row) {
return toSafeLocalDateStr(row.date_created)
}
},
actions: {
prop: 'id',
width: '80px',
formatterArgs: {
hasEdit: false,
hasDelete: false,
hasUpdate: false,
hasClone: false,
extraActions: [
{
name: 'detail',
title: this.$t('ops.detail'),
type: 'primary',
callback: function({ row, tableData }) {
return this.$router.push({ name: 'AdhocDetail', params: { id: row.id }})
}
}
]
}
}
}
},
headerActions: {
hasLeftActions: false,
hasRightActions: false
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -3,25 +3,16 @@
<el-col :md="14" :sm="24">
<DetailCard :title="cardTitle" :items="detailCardItems" />
</el-col>
<el-col :md="10" :sm="24">
<RunInfoCard type="danger" style="margin-top: 15px" v-bind="RunFailedConfig" />
<RunInfoCard type="info" v-bind="RunSuccessConfig" style="margin-top: 15px" />
</el-col>
</el-row>
</template>
<script type="text/jsx">
import DetailCard from '@/components/DetailCard'
import RunInfoCard from '../RunInfoCard/index'
import { toLastFailureDisplay, toLastSucessDisplay } from './business'
import { toSafeLocalDateStr } from '@/utils/common'
import { openTaskPage } from '@/utils/jms'
export default {
name: 'TaskDetail',
components: {
DetailCard,
RunInfoCard
DetailCard
},
props: {
object: {
@ -30,100 +21,22 @@ export default {
}
},
data() {
return {
RunSuccessConfig: {
icon: 'fa-info',
title: this.$t('ops.lastRunSuccessHosts'),
contents: toLastSucessDisplay(this.object.latest_execution)
},
RunFailedConfig: {
icon: 'fa-info',
title: this.$t('ops.lastRunFailedHosts'),
contents: toLastFailureDisplay(this.object.latest_execution)
},
taskData: {}
}
return {}
},
computed: {
cardTitle() {
return this.object.display_name
return this.object.name
},
detailCardItems() {
return [
{
key: this.$t('common.Name'),
value: this.object.display_name
},
{
key: this.$t('common.dateCreated'),
value: toSafeLocalDateStr(this.object.date_created)
},
{
key: this.$t('ops.totalVersions'),
value: JSON.stringify(this.object.summary.total)
},
{
key: this.$t('ops.latestVersion'),
value: this.object.latest_execution,
formatter: function(row, data) {
const route = { to: { name: 'AdhocDetail', params: { id: data.adhoc }}}
if (!this.$hasPerm('ops.ops.view_adhoc')) {
return <span>{ data.adhoc_short_id }</span>
}
return <router-link {...{ attrs: route }}>{ data.adhoc_short_id }</router-link>
}
},
{
key: this.$t('ops.lastRun'),
value: toSafeLocalDateStr(this.object.latest_execution.date_finished)
},
{
key: this.$t('ops.timeDelta'),
value: this.object.latest_execution.timedelta.toFixed(2) + 's'
},
{
key: this.$t('ops.isFinished'),
value: this.object.latest_execution.is_finished
},
{
key: this.$t('ops.isSuccess'),
value: this.object.latest_execution.is_success
},
{
key: this.$t('ops.contents'),
value: this.toContentsDisplay(this.object.contents),
formatter(row, value) {
return (<div>{
value.map((content) => {
return <div>{ content }</div>
})}
</div>)
}
},
{
key: this.$t('ops.lastExecutionOutput'),
value: this.object.latest_execution.id,
formatter: function(row, value) {
const onClick = function() {
openTaskPage(value, 'ansible')
}
const title = this.$t('common.View')
return <a onClick={onClick} >{ title }</a>
}
value: this.object.name
}
]
}
},
methods: {
toContentsDisplay(contents) {
const lines = []
for (let i = 0; i < contents.length; i++) {
const content = contents[i]
lines.push(`${i}. ${content.name} ::: ${content.action.module}`)
}
return lines
}
}
methods: {}
}
</script>

View File

@ -4,8 +4,6 @@
<script type="text/jsx">
import ListTable from '@/components/ListTable'
import { DetailFormatter } from '@/components/TableFormatters'
import { toSafeLocalDateStr } from '@/utils/common'
export default {
name: 'TaskHistory',
@ -21,63 +19,25 @@ export default {
data() {
return {
tableConfig: {
url: `/api/v1/ops/adhoc-executions/?task=${this.object.id}`,
url: `/api/v1/ops/tasks/${this.object.id}/executions/`,
columns: [
'date_start', 'stat', 'ratio', 'is_finished', 'is_success', 'timedelta', 'adhoc_short_id', 'actions'
'id', 'state', 'is_finished', 'date_published', 'date_start', 'date_finished', 'actions'
],
columnsMeta: {
date_start: {
formatter: (row) => toSafeLocalDateStr(row.date_start),
width: '160px'
},
stat: {
label: this.$t('ops.stat'),
align: 'center',
width: '100px',
formatter: function(row) {
return (
<div>
<span class='text-primary'>{row.stat.success}</span>/
<span class='text-danger'>{row.stat.failed}</span>/
<span>{row.stat.total}</span>
</div>
)
state: {
label: this.$t('ops.state'),
width: '120px',
formatter: (row) => {
switch (row.state) {
case 'PENDING':
return <span><i Class='fa fa-spinner fa-spin' />{this.$t(`ops.${row.state}`)}</span>
case 'RUNNING':
return <span><i Class='fa fa-spinner fa-spin' />{this.$t(`ops.${row.state}`)}</span>
case 'SUCCESS':
return <span Class='text-success'><i Class='fa fa-check' />{this.$t(`ops.${row.state}`)}</span>
case 'FAILURE':
return <span Class='text-danger'><i Class='fa fa-times' />{this.$t(`ops.${row.state}`)}</span>
}
},
ratio: {
label: this.$t('ops.ratio'),
align: 'center',
width: '80px',
formatter: function(row) {
const ratio = (row.stat.success / row.stat.total) * 100
if (ratio === 100) {
return <span class='text-navy'>{ratio + '%'}</span>
}
return <span class='text-danger'>{ratio + '%'}</span>
}
},
is_finished: {
align: 'center',
width: '100px',
label: this.$t('ops.isFinished')
},
is_success: {
align: 'center',
width: '100px',
label: this.$t('ops.isSuccess')
},
timedelta: {
label: this.$t('ops.time'),
width: '100px',
formatter: function(row) {
return row.timedelta.toFixed(2) + 's'
}
},
adhoc_short_id: {
label: this.$t('ops.version'),
formatter: DetailFormatter,
formatterArgs: {
route: 'HistoryExecutionDetail'
}
},
actions: {

View File

@ -10,7 +10,6 @@
import { GenericDetailPage, TabPage } from '@/layout/components'
import TaskDetail from './TaskDetail'
import TaskHistory from './TaskHistory'
import TaskAdhoc from './TaskAdhoc'
import CeleryTaskLog from '../CeleryTaskLog'
export default {
components: {
@ -18,7 +17,6 @@ export default {
CeleryTaskLog,
TaskHistory,
TaskDetail,
TaskAdhoc,
TabPage
},
data() {
@ -34,11 +32,6 @@ export default {
title: this.$t('ops.taskDetail'),
name: 'TaskDetail'
},
{
title: this.$t('ops.taskVersions'),
name: 'TaskAdhoc',
hidden: () => !this.$hasPerm('ops.view_adhoc')
},
{
title: this.$t('ops.execution'),
name: 'TaskHistory',

View File

@ -3,14 +3,9 @@
</template>
<script type="text/jsx">
import { timeOffset, toSafeLocalDateStr } from '@/utils/common'
// import { timeOffset, toSafeLocalDateStr } from '@/utils/common'
import { GenericListPage } from '@/layout/components'
import { openTaskPage } from '@/utils/jms'
const performDelete = function({ row }) {
const id = row.id
const url = `${this.url}${id}/`
return this.$axios.delete(url)
}
// import { openTaskPage } from '@/utils/jms'
export default {
components: {
@ -21,109 +16,150 @@ export default {
tableConfig: {
url: '/api/v1/ops/tasks/',
columns: [
'name', 'runtimes', 'host_amount', 'is_success',
'date_start', 'time', 'actions'
'name', 'queue', 'comment', 'count', 'state', 'last_published_time'
],
columnsMeta: {
name: {
showOverflowTooltip: true,
formatterArgs: {
getTitle({ row }) {
return row['display_name']
can: true
}
// formatter: (row) => {
// return row.meta.verbose_name != null ? row.meta.verbose_name : row.name
// }
},
comment: {
label: 'comment',
formatter: (row) => {
return row.meta.comment
}
},
runtimes: {
label: this.$t('ops.runTimes'),
queue: {
label: 'queue',
formatter: (row) => {
return row.meta.queue
}
},
last_published_time: {
width: '210px',
formatter: (row) => {
return row.last_published_time != null ? row.last_published_time : '-'
}
},
count: {
label: 'success/total',
width: '120px',
formatter: function(row) {
return (<div>
<span Class='text-primary'>{row.summary.success}</span>/
<span Class='text-danger'>{row.summary.failed}</span>/
<span>{row.summary.total}</span>
</div>)
formatter: (row) => {
return <div>
<span Class='text-primary'>{row.success_count}</span>/
<span>{row.publish_count}</span>
</div>
}
},
host_amount: {
label: this.$t('ops.hosts'),
width: '65px',
formatter: function(row) {
return _.get(row, 'latest_execution.hosts_amount', 0)
}
},
is_success: {
label: this.$t('ops.success'),
state: {
width: '60px',
align: 'center',
width: '80px',
formatter: row => {
if (_.get(row, 'latest_execution.is_success', false)) {
return <i Class='fa fa-check text-primary'/>
formatter: (row) => {
switch (row.state) {
case 'green':
return <i Class='fa fa-circle-o text-primary' />
case 'yellow':
return <i Class='fa fa-circle-o text-warning' />
case 'red':
return <i Class='fa fa-circle-o text-danger' />
}
return <i Class='fa fa-times text-danger'/>
}
},
date_start: {
label: this.$t('ops.date'),
width: '150px',
formatter: function(row) {
if (_.get(row, 'latest_execution.date_start', false)) {
return toSafeLocalDateStr(row.latest_execution.date_start)
}
return ''
}
},
time: {
label: this.$t('ops.time'),
width: '100px',
formatter: function(row) {
if (_.get(row, 'latest_execution.date_start', false)) {
return timeOffset(row.latest_execution.date_start, row.latest_execution.date_finished)
}
return ''
}
},
// runtimes: {
// label: this.$t('ops.runTimes'),
// width: '120px',
// formatter: function(row) {
// return (<div>
// <span Class='text-primary'>{row.summary.success}</span>/
// <span Class='text-danger'>{row.summary.failed}</span>/
// <span>{row.summary.total}</span>
// </div>)
// }
// },
// host_amount: {
// label: this.$t('ops.hosts'),
// width: '65px',
// formatter: function(row) {
// return _.get(row, 'latest_execution.hosts_amount', 0)
// }
// },
// is_success: {
// label: this.$t('ops.success'),
// align: 'center',
// width: '80px',
// formatter: row => {
// if (_.get(row, 'latest_execution.is_success', false)) {
// return <i Class='fa fa-check text-primary'/>
// }
// return <i Class='fa fa-times text-danger'/>
// }
// },
// date_start: {
// label: this.$t('ops.date'),
// width: '150px',
// formatter: function(row) {
// if (_.get(row, 'latest_execution.date_start', false)) {
// return toSafeLocalDateStr(row.latest_execution.date_start)
// }
// return ''
// }
// },
// time: {
// label: this.$t('ops.time'),
// width: '100px',
// formatter: function(row) {
// if (_.get(row, 'latest_execution.date_start', false)) {
// return timeOffset(row.latest_execution.date_start, row.latest_execution.date_finished)
// }
// return ''
// }
// },
actions: {
prop: 'id',
formatterArgs: {
hasUpdate: false,
hasClone: false,
canDelete: this.$hasPerm('ops.delete_task'),
onDelete: function({ row, col, cellValue, reload }) {
const msg = this.$t('common.deleteWarningMsg') + ` "${row.display_name || row.name}" ` + '?'
const title = this.$t('common.Info')
this.$alert(msg, title, {
type: 'warning',
confirmButtonClass: 'el-button--danger',
showCancelButton: true,
beforeClose: async(action, instance, done) => {
if (action !== 'confirm') return done()
instance.confirmButtonLoading = true
try {
await performDelete.bind(this)({ row: row, col: col })
done()
reload()
this.$message.success(this.$t('common.deleteSuccessMsg'))
} finally {
instance.confirmButtonLoading = false
}
}
})
},
extraActions: [
{
name: 'run',
can: this.$hasPerm('ops.add_adhoc'),
title: this.$t('ops.run'),
type: 'primary',
callback: function({ row, tableData }) {
this.$axios.get(
`/api/v1/ops/tasks/${row.id}/run/`
).then(res => {
openTaskPage(res['task'])
})
}
}
]
// hasUpdate: false,
// hasClone: false,
// canDelete: this.$hasPerm('ops.delete_task'),
// onDelete: function({ row, col, cellValue, reload }) {
// const msg = this.$t('common.deleteWarningMsg') + ` "${row.display_name || row.name}" ` + '?'
// const title = this.$t('common.Info')
// this.$alert(msg, title, {
// type: 'warning',
// confirmButtonClass: 'el-button--danger',
// showCancelButton: true,
// beforeClose: async(action, instance, done) => {
// if (action !== 'confirm') return done()
// instance.confirmButtonLoading = true
// try {
// await performDelete.bind(this)({ row: row, col: col })
// done()
// reload()
// this.$message.success(this.$t('common.deleteSuccessMsg'))
// } finally {
// instance.confirmButtonLoading = false
// }
// }
// })
// },
// extraActions: [
// {
// name: 'run',
// can: this.$hasPerm('ops.add_adhoc'),
// title: this.$t('ops.run'),
// type: 'primary',
// callback: function({ row, tableData }) {
// this.$axios.get(
// `/api/v1/ops/tasks/${row.id}/run/`
// ).then(res => {
// openTaskPage(res['task'])
// })
// }
// }
// ]
}
}
}
@ -133,7 +169,8 @@ export default {
hasCreate: false
}
}
}
},
methods: {}
}
</script>