blob: d890be8ded7ddeb73f0c3c32eaa41b4aaf0af1df [file] [log] [blame]
<!--
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<template>
<div class="app-container">
<div class="createPost-container">
<el-form :inline="true" :model="postForm" class="form-container">
<el-form-item :label="$t('tenant.label')">
<el-select v-model="postForm.tenant" placeholder="select tenant" style="width: 150px;" @change="getNamespacesList(postForm.tenant)">
<el-option v-for="(item,index) in tenantsListOptions" :key="item+index" :label="item" :value="item"/>
</el-select>
</el-form-item>
<el-form-item :label="$t('namespace.label')">
<el-select v-model="postForm.namespace" placeholder="select namespace" style="width: 150px;" @change="getTopicsList()">
<el-option v-for="(item,index) in namespacesListOptions" :key="item+index" :label="item" :value="item"/>
</el-select>
</el-form-item>
<el-form-item :label="$t('topic.label')">
<el-select v-model="postForm.topic" placeholder="select topic" style="width: 150px;" @change="generatePartitions()">
<el-option v-for="(item,index) in topicsListOptions" :key="item+index" :label="item" :value="item"/>
</el-select>
</el-form-item>
<el-form-item :label="$t('topic.partition')">
<el-select v-model="postForm.partition" :disabled="partitionDisabled" placeholder="select partition" style="width: 150px;" @change="getSubscriptionsList()">
<el-option v-for="(item,index) in partitionsListOptions" :key="item+index" :label="item" :value="item"/>
</el-select>
</el-form-item>
<el-form-item :label="$t('topic.subscription.label')">
<el-select v-model="postForm.subscription" placeholder="select subscription" style="width: 150px;" @change="getSubscriptionsInfo()">
<el-option v-for="(item,index) in subscriptionsListOptions" :key="item+index" :label="item" :value="item"/>
</el-select>
</el-form-item>
</el-form>
<el-form v-if="replicatedClusters.length > 0" :inline="true" :model="clusterForm" class="form-container">
<el-form-item :label="$t('table.cluster')">
<el-radio-group v-model="clusterForm.cluster" @change="onClusterChanged()">
<el-radio-button
v-for="cluster in replicatedClusters"
:key="cluster"
:label="cluster"/>
</el-radio-group>
</el-form-item>
</el-form>
</div>
<el-tabs v-model="topActiveName" @tab-click="handleClick">
<el-tab-pane :label="$t('topic.consumer.consumers')" name="consumers">
<el-row :gutter="24">
<el-col :xs="{span: 24}" :sm="{span: 24}" :md="{span: 24}" :lg="{span: 24}" :xl="{span: 24}">
<el-table
v-loading="consumersListLoading"
:key="consumerTableKey"
:data="consumersList"
border
fit
highlight-current-row
style="width: 100%;">
<el-table-column :label="$t('topic.consumer.name')" min-width="30px" align="center">
<template slot-scope="scope">
<span>{{ scope.row.consumerName }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('common.outMsg')" min-width="30px" align="center">
<template slot-scope="scope">
<span>{{ scope.row.outMsg }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('common.outBytes')" min-width="30px" align="center">
<template slot-scope="scope">
<span>{{ scope.row.outBytes }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('topic.consumer.avgMsgSize')" min-width="30px" align="center">
<template slot-scope="scope">
<span>{{ scope.row.avgMsgSize }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('topic.consumer.address')" min-width="30px" align="center">
<template slot-scope="scope">
<span>{{ scope.row.address }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('topic.consumer.since')" min-width="30px" align="center">
<template slot-scope="scope">
<span>{{ scope.row.since }}</span>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
</el-tab-pane>
<el-tab-pane :label="$t('topic.backlogOpeartion')" name="backlogOperation">
<el-tabs v-model="leftActiveName" :tab-position="tabPosition" @tab-click="handleLeftTabClick">
<!-- <el-tab-pane label="INSPECT" name="inspect">
<el-form :inline="true" :model="form">
<el-button type="primary" @click="handlePeekMessages">Peek</el-button>
<el-form-item>
<el-input v-model="form.peekNumMessages" placeholder="messages"/>
</el-form-item>
<span>messages</span>
</el-form>
<el-row :gutter="24" style="margin-top:15px">
<el-col :xs="{span: 24}" :sm="{span: 24}" :md="{span: 24}" :lg="{span: 24}" :xl="{span: 24}" style="padding-right:8px;margin-bottom:30px;">
<el-table
v-loading="inspectListLoading"
:key="inspectTableKey"
:data="inspectsList"
border
fit
highlight-current-row
style="width: 100%;">
<el-table-column label="Message ID" min-width="10px" align="center">
<template slot-scope="scope">
<span>{{ scope.row.messageId }}</span>
</template>
</el-table-column>
<el-table-column label="data" min-width="30px" align="center">
<template slot-scope="scope">
<span>{{ scope.row.data }}</span>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
</el-tab-pane> -->
<el-tab-pane :label="$t('topic.subscription.skip')" name="skip">
<el-form :inline="true" :model="form">
<el-button type="primary" @click="handleSkipMessages">{{ $t('topic.subscription.skip') }}</el-button>
<el-form-item>
<el-input v-model="form.skipNumMessages" placeholder="messages"/>
</el-form-item>
<span>{{ $t('topic.subscription.skipMessage') }}</span>
</el-form>
</el-tab-pane>
<el-tab-pane :label="$t('topic.subscription.expire')" name="expire">
<el-form :inline="true" :model="form">
<el-button type="primary" @click="handleExpireMessages">{{ $t('topic.subscription.expire') }}</el-button>
<el-form-item>
<el-input v-model="form.expireNumMessages" placeholder="messages"/>
</el-form-item>
<span>{{ $t('topic.subscription.expireMessage') }}</span>
</el-form>
</el-tab-pane>
<el-tab-pane :label="$t('topic.subscription.clear')" name="clear">
<el-form :inline="true" :model="form">
<el-button type="primary" @click="handleClearBacklog">{{ $t('topic.subscription.clear') }}</el-button>
<span>{{ $t('topic.subscription.clearMessage') }}</span>
</el-form>
</el-tab-pane>
<el-tab-pane :label="$t('topic.subscription.reset')" name="reset">
<el-form :inline="true" :model="form">
<el-button type="primary" @click="handleResetCursorByTime">{{ $t('topic.subscription.resetByTimeMessage') }}</el-button>
<span>The cursor to</span>
<el-form-item>
<el-input v-model="form.minutes" placeholder="minutes"/>
</el-form-item>
<span>{{ $t('topic.subscription.mins') }}</span>
<br>
<el-button type="primary" @click="handleResetCursorByMessageId">{{ $t('topic.subscription.resetById') }}</el-button>
<span>{{ $t('topic.subscription.messageId') }}</span>
<el-form-item>
<el-select v-model="form.ledgerValue" :placeholder="$t('topic.segment.ledgerId')" style="width:150px">
<el-option
v-for="item in ledgerOptions"
:key="item.value"
:label="item.label"
:value="item.value"/>
</el-select>
<el-input v-model="form.messagesId" :placeholder="$t('topic.subscription.messageId')" style="width: 150px"/>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import { fetchTenants } from '@/api/tenants'
import { fetchNamespaces, getClusters } from '@/api/namespaces'
import {
fetchTopicsByPulsarManager,
fetchTopicStats,
// peekMessages,
skipOnCluster,
expireMessageOnCluster,
clearBacklogOnCluster,
fetchTopicStatsInternal,
resetCursorByTimestampOnCluster,
resetCursorByPositionOnCluster
} from '@/api/topics'
import { fetchSubscriptions } from '@/api/subscriptions'
import { formatBytes } from '@/utils/index'
import { numberFormatter } from '@/filters/index'
const defaultForm = {
persistent: '',
tenant: '',
namespace: '',
topic: '',
partition: '',
subscription: ''
}
const defaultClusterForm = {
cluster: ''
}
export default {
name: 'SubscriptionInfo',
data() {
return {
postForm: Object.assign({}, defaultForm),
clusterForm: Object.assign({}, defaultClusterForm),
subscriptionsListOptions: [],
replicatedClusters: [],
topActiveName: 'consumers',
leftActiveName: '',
currentTopTabName: 'consumers',
currentLeftTabName: 'skip',
tenantsListOptions: [],
namespacesListOptions: [],
topicsListOptions: [],
consumersListLoading: false,
consumerTableKey: 0,
consumersList: [],
consumersTotal: 0,
consumersListQuery: {
page: 0,
limit: 1
},
inspectsList: [],
inspectTableKey: 0,
inspectListLoading: false,
tabPosition: 'left',
form: {
peekNumMessages: 0,
skipNumMessages: 0,
expireNumMessages: 10,
minutes: '0',
messagesId: '',
ledgerValue: ''
},
firstInitNamespace: false,
firstInitTopic: false,
firstInitSubscription: false,
ledgerOptions: [],
partitionsListOptions: [],
topicPartitions: {},
partitionDisabled: false
}
},
created() {
this.postForm.persistent = this.$route.params && this.$route.params.persistent
this.postForm.tenant = this.$route.params && this.$route.params.tenant
this.postForm.namespace = this.$route.params && this.$route.params.namespace
this.postForm.topic = this.$route.params && this.$route.params.topic
if (this.postForm.topic.indexOf('-partition-') > 0) {
var splitTopic = this.postForm.topic.split('-partition-')
this.postForm.partition = splitTopic[1]
this.postForm.topic = splitTopic[0]
} else {
this.postForm.partition = '-1'
this.partitionDisabled = true
}
this.tenantNamespaceTopic = this.postForm.tenant + '/' + this.postForm.namespace + '/' + this.postForm.topic
this.postForm.subscription = this.$route.params && this.$route.params.subscription
this.firstInitNamespace = true
this.firstInitTopic = true
this.firstInitSubscription = true
if (this.$route.query && this.$route.query.topTab) {
this.topActiveName = this.$route.query.topTab
this.currentTopTabName = this.$route.query.topTab
if (this.$route.query.leftTab) {
this.leftActiveName = this.$route.query.leftTab
}
}
this.getRemoteTenantsList()
this.getNamespacesList(this.postForm.tenant)
this.getReplicatedClusters()
this.getTopicsList()
this.initTopicStats()
this.handleStatsInternal()
},
methods: {
generatePartitions() {
var partitions = parseInt(this.topicPartitions[this.postForm.topic])
this.partitionsListOptions = []
if (partitions > 0) {
this.partitionDisabled = false
if (this.postForm.partition === '-1') {
this.postForm.partition = ''
}
for (var i = 0; i < partitions; i++) {
this.partitionsListOptions.push(i)
}
if (this.firstInitSubscription) {
this.getSubscriptionsList()
} else {
this.subscriptionsListOptions = []
this.postForm.subscription = ''
}
} else {
this.partitionDisabled = true
this.postForm.partition = '-1'
this.partitionsListOptions.push('-1')
this.getSubscriptionsList()
}
},
getRemoteTenantsList() {
fetchTenants().then(response => {
if (!response.data) return
for (var i = 0; i < response.data.total; i++) {
this.tenantsListOptions.push(response.data.data[i].tenant)
}
})
},
getReplicatedClusters() {
if (this.postForm.tenant && this.postForm.namespace) {
getClusters(this.postForm.tenant, this.postForm.namespace).then(response => {
if (!response.data) {
return
}
this.replicatedClusters = response.data
if (response.data.length > 0) {
this.clusterForm.cluster = this.routeCluster || this.replicatedClusters[0]
}
})
}
},
getCurrentCluster() {
return this.clusterForm.cluster || ''
},
getNamespacesList(tenant) {
let namespace = []
this.namespacesListOptions = []
this.topicsListOptions = []
this.partitionsListOptions = []
this.subscriptionsListOptions = []
if (this.firstInitNamespace) {
this.firstInitNamespace = false
} else {
this.postForm.namespace = ''
this.postForm.topic = ''
this.postForm.partition = ''
this.postForm.subscription = ''
}
fetchNamespaces(tenant, this.query).then(response => {
if (!response.data) return
for (var i = 0; i < response.data.data.length; i++) {
namespace = response.data.data[i].namespace
this.namespacesListOptions.push(namespace)
}
})
},
getTopicsList() {
this.getReplicatedClusters()
this.topicsListOptions = []
this.partitionsListOptions = []
this.subscriptionsListOptions = []
if (this.firstInitTopic) {
this.firstInitTopic = false
} else {
this.postForm.topic = ''
this.postForm.partition = ''
this.postForm.subscription = ''
}
fetchTopicsByPulsarManager(this.postForm.tenant, this.postForm.namespace).then(response => {
if (!response.data) return
for (var i in response.data.topics) {
this.topicsListOptions.push(response.data.topics[i]['topic'])
this.topicPartitions[response.data.topics[i]['topic']] = response.data.topics[i]['partitions']
if (response.data.topics[i]['topic'] === this.postForm.topic) {
this.generatePartitions()
}
}
})
},
getSubscriptionsList() {
fetchSubscriptions(this.postForm.persistent, this.getFullTopic()).then(response => {
if (!response.data) return
if (this.firstInitSubscription) {
this.firstInitSubscription = false
} else {
this.subscriptionsListOptions = []
this.postForm.subscription = ''
}
for (var i in response.data) {
this.subscriptionsListOptions.push(response.data[i])
}
})
},
getSubscriptionsInfo() {
this.$router.push({ path: '/management/subscriptions/' + this.postForm.persistent +
'/' + this.getFullTopic() + '/' + this.postForm.subscription + '/subscription?topTab=' +
this.currentTopTabName + '&leftTab=' + this.currentLeftTabName })
},
handleStatsInternal() {
fetchTopicStatsInternal(this.postForm.persistent, this.getFullTopic()).then(response => {
if (!response.data) return
for (var i in response.data.ledgers) {
this.ledgerOptions.push({
value: response.data.ledgers[i]['ledgerId'],
label: response.data.ledgers[i]['ledgerId']
})
}
})
},
getFullTopic() {
var fullTopic = this.postForm.tenant + '/' + this.postForm.namespace + '/' + this.postForm.topic
if (parseInt(this.postForm.partition) >= 0) {
fullTopic += '-partition-' + this.postForm.partition
}
return fullTopic
},
initTopicStats() {
fetchTopicStats(this.postForm.persistent, this.getFullTopic()).then(response => {
if (!response.data) return
if (response.data.subscriptions.hasOwnProperty(this.postForm.subscription)) {
var subscription = response.data.subscriptions[this.postForm.subscription]
if (subscription.hasOwnProperty('consumers')) {
var consumers = subscription['consumers']
for (var s in consumers) {
this.consumersList.push({
'consumerName': consumers[s].consumerName,
'outMsg': numberFormatter(consumers[s].msgRateOut, 2),
'outBytes': formatBytes(consumers[s].msgThroughputOut),
'avgMsgSize': formatBytes(response.data.averageMsgSize),
'address': consumers[s].address,
'since': consumers[s].connectedSince
})
}
}
}
})
},
handleClick(tab, event) {
this.currentTopTabName = tab.name
if (this.currentTopTabName === 'backlogOperation') {
this.$router.push({ query: { 'topTab': tab.name, 'leftTab': this.currentLeftTabName }})
} else {
this.$router.push({ query: { 'topTab': tab.name }})
}
},
handleLeftTabClick(tab, event) {
this.currentLeftTabName = tab.name
},
getConsumers() {
},
// To do, parse message
// handlePeekMessages() {
// if (this.form.peekNumMessages <= 0) {
// this.$notify({
// title: 'error',
// message: 'Messages should greater than 0',
// type: 'error',
// duration: 3000
// })
// return
// }
// peekMessages(this.postForm.persistent, this.tenantNamespaceTopic, this.postForm.subscription, this.form.peekNumMessages).then(response => {
// if (!response.data) return
// console.log(response)
// console.log(response.data)
// })
// },
handleSkipMessages() {
if (this.form.skipNumMessages <= 0) {
this.$notify({
title: 'error',
message: 'Messages should greater than 0',
type: 'error',
duration: 3000
})
return
}
skipOnCluster(this.getCurrentCluster(), this.postForm.persistent, this.getFullTopic(), this.postForm.subscription, this.form.skipNumMessages).then(response => {
this.$notify({
title: 'success',
message: 'Messages skip success',
type: 'success',
duration: 3000
})
})
},
handleExpireMessages() {
if (this.form.expireMessages <= 0) {
this.$notify({
title: 'error',
message: 'Messages should greater than 0',
type: 'error',
duration: 3000
})
return
}
expireMessageOnCluster(this.getCurrentCluster(), this.postForm.persistent, this.getFullTopic(), this.postForm.subscription, this.form.expireNumMessages).then(response => {
this.$notify({
title: 'success',
message: 'Messages expire success',
type: 'success',
duration: 3000
})
})
},
handleClearBacklog() {
clearBacklogOnCluster(this.getCurrentCluster(), this.postForm.persistent, this.getFullTopic(), this.postForm.subscription).then(response => {
this.$notify({
title: 'success',
message: 'Clear messages success',
type: 'success',
duration: 3000
})
})
},
handleResetCursorByTime() {
if (parseInt(this.form.minutes) <= 0) {
this.$notify({
title: 'error',
message: 'Minutes cannot be less than 0',
type: 'error',
duration: 3000
})
return
}
var dateTime = new Date().getTime()
var timestamp = Math.floor(dateTime) - parseInt(this.form.minutes) * 60 * 1000
resetCursorByTimestampOnCluster(
this.getCurrentCluster(),
this.postForm.persistent,
this.getFullTopic(),
this.postForm.subscription, timestamp).then(response => {
this.$notify({
title: 'success',
message: 'Reset cursor success',
type: 'success',
duration: 3000
})
})
},
handleResetCursorByMessageId() {
if (this.form.messagesId.length <= 0 && this.form.ledgerValue != null) {
this.$notify({
title: 'error',
message: 'Message Id cannot be less than 0',
type: 'error',
duration: 3000
})
return
}
var data = {
'ledgerId': this.form.ledgerValue,
'entryId': parseInt(this.form.messagesId)
}
resetCursorByPositionOnCluster(this.getCurrentCluster(), this.postForm.persistent, this.getFullTopic(), this.postForm.subscription, data).then(response => {
this.$notify({
title: 'success',
message: 'Reset cursor success',
type: 'success',
duration: 3000
})
})
},
handleFilterConsumer() {
}
}
}
</script>