fix: 展示调整

develop
时启龙 2024-08-30 17:08:22 +08:00
parent 2d46aa7999
commit 309ec2e592
37 changed files with 6000 additions and 3 deletions

View File

@ -0,0 +1,92 @@
<template>
<div>
<el-dialog title="新增集群" width="40%" :close-on-click-modal="false" v-if="addData.dialog" :visible.sync="addData.dialog">
<cb-form :model="addData.data" ref="data">
<el-row :gutter="10">
<el-col :span="24">
<cb-form-item label="地域:" prop="region" validate="required" required-message="">
<el-select v-model="addData.data.region" filterable>
<el-option v-for="(item, index) in regions" :key="index" :label="item.regionName" :value="item.alias"></el-option>
</el-select>
</cb-form-item>
</el-col>
</el-row>
</cb-form>
<div slot="footer" class="dialog-footer">
<el-button type="ghost" @click.native="addData.dialog = false">取消</el-button>
<el-button type="primary" @click.native="ok">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
/* global $ */
import { modifyInstance, getTKERegions } from 'views/tce/services/tke.js'
export default {
props: {
addData: {
type: Object,
default: function () {
return {
data: {
name: '',
vendorId: -1
},
dialog: false
}
}
},
vendorId: {
type: Number,
default: 0
},
platformObject: {
type: Object,
default() {
return {}
}
}
},
data() {
return {
regions: []
}
},
methods: {
getRegionList() {
this.regions = []
getTKERegions({
tenantUuid: this.platformObject.tceTenantUuid,
vendorId: this.vendorId
}).then(data => {
if (data.success) {
this.regions = data.data.regionInstanceSet
}
})
},
ok() {
this.$refs.data.validate(valid => {
if (valid) {
const param = {
id: this.addData.data.id,
newName: this.addData.data.name
}
}
})
}
},
created() {},
watch: {
'addData.dialog': {
handler(newVal, oldVal) {
if (newVal) {
this.getRegionList()
}
},
deep: true,
immediate: true
}
}
}
</script>

View File

@ -0,0 +1,77 @@
<template>
<div>
<el-dialog title="绑定云主机" :visible.sync="addData.dialog" width="40%">
<el-row>
<el-form label-width="90px" :model="addData.data" ref="data">
<cb-form-item label="云主机:" prop="serverId" validate="required" required-message="">
<el-select v-model="addData.data.serverId">
<el-option v-for="(item, index) in vmList" :label="item.name" :value="item.id" :key="index"></el-option>
</el-select>
</cb-form-item>
</el-form>
</el-row>
<span slot="footer" class="dialog-footer">
<el-button type="ghost" @click="addData.dialog = false"> </el-button>
<el-button type="primary" @click="ok"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
/* global $ */
import { floatIpPorts } from 'views/tce/services/floatips.js'
import { getVm } from 'views/tce/services/vms.js'
export default {
props: {
addData: {
type: Object,
default: function () {
return {
data: {
labelIds: []
},
dialog: false
}
}
}
},
data() {
return {
vmList: []
}
},
methods: {
ok() {
this.$refs.data.validate(valid => {
if (valid) {
floatIpPorts(this.addData.data).then(data => {
if (data.success) {
this.$message({
type: 'success',
message: data.message
})
this.addData.dialog = false
this.$parent.$parent.getData()
}
})
}
})
},
//
getVm() {
getVm({
simple: true,
params: JSON.stringify([{ param: { vendorId: this.addData.data.vendorId, regionId: this.addData.data.regionId }, sign: 'EQ' }])
}).then(data => {
if (data.success) {
this.vmList = data.data.rows
}
})
}
},
created() {
this.getVm()
}
}
</script>

View File

@ -0,0 +1,321 @@
<template>
<div>
<cb-advance-table @cell-click="handleCellClick" :data="clusterTableData" :params="params" :columns="Clustercolumns" :get-list="getClusterData" :total="total" :loading="loading">
<template v-slot:action>
<el-button type="primary" @click="addCluster()"> </el-button>
</template>
<template #name="val, record">
<span class="detail-href" @click="getDetail(record)" v-if="record.clusterStatus !== 'Initializing'">{{ val }}</span>
<span v-else>{{ val }}</span>
</template>
<template #clusterStatus="val">
<cb-status-icon :type="val | tceClusterColor" v-if="val && val.toLowerCase() != 'stopped'">{{ val | tceClusterText }}</cb-status-icon>
</template>
<template #clusterOs="val">
{{ val ? val : 'ubuntu16.04.1 LTSx86_64' }}
</template>
<template #clusterVersion="val">
{{ val && val.length > 0 ? val : '1.10.5' }}
</template>
<template #operate="val, record">
<AssignTenant :detail="record" type="TKE" @back="getClusterData"></AssignTenant>
<div class="action-divider"></div>
<el-button type="text" @click="edit(record)" :disabled="record.clusterStatus == 'Initializing'"> 编辑 </el-button>
<div class="action-divider"></div>
<el-button type="text" @click="handleOperate({ flag: 3, row: record })" :disabled="record.status === 'BIND' || record.tenantName"> 删除 </el-button>
</template>
</cb-advance-table>
<detail v-if="detaildialogVisible" :detail="detail" @back="detaildialogVisible = false" :platform-object="platformObject"></detail>
<bind :add-data="bindData" v-if="bindData.dialog"></bind>
<service ref="serviceTable" v-if="serviceVisible" :vendorId="platformObject.vendorId" :clusterInfo="clusterInfo" @close="closeService"></service>
<modify-cluster :add-data="modifyClusterData" :vendor-id="platformObject.vendorId" v-if="modifyClusterData.dialog"></modify-cluster>
<delete-cluster :data="deleteClusterData" :vendor-id="platformObject.vendorId" v-if="deleteClusterData.dialog" :clusterInfo="clusterInfo"></delete-cluster>
<add-cluster :add-data="addClusterData" :vendor-id="platformObject.vendorId" :platform-object="platformObject" v-if="addClusterData.dialog"></add-cluster>
</div>
</template>
<script>
/* global $ */
import bind from './bind.vue'
import { getClusterList } from 'views/tce/services/tke.js'
import { removeFloatIp, detailFloatIp } from 'views/tce/services/floatips.js'
import { getRegion } from 'views/tce/services/regions.js'
import service from './service/index.vue'
import modifyCluster from './modifyCluster.vue'
import deleteCluster from './deleteCluster.vue'
import addCluster from './addCluster.vue'
import webSocket from '@cmp/cmp-common/mixins/getGlobalSocket'
import detail from './detail'
import AssignTenant from 'views/platform/assign/index.vue'
const Clustercolumns = [
{
label: 'ID',
prop: 'clusterId',
disabled: true,
scopedSlots: { customRender: 'name' }
},
{
label: '名称',
prop: 'name'
},
{
label: '状态',
prop: 'clusterStatus',
scopedSlots: { customRender: 'clusterStatus' }
},
{
label: '版本',
prop: 'clusterVersion',
scopedSlots: { customRender: 'clusterVersion' }
},
{
label: '类型',
prop: 'clusterType',
customRender(val) {
const map = {
INDEPENDENT_CLUSTER: '独立集群'
}
return map[val] || val
}
},
{
label: '节点数量',
prop: 'clusterNodeNum'
},
{
label: '所属租户',
prop: 'tenantName'
},
{
label: '描述',
prop: 'clusterDescription'
},
{
label: '系统',
prop: 'clusterOs',
scopedSlots: { customRender: 'clusterOs' }
},
{
label: '创建时间',
prop: 'gmtCreate'
},
{
label: '操作',
scopedSlots: { customRender: 'operate' },
width: '220px'
}
]
export default {
mixins: [webSocket],
components: {
bind,
service,
modifyCluster,
deleteCluster,
addCluster,
detail,
AssignTenant
},
props: {
platformObject: {
type: Object,
default: function () {
return {
vendorId: -1
}
}
}
},
data() {
return {
clusterInfo: {},
instanceVisible: false,
secrectVisible: false,
serviceVisible: false,
flexibleVisible: false,
Clustercolumns,
clusterTableData: [],
activeName: 'first', //
loading: false,
detaildialogVisible: false,
detail: {},
params: {
page: 1,
rows: 10
},
total: 0,
addData: {
dialog: false,
data: {}
},
regionList: [],
bindData: {
dialog: false,
data: {}
},
modifyClusterData: {
dialog: false,
data: {}
},
addClusterData: {
dialog: false,
data: {}
},
deleteClusterData: {
dialog: false,
data: {}
}
}
},
methods: {
onmessage(data) {
const arr = ['tke.modify.cluster', 'tke.delete.instance', 'tke.add.instance', 'tce.sync', 'tce.cluster', 'tke.create.cluster', 'tke.delete.cluster']
if (arr.includes(data.operate)) {
this.getClusterData()
}
},
addCluster() {
this.$router.push({ name: 'tceCreateCluster', query: { vendorId: this.platformObject.vendorId } })
},
handleCellClick(data) {
this.clusterInfo = data
},
edit(row) {
this.modifyClusterData = {
dialog: true,
data: {
name: row.name,
id: row.id
}
}
},
handleOperate(command) {
switch (command.flag) {
case 1: //
this.showSecrect(command.row)
break
case 2: //
this.showService(command.row)
break
case 3: //
this.deleteCluster(command.row)
break
case 4: //
this.addExistInstance(command.row)
break
case 5: //
this.showFlexible(command.row)
break
case 'node':
this.showInstance(command.row)
break
default:
}
},
deleteCluster(row) {
this.clusterInfo = row
this.deleteClusterData = {
dialog: true,
mode: false,
data: row
}
},
closeService() {
this.serviceVisible = false
},
showService(row) {
this.serviceVisible = true
this.clusterInfo = row
},
closeSecrect() {
this.secrectVisible = false
},
showSecrect(row) {
this.secrectVisible = true
this.clusterInfo = row
},
showFlexible(row) {
this.flexibleVisible = true
this.clusterInfo = row
},
closeInstance() {
this.instanceVisible = false
},
closeFlexible() {
this.flexibleVisible = false
},
showInstance(row) {
this.instanceVisible = true
this.clusterInfo = row
},
getClusterData() {
// if (this.instanceVisible) {
// this.$refs.instanceTable.getData()
// return
// }
this.loading = true
this.clusterTableData = []
this.total = 0
const params = {
page: this.params.page,
rows: this.params.rows,
params: JSON.stringify([
{
param: {
vendorId: this.platformObject.vendorId,
tenantUuid: this.platformObject.tecTenantUuid,
regionId: this.platformObject.tceRegionId
},
sign: 'EQ'
}
])
}
getClusterList(params).then(data => {
if (data.success) {
this.loading = false
this.clusterTableData = data.data.rows
this.total = data.data.total
}
})
},
createCluster() {
this.$router.push({ name: 'tceClusterAdd', params: { id: this.platformObject.vendorId } })
},
//
getDetail(row) {
this.detaildialogVisible = true
this.detail = row
},
add() {
this.addData = {
dialog: true,
data: {
vendorId: this.platformObject.vendorId
}
}
},
//
getRegion() {
getRegion({ vendorId: this.platformObject.vendorId }).then(data => {
if (data.success) {
this.regionList = data.data
}
})
}
},
mounted() {
if (this.$router.currentRoute.params.formCreateInstance) {
this.showInstance(this.$router.currentRoute.params.clusterInfo)
}
},
watch: {
platformObject: {
handler(newVal, oldVal) {
this.getClusterData()
},
deep: true,
immediate: true
}
}
}
</script>

View File

@ -0,0 +1,75 @@
<template>
<div>
<el-dialog title="分配IP" append-to-body :visible.sync="dialog.visible" width="400px" :close-on-click-modal="false">
<div>
<div>网段{{ dialog.data.cidr }}</div>
<div v-for="(item, index) in address" :key="index" class="m-t-md">
<el-input v-model="address[index]"></el-input>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button type="ghost" @click="dialog.visible = false"> </el-button>
<el-button type="primary" @click="ok"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { conditionSubnet } from 'views/tce/services/subnets.js'
export default {
props: {
dialog: {
type: Object
}
},
data() {
return {
address: []
}
},
methods: {
ok() {
if (this.address.indexOf('') > -1) return this.$message.error('请输入IP')
let flag = false
const ary = []
const reg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/
for (const a of this.address) {
if (!reg.test(a)) {
this.$message.error(`IP[${a}]输入有误`)
flag = true
break
}
if (ary.indexOf(a) == -1) ary.push(a)
if (this.dialog.data.allIpAddress.includes(a)) {
this.$message.error(`IP[${a}]已经被其他节点占用,请选择其他可用IP`)
flag = true
break
}
}
if (flag) return
if (ary.length != this.address.length) return this.$message.error('IP输入重复')
const { vendorId, SubnetId } = this.dialog.data
conditionSubnet({
condition: JSON.stringify({
condition: 'ipCheck',
vendorId,
subnetUuid: SubnetId,
ips: this.address
})
}).then(data => {
if (data.success) {
this.$emit('setIp', this.address)
this.dialog.visible = false
}
})
}
},
created() {
const { InstanceCount, PrivateIpAddresses } = this.dialog.data
for (let i = 0; i < InstanceCount; i++) {
this.address.push(PrivateIpAddresses.length > i ? PrivateIpAddresses[i] : '')
}
}
}
</script>

View File

@ -0,0 +1,268 @@
<template>
<el-row>
<el-col :span="24">
<cb-form-item :label="label">
<div v-for="(node, index) in nodeList" :key="index" class="host-card">
<el-row style="text-align: right" v-if="index != 0">
<el-button type="text" style="padding-right: 30px !important" @click.native="deletNode(index)">删除</el-button>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="可用区:" :prop="type + 'List.' + index + '.zoneId'" validate="required" required-message="">
<el-select v-model="node.zoneId" @change="handleZone(node)">
<el-option v-for="(item, index) in zoneList" :label="item.name" :value="item.zoneId" :key="index"></el-option>
</el-select>
</cb-form-item>
</el-col>
<el-col :span="24">
<cb-form-item label="节点网络:" :prop="type + 'List.' + index + '.SubnetId'" validate="required" required-message="">
<el-col :span="11" class="m-r-xs">
<el-input v-model="addData.vpcName" disabled></el-input>
</el-col>
<el-col :span="11">
<el-select v-model="node.SubnetId" v-if="node.subnetList.length != 0" placeholder="请选择子网" @change="clearPrimaryIps(node)">
<el-option v-for="(item, index) in node.subnetList" :label="item.name" :value="item.subnetUuid" :key="index"></el-option>
</el-select>
<span v-else style="color: red">此私有网络在该可用区无有效子网</span>
</el-col>
</cb-form-item>
</el-col>
</el-row>
<el-row v-if="node.SubnetId">
<el-col :span="24">
<cb-form-item label="分配IP">
<el-radio-group v-model="node.subentCreateType" class="m-r">
<el-radio-button label="Automatic">自动分配</el-radio-button>
<el-radio-button label="Manual">手动分配</el-radio-button>
</el-radio-group>
<template v-if="node.subentCreateType == 'Manual'">
<el-button type="text" @click="openIpDialog(node)">IP</el-button>
<el-tag v-for="item in node.PrivateIpAddresses" :key="item" class="m-l-sm">{{ item }}</el-tag>
</template>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="机型:">
{{ node.hostData.flavorUuid ? `${node.hostData.flavorUuid}(${node.hostData.cpu}${node.hostData.memory}GB)` : '请先选择机型' }}
<el-button type="text" icon="el-icon-edit" @click="openHostDialog(node)" :disabled="!node.zoneId"></el-button>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="系统盘:">
{{ node.SystemDisk.DiskSize ? `${node.SystemDisk.diskCategory} ${node.SystemDisk.DiskSize}GB` : '暂不购买' }}
<el-button type="text" :disabled="!node.zoneId" icon="el-icon-edit" @click="openDiskDialog('systemDisk', node)"></el-button>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="数据盘:">
{{ node.DataDisks.DiskSize ? `${node.DataDisks.diskCategory} ${node.DataDisks.DiskSize}GB` : '暂不购买' }}
<el-button type="text" :disabled="!node.zoneId" icon="el-icon-edit" @click="openDiskDialog('dataDisk', node)"></el-button>
<el-button type="text" @click="cancelDiskData('DataDisks', node)" v-if="node.DataDisks.DiskSize"></el-button>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="数量:">
<el-input-number :min="1" :max="100" v-model="node.InstanceCount"></el-input-number>
</cb-form-item>
</el-col>
</el-row>
</div>
</cb-form-item>
<cb-form-item label="">
<el-button style="width: 100%" type="ghost" @click="addNode"></el-button>
</cb-form-item>
<HostDialog :dialog="hostDialog" v-if="hostDialog.visible" @getHostData="getHostData"></HostDialog>
<DiskDialog :diskDialog="diskDialog" :platform-object="platformObject" v-if="diskDialog.visible" @getDiskData="getDiskData"></DiskDialog>
<AssignIp v-if="assignIpDialog.visible" :dialog="assignIpDialog" @setIp="setIp"></AssignIp>
</el-col>
</el-row>
</template>
<script>
import { ref, computed } from 'vue'
import { getSubnet } from 'views/tce/services/subnets.js'
import HostDialog from './hostDialog.vue'
import DiskDialog from './diskDialog.vue'
import AssignIp from './AssignIpItem.vue'
import { instanceParams } from './data'
export default {
components: { HostDialog, DiskDialog, AssignIp },
props: {
type: {
type: String,
default: 'master'
},
label: {
type: String
},
zoneList: {},
addData: {},
nodeList: {
type: Array
},
platformObject: {
type: Object
}
},
setup(props) {
const allIpAddress = computed(() => {
const { masterList, nodeList } = props.addData
const address = []
;[...masterList, ...nodeList].forEach(item => {
address.push(...item.PrivateIpAddresses)
})
return address
})
function deletNode(index) {
props.nodeList.splice(index, 1)
}
function addNode() {
props.nodeList.push({
...instanceParams
})
}
function cancelDiskData(type, node) {
node[type] = {
DiskType: '',
DiskSize: ''
}
}
const diskDialog = ref({})
function getDiskData(data) {
switch (data.type) {
case 'systemDisk':
currentNode.value.SystemDisk = data.data
break
case 'dataDisk':
currentNode.value.DataDisks = data.data
break
default:
}
diskDialog.value.visible = false
}
function openDiskDialog(type, node) {
currentNode.value = node
diskDialog.value = {
visible: true,
type,
regionId: this.addData.regionId,
zoneId: node.zoneId,
vendorId: this.addData.vendorId,
data: type === 'systemDisk' ? node.SystemDisk : node.DataDisks
}
}
//
function getHostData(data) {
const item = currentNode.value
item.hostData = data
item.SystemDisk = {
DiskType: 'CLOUD_SSD',
DiskSize: 50,
diskCategory: 'SSD云硬盘'
}
}
const hostDialog = ref({
visible: false,
data: {}
})
const currentNode = ref({})
function openHostDialog(node) {
currentNode.value = node
const { hostData } = node
hostDialog.value = {
visible: true,
data: hostData,
type: props.type,
regionId: this.addData.regionId,
zoneId: node.zoneId
}
}
// IP
const assignIpDialog = ref({})
function openIpDialog(node) {
currentNode.value = node
const { SubnetId, PrivateIpAddresses, InstanceCount, subnetList } = node
const obj = subnetList.find(item => item.subnetUuid == SubnetId)
assignIpDialog.value = {
visible: true,
data: {
cidr: obj.cidr,
PrivateIpAddresses,
InstanceCount,
allIpAddress: allIpAddress.value.filter(item => !PrivateIpAddresses.includes(item)),
SubnetId,
vendorId: props.addData.vendorId
}
}
}
function setIp(data) {
currentNode.value.PrivateIpAddresses = data
}
function clearPrimaryIps(node) {
node.PrivateIpAddresses = []
}
//
function handleZone(node) {
node.zoneName = props.zoneList.find(e => e.zoneId == node.zoneId).name
//
node.SubnetId = ''
cancelDiskData('SystemDisk', node)
cancelDiskData('DataDisks', node)
node.hostData = {}
getSubnetList(node)
}
function getSubnetList(node) {
getSubnet({
page: 1,
rows: 9999,
params: JSON.stringify([
{
param: {
vendorId: props.addData.vendorId,
networkId: props.addData.vpcNumberId,
type: 0
},
sign: 'EQ'
}
])
}).then(data => {
if (data.success) {
node.subnetList = data.data.rows.filter(temp => temp.zone == node.zoneId)
}
})
}
return {
deletNode,
addNode,
cancelDiskData,
diskDialog,
openDiskDialog,
getDiskData,
//
hostDialog,
openHostDialog,
getHostData,
assignIpDialog,
openIpDialog,
setIp,
clearPrimaryIps,
handleZone
}
}
}
</script>
<style scoped>
.host-card {
background-color: rgb(242, 242, 242) !important;
padding: 18px 18px 0 18px;
margin-bottom: 20px;
}
</style>

View File

@ -0,0 +1,509 @@
<template>
<cb-form :model="addData" ref="addData" label-width="160px">
<el-row>
<el-col :span="12">
<cb-form-item label="地域:">
<el-select v-model="region" @change="changeRegion">
<el-option v-for="(item, index) in platformObject.tceRegionList" :label="item.name" :value="item.regionId" :key="index"></el-option>
</el-select>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="名称:" prop="name" validate="notZN,required" required-message="">
<el-input v-model="addData.name" maxlength="20" show-word-limit></el-input>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="Kubernetes版本" prop="kubernetes" validate="required" required-message="Kubernetes">
<el-select v-model="addData.kubernetes">
<el-option v-for="(item, index) in kubernetesList" :label="item.version" :value="item.version" :key="index"></el-option>
</el-select>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="容器集群地域:" prop="regionId" validate="required" required-message="">
<el-select v-model="addData.regionId" @change="region">
<el-option v-for="(item, index) in regionList" :label="item.regionName" :value="item.alias" :key="index"></el-option>
</el-select>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="集群网络:" prop="vpcId" validate="required" required-message="">
<el-select v-model="addData.vpcId" @change="handleVpc">
<el-option v-for="(item, index) in vpcList" :key="index" :label="item.name" :value="item.vpcId"></el-option>
</el-select>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="容器网络插件:" prop="networkPlug" validate="required" required-message="">
<el-radio-group v-model="addData.networkPlug" @change="handleNetwork">
<el-radio-button label="router">Global Router</el-radio-button>
<el-radio-button label="vpc">VPC-CNI</el-radio-button>
</el-radio-group>
</cb-form-item>
</el-col>
</el-row>
<el-row v-if="addData.networkPlug == 'router'">
<cb-form-item label="CIDR">
<el-select style="width: 100px" v-model="addData.one" @change="handleOne(1)">
<el-option value="10"></el-option>
<el-option value="172"></el-option>
<el-option value="192"></el-option> </el-select
>&nbsp;.&nbsp; <el-input-number @change="handleTwoValue" :disabled="addData.one === '192'" :title="title" v-model="addData.two" :min="twoMin" :max="twoMax" style="width: 100px" :controls="false"></el-input-number>&nbsp;.&nbsp;0&nbsp;.&nbsp;0&nbsp;/&nbsp;
<el-select style="width: 100px" v-model="addData.end" @change="handleCIDR">
<el-option value="14" v-if="addData.one == 10"></el-option>
<el-option value="15" v-if="addData.one == 10"></el-option>
<el-option value="16"></el-option>
<el-option value="17"></el-option>
<el-option value="18"></el-option>
<el-option value="19"></el-option>
<el-option value="20"></el-option>
<el-option value="21"></el-option>
<el-option value="22"></el-option>
<el-option value="23"></el-option>
<el-option value="24"></el-option>
</el-select>
<span style="color: red; display: block" v-if="!grIsOk && !addData.checkLoading && CIDRIndex">{{ grMessage }}</span>
</cb-form-item>
</el-row>
<el-row v-if="addData.networkPlug == 'router'">
<el-col :span="12">
<cb-form-item label-width="200px" label="单节点Pod数量上限" prop="podNum" validate="required" required-message="Pod">
<el-select v-model="addData.podNum">
<el-option value="16"></el-option>
<el-option value="32"></el-option>
<el-option value="64"></el-option>
<el-option value="128"></el-option>
<el-option value="256"></el-option>
</el-select>
</cb-form-item>
</el-col>
</el-row>
<el-row v-if="addData.networkPlug == 'router'">
<el-col :span="12">
<cb-form-item label-width="200px" label="集群内Service数量上限" prop="serviceNum" validate="required" required-message="Service">
<el-select v-model="addData.serviceNum">
<el-option value="32"></el-option>
<el-option value="64"></el-option>
<el-option value="128"></el-option>
<el-option value="256"></el-option>
<el-option value="512"></el-option>
<el-option value="1024"></el-option>
<el-option value="2048"></el-option>
<el-option value="4096"></el-option>
<el-option value="8192"></el-option>
<el-option value="16384"></el-option>
<el-option value="32768"></el-option>
</el-select>
</cb-form-item>
</el-col>
<el-col :span="24">
<cb-form-item label=" " label-width="200px">
<span class="tip">
当前容器网络配置下集群最多 <span class="text-danger">{{ maxNode }}</span> 个节点含Master节点Master节点默认不支持部署业务POD因此实际可用于部署业务的节点数为集群的节点数减去Master节点数
</span>
</cb-form-item>
</el-col>
</el-row>
<el-row v-if="addData.networkPlug == 'vpc'">
<cb-form-item label="固定Pod IP">
<el-switch v-model="addData.podIpSwitch" @change="handlePodIpSwitch"></el-switch>
</cb-form-item>
</el-row>
<el-row v-if="addData.networkPlug == 'vpc'">
<cb-form-item label="容器子网:">
<cb-advance-table ref="multipleTable" :data="subnetList" :columns="subnetColumns" :get-list="getSubnet" :loading="loading" @selection-change="handleSelectChange">
<div slot="pagination"></div>
</cb-advance-table>
</cb-form-item>
</el-row>
<el-row v-if="addData.networkPlug == 'vpc'">
<cb-form-item label="Service IP段">
<el-select style="width: 100px" v-model="addData.oneService" @change="handleOne(2)">
<el-option value="10"></el-option>
<el-option value="172"></el-option>
<el-option value="192"></el-option> </el-select
>&nbsp;.&nbsp; <el-input-number @change="handleTwoValue" :disabled="addData.oneService === '192'" :title="title" v-model="addData.twoService" :min="twoMin" :max="twoMax" style="width: 100px" :controls="false"></el-input-number>&nbsp;.&nbsp;0&nbsp;.&nbsp;0&nbsp;/&nbsp;
<el-select style="width: 100px" v-model="addData.endService" @change="handleCIDR">
<el-option value="14" v-if="addData.oneService == 10"></el-option>
<el-option value="15" v-if="addData.oneService == 10"></el-option>
<el-option value="16"></el-option>
<el-option value="17"></el-option>
<el-option value="18"></el-option>
<el-option value="19"></el-option>
<el-option value="20"></el-option>
<el-option value="21"></el-option>
<el-option value="22"></el-option>
<el-option value="23"></el-option>
<el-option value="24"></el-option>
</el-select>
<span style="color: red; display: block" v-if="!serveiceIpIsOk && !addData.checkLoading && CIDRIndex">{{ serveiceIpMessage }}</span>
</cb-form-item>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="操作系统:" prop="system" validate="required" required-message="">
<el-select v-model="addData.system">
<el-option v-for="(item, index) in systemList" :key="index" :label="item.alias" :value="item.osName"></el-option>
</el-select>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="集群描述:">
<el-input type="textarea" :rows="5" v-model="addData.ClusterDescription"></el-input>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<cb-form-item label="ipvs支持" prop="ipvsSwitch" validate="required" required-message="ipvs">
<el-switch v-model="addData.ipvsSwitch"></el-switch>
</cb-form-item>
</el-row>
</cb-form>
</template>
<script>
import { getDescribeVersions, getTKERegions, createCluterImages, checkCIDR } from 'views/tce/services/tke.js'
import { getVpc } from 'views/tce/services/vpcs.js'
import { getSubnet } from 'views/tce/services/subnets.js'
const subnetColumns = [
{
type: 'selection',
disabled: true,
selectable: (row, index) => {
return row.podIpSwitch ? row.vmNum == 0 : true
}
},
{
label: '子网',
prop: 'subnetUuid'
},
{
label: '子网名称',
prop: 'name'
},
{
label: '可用区',
prop: 'zoneName'
}
]
export default {
components: {},
props: {
vendorId: {
type: Number,
default: -1
},
addData: {
type: Object,
default: () => {
return {}
}
},
platformObject: {
type: Object
}
},
data() {
return {
title: '',
systemList: [],
kubernetesList: [],
vpcList: [],
regionList: [],
twoMin: 0,
twoMax: 255,
subnetList: [],
loading: false,
subnetColumns,
selectionIds: [],
zoneList: [],
grTime: null,
grMessage: '校验失败',
grIsOk: true,
serveiceIpTime: null,
serveiceIpIsOk: false,
serveiceIpMessage: '校验异常',
CIDRIndex: 0,
region: ''
}
},
computed: {
maxNode() {
// (cidr IP - Service) / Pod
const { end, podNum, serviceNum } = this.addData
return (Math.pow(2, 32 - end) - serviceNum) / podNum
}
},
methods: {
clearSubnet() {
this.$refs.multipleTable.clearSelection()
},
getKubernetesList() {
this.kubernetesList = []
getDescribeVersions({
tenantUuid: this.platformObject.tceTenantUuid,
regionId: this.region
}).then(data => {
if (data.success) {
this.kubernetesList = data.data.versionInstanceSet
}
})
},
handleOne(type) {
this.title = ''
if (this.addData.one == '192') {
this.addData.two = 168
} else {
this.addData.two = 0
}
if (this.addData.oneService == '192') {
this.addData.twoService = 168
} else {
this.addData.twoService = 0
}
if ((this.addData.oneService == '10' && type == 2) || (this.addData.one == '10' && type == 1)) {
this.title = '范围0, 4, ... , 252'
} else if ((this.addData.oneService == '172' && type == 2) || (this.addData.one == '172' && type == 1)) {
this.title = '范围16, 18, ... , 31'
if (type == 1) {
this.$set(this.addData, 'two', 16)
}
if (type == 2) {
this.$set(this.addData, 'twoService', 16)
}
}
this.handleCIDR()
},
handleTwoValue() {
let value = 0
if (this.addData.one == 10) {
value = this.addData.two - (this.addData.two % 4)
this.$set(this.addData, 'two', value)
}
if (this.addData.oneService == 10) {
value = this.addData.twoService - (this.addData.twoService % 4)
this.$set(this.addData, 'twoService', value)
}
if (this.addData.one == 172 && (this.addData.two == 17 || this.addData.two < 16 || this.addData.two > 31)) {
this.$message.error('请按规则输入有效数字')
this.$nextTick(() => {
this.$set(this.addData, 'two', 16)
})
}
if (this.addData.oneService == 172 && (!this.addData.twoService || this.addData.twoService == 17 || this.addData.twoService < 16 || this.addData.twoService > 31)) {
this.$message.error('请按规则输入有效数字')
this.$nextTick(() => {
this.$set(this.addData, 'twoService', 16)
})
}
this.handleCIDR()
},
handleCIDR() {
this.grIsOk = false
this.serveiceIpIsOk = false
if (!this.addData.vpcId) {
return
}
if (this.addData.networkPlug == 'vpc') {
this.handleServiceIp()
} else {
this.handleCIDRgr()
}
},
handleCIDRgr() {
clearTimeout(this.grTime)
this.grTime = setTimeout(() => {
this.checkCIDRValue({
vpcId: this.addData.vpcId,
vendorId: this.vendorId,
CIDR: `${this.addData.one}.${this.addData.two}.0.0/${this.addData.end}`
})
}, 500)
},
handleServiceIp() {
clearTimeout(this.serveiceIpTime)
this.serveiceIpTime = setTimeout(() => {
this.checkCIDRValue({
vpcId: this.addData.vpcId,
vendorId: this.vendorId,
CIDR: `${this.addData.oneService}.${this.addData.twoService}.0.0/${this.addData.endService}`
})
}, 500)
},
checkCIDRValue(params) {
this.CIDRIndex++
const index = this.CIDRIndex
params.region = this.addData.regionId
params.tenantUuid = this.platformObject.tceTenantUuid
this.addData.checkLoading = true
checkCIDR(params).then(data => {
this.addData.checkLoading = false
if (index == this.CIDRIndex) {
if (data.success && data.data && !data.data.isConflict) {
this.grIsOk = true
this.serveiceIpIsOk = true
this.$message({
type: 'success',
message: 'CIDR校验成功'
})
} else {
this.$message({
type: 'warning',
message: 'CIDR校验失败,请重新填写'
})
this.grIsOk = false
this.serveiceIpIsOk = false
}
}
})
},
getImages() {
this.systemList = []
createCluterImages({
tenantUuid: this.platformObject.tceTenantUuid,
vendorId: this.vendorId,
regionId: this.region
}).then(data => {
if (data.success) {
this.systemList = data.data
}
})
},
getData() {
if (this.addData.checkLoading) {
this.$message({
type: 'warning',
message: '正在校验CIDR,请稍后'
})
return
}
if (this.addData.networkPlug == 'vpc' && !this.serveiceIpIsOk && this.CIDRIndex) {
this.$message({
type: 'warning',
message: 'CIDR校验失败,请重新填写'
})
return
}
if (this.addData.networkPlug == 'router' && !this.grIsOk && this.CIDRIndex) {
this.$message({
type: 'warning',
message: 'CIDR校验失败,请重新填写'
})
return
}
this.$refs.addData.validate(valid => {
if (valid) {
this.addData.kubernetesName = this.kubernetesList.find(item => item.version == this.addData.kubernetes).name
this.addData.regionName = this.regionList.find(item => item.alias == this.addData.regionId).regionName
this.addData.systemName = this.systemList.find(item => item.osName == this.addData.system).alias
this.addData.imageId = this.systemList.find(item => item.osName == this.addData.system).imageId
this.addData.vpcName = this.vpcList.find(item => item.vpcId == this.addData.vpcId).name
this.addData.vpcNumberId = this.vpcList.find(item => item.vpcId == this.addData.vpcId).id
this.addData.ClusterCIDR = this.addData.networkPlug == 'vpc' ? `${this.addData.oneService}.${this.addData.twoService}.0.0/${this.addData.endService}` : `${this.addData.one}.${this.addData.two}.0.0/${this.addData.end}`
this.$emit('addStep')
}
})
},
handleZone() {
this.handleSearchSpec()
},
//
handleSelectChange(selection) {
this.selectionIds = selection.map(item => item.id)
this.addData.selectionSubnet = selection
},
handlePodIpSwitch() {
this.clearSubnet()
this.selectionIds = []
this.addData.selectionSubnet = []
this.initClick()
},
handleNetwork() {
this.selectionIds = []
this.addData.selectionSubnet = []
},
getRegionList() {
this.regionList = []
getTKERegions({
tenantUuid: this.platformObject.tceTenantUuid,
vendorId: this.vendorId,
regionId: this.region
}).then(data => {
if (data.success) {
this.regionList = data.data.regionInstanceSet
}
})
},
handleVpc() {
this.getSubnet()
this.handleCIDR()
},
getSubnet() {
this.loading = true
getSubnet({
page: 1,
rows: 9999,
params: JSON.stringify([
{
param: {
vendorId: this.vendorId,
networkId: this.vpcList.find(item => item.vpcId == this.addData.vpcId).id,
type: 0
},
sign: 'EQ'
}
])
}).then(data => {
if (data.success) {
this.loading = false
this.subnetList = data.data.rows
this.initClick()
}
})
},
initClick() {
this.subnetList.forEach((item, index) => {
item.podIpSwitch = this.addData.podIpSwitch
})
},
getVpc() {
getVpc({
page: 1,
rows: 9999,
params: JSON.stringify([{ param: { vendorId: this.vendorId, tenantId: 0 }, sign: 'EQ' }])
}).then(data => {
if (data.success) {
this.vpcList = data.data.rows
if (this.addData.vpcId) this.getSubnet()
}
})
},
changeRegion() {
this.getRegionList()
this.getImages()
this.getKubernetesList()
}
},
created() {
this.handleOne(1)
// this.getRegionList()
this.getVpc()
// this.getImages()
// this.getKubernetesList()
}
}
</script>

View File

@ -0,0 +1,26 @@
export const instanceParams = {
InstanceCount: 1,
zoneName: '',
zoneId: '',
subnetList: [],
SubnetId: '',
hostType: '',
flavorId: '',
hostData: {},
systemDiskCategory: '',
rootSize: 50,
subentCreateType: 'Automatic',
PrivateIpAddresses: [],
SystemDisk: {
DiskType: '',
DiskSize: ''
},
DataDisks: {
DiskType: '',
DiskSize: ''
},
publicIp: {
type: '',
value: ''
}
}

View File

@ -0,0 +1,147 @@
<template>
<el-dialog title="磁盘规格" width="40%" :close-on-click-modal="false" v-if="diskDialog.visible" :visible.sync="diskDialog.visible" append-to-body>
<cb-form :model="configData" ref="diskDialog" label-width="160px" v-loading="loading">
<el-alert type="warning" :closable="false" class="m-b-sm">SSD云硬盘为数据盘时不小于100G</el-alert>
<el-row>
<el-col :span="24">
<cb-form-item label="磁盘类型:" prop="categoryId" validate="required" required-message="">
<el-radio-group v-model="configData.categoryId" @change="changeCategory()">
<el-radio-button :label="item.id" v-for="(item, index) in diskTypeList" :key="index" :disabled="!item.available">
<span v-if="!item.available"></span>
{{ item.name }}
</el-radio-button>
</el-radio-group>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<cb-form-item label="磁盘大小(GB)" prop="DiskSize" validate="required,number" required-message="">
<el-input-number step-strictly :step="configData.stepSize" :min="configData.minDiskSize" :max="configData.maxDiskSize" v-model.number="configData.DiskSize"> </el-input-number>
</cb-form-item>
</el-col>
</el-row>
</cb-form>
<div slot="footer" class="dialog-footer">
<el-button type="ghost" @click.native="diskDialog.visible = false">取消</el-button>
<el-button type="primary" @click.native="ok">确定</el-button>
</div>
</el-dialog>
</template>
<script>
import { getCategoriesByCode } from 'views/tce/services/cloud.js'
import { queryAvailable } from 'views/tce/services/cbs.js'
export default {
props: {
diskDialog: {
type: Object,
default: function () {
return {
dialog: false
}
}
},
platformObject: {
type: Object
}
},
data() {
return {
loading: true,
diskTypeList: [],
categoryMap: {},
configData: {
categoryId: '',
DiskSize: ''
}
}
},
created() {
this.getCategoryList()
},
methods: {
//
async getCategoryList() {
const { vendorId, regionId, zoneId } = this.diskDialog
const res = await queryAvailable({
vendorId,
regionId,
diskChargeType: 'POSTPAID_BY_HOUR',
zones: [zoneId],
tenantUuid: this.platformObject.tceTenantUuid
})
if (!res.success) {
this.loading = false
return
}
const map = {}
res.data.forEach(item => {
const { diskType } = item
map[diskType] = item
})
const result = await getCategoriesByCode('tce.standard.volume')
console.log(result)
this.diskTypeList = result.data.map(item => {
const { available, maxDiskSize, minDiskSize, stepSize } = map[item.code]
const obj = {
...item,
available,
maxDiskSize,
minDiskSize,
stepSize
}
this.categoryMap[item.code] = obj
this.categoryMap[item.id] = obj
return obj
})
const { DiskType, DiskSize } = this.diskDialog.data
console.log('???', DiskType, DiskSize, this.diskDialog.data)
console.log(this.diskTypeList)
if (DiskType) {
this.configData.categoryId = this.categoryMap[DiskType].id
this.configData.DiskSize = DiskSize
} else {
this.configData.DiskSize = 50
this.configData.categoryId = this.diskTypeList.find(item => item.available)?.id
if (!this.configData.categoryId) {
this.loading = false
return
}
}
this.changeCategory()
this.loading = false
},
ok() {
this.$refs.diskDialog.validate(valid => {
if (valid) {
const { name: diskCategory, code: DiskType } = this.categoryMap[this.configData.categoryId]
this.diskDialog.data = {
diskCategory,
DiskType,
DiskSize: this.configData.DiskSize
}
this.$emit('getDiskData', this.diskDialog)
}
})
},
changeCategory() {
const { maxDiskSize, minDiskSize, stepSize, code } = this.categoryMap[this.configData.categoryId]
this.configData = {
...this.configData,
maxDiskSize,
minDiskSize,
stepSize
}
// SSD100
const { type } = this.diskDialog
if (code === 'CLOUD_SSD' && type === 'dataDisk') {
this.configData.minDiskSize = 100
if (this.configData.DiskSize < 100) this.configData.DiskSize = 100
} else {
this.configData.minDiskSize = minDiskSize
}
}
}
}
</script>

View File

@ -0,0 +1,241 @@
<template>
<cb-form :model="addData" ref="addData" label-width="160px">
<h1>已选配置</h1>
<el-row>
<el-col :span="12">
<cb-form-item label="集群名:" prop="name">
{{ addData.name }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="Kubernetes版本" prop="name">
{{ addData.kubernetes }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="所在地域:" prop="name">
{{ addData.regionName }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="容器网络:" prop="name">
{{ addData.ClusterCIDR }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="操作系统:" prop="name">
{{ addData.systemName }}
</cb-form-item>
</el-col>
</el-row>
<el-divider></el-divider>
<el-row>
<el-col :span="24">
<cb-form-item label="数据盘挂载:" prop="system" validate="required" required-message="">
<el-checkbox v-model="addData.storeDiskSwitch"></el-checkbox>
<div class="tip">将容器和镜像存储在数据盘将自动格式化数据盘成ext4且自动挂载到您指定的挂载点并将容器存储到挂载点的docker目录仅对购买数据盘的节点生效</div>
</cb-form-item>
</el-col>
<el-col :span="24" v-if="addData.storeDiskSwitch">
<cb-form-item label=" " prop="mountPath" validate="required" required-message="">
<el-select v-model="addData.mountPath">
<el-option v-for="(item, index) in mountList" :key="index" :label="item.name" :value="item.value"></el-option>
</el-select>
<div class="tip">数据盘挂载点{{ addData.mountPath }}, 容器目录{{ addData.mountPath }}</div>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<cb-form-item label="安全组:" prop="group" validate="required" required-message="">
<el-select v-model="addData.group">
<el-option v-for="(item, index) in groupList" :key="index" :label="item.name" :value="item.groupUuid"></el-option>
</el-select>
</cb-form-item>
</el-row>
<el-row>
<cb-form-item label="登录方式:" prop="loginWay" validate="required" required-message="">
<el-radio-group v-model="addData.loginWay">
<el-radio-button label="password">设置密码</el-radio-button>
<el-radio-button label="secretKey">立即关联密钥</el-radio-button>
</el-radio-group>
</cb-form-item>
</el-row>
<el-row v-if="addData.loginWay != 'auto'">
<cb-form-item label="用户名:">{{ username }}</cb-form-item>
</el-row>
<el-row v-if="addData.loginWay == 'password'" key="password">
<cb-form-item label="密码:" prop="password" validate="required,tceClusterPassword" required-message="">
<el-input type="password" v-model="addData.password"></el-input>
</cb-form-item>
</el-row>
<el-row v-if="addData.loginWay == 'password'" key="password2">
<cb-form-item label="确认密码:" prop="password2" validate="required,tceClusterPassword" required-message="">
<el-input type="password" v-model="addData.password2"></el-input>
</cb-form-item>
</el-row>
<el-row v-if="addData.loginWay == 'secretKey'" key="ssh">
<cb-form-item label="SSH密钥" prop="ssh" validate="required" required-message="SSH">
<el-select v-model="addData.ssh">
<el-option v-for="(item, index) in keyList" :key="index" :label="item.name" :value="item.id"></el-option>
</el-select>
</cb-form-item>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="安全加固:">
<el-checkbox v-model="addData.SecurityService"></el-checkbox>
<div class="tip">安装组件免费开通DDoS防护WAF和云镜主机防护</div>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="云监控:">
<el-checkbox v-model="addData.MonitorService"></el-checkbox>
<div class="tip">免费开通云产品监控分析和实施告警安装组件获取主机监控指标</div>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="自定义数据:">
<el-input type="textarea" :rows="5" v-model="addData.selfDefining"></el-input>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="封锁cordon">
<el-checkbox v-model="addData.cordon"></el-checkbox>
</cb-form-item>
</el-col>
</el-row>
</cb-form>
</template>
<script>
import { getGroup } from 'views/tce/services/sgroups.js'
import { getKey } from 'views/tce/services/keypairs.js'
import { computed } from 'vue'
const mountList = [
{ name: '/var/lib/docker', value: '/var/lib/docker' },
{ name: '/data', value: '/data' },
{ name: '/opt', value: '/opt' }
]
export default {
components: {},
props: {
vendorId: {
type: Number,
default: -1
},
activeStep: {
type: Number,
default: -1
},
addData: {
type: Object,
default: () => {
return {}
}
}
},
data() {
return {
mountList,
groupList: [],
keyList: []
}
},
methods: {
getData() {
//
this.$refs.addData.validate(valid => {
if (valid) {
if (this.addData.loginWay == 'password' && this.addData.password !== this.addData.password2) return this.$message.error('两次密码输入不一致')
if (this.addData.loginWay == 'secretKey') {
this.addData.sshId = this.keyList.find(item => item.id == this.addData.ssh).fingerprint
}
this.$emit('addStep')
}
})
},
getKey() {
this.keyList = []
getKey({
page: 1,
rows: 9999,
params: JSON.stringify([
{
param: {
vendorId: this.vendorId
},
sign: 'EQ'
}
])
}).then(data => {
if (data.success) {
this.keyList = data.data.rows
}
})
},
getGroup() {
this.groupList = []
getGroup({
page: 1,
rows: 9999,
params: JSON.stringify([
{
param: {
vendorId: this.vendorId
},
sign: 'EQ'
}
])
}).then(data => {
if (data.success) {
this.groupList = data.data.rows
}
})
}
},
setup(props) {
const username = computed(() => {
const { system } = props.addData
if (system.includes('ubuntu')) return 'ubuntu'
return 'root'
})
return {
username
}
},
watch: {
activeStep: {
handler(val) {
if (val == 2) {
this.getGroup()
this.getKey()
}
},
immediate: true,
deep: true
}
}
}
</script>
<style scoped>
.hostClass {
background-color: rgb(242, 242, 242) !important;
padding-top: 10px;
margin-bottom: 20px;
}
</style>

View File

@ -0,0 +1,165 @@
<template>
<div>
<el-dialog title="主机规格" width="50%" :close-on-click-modal="false" v-if="dialog.visible" :visible.sync="dialog.visible">
<el-alert v-if="dialog.type === 'master'" type="warning" :closable="false" class="m-b-sm">master44GB</el-alert>
<table-search :configs="searchConfigs" :on-search="handleSearch" class="m-b-sm"></table-search>
<cb-table :data="list" :get-list="getList" :params="params" :total="total" highlight-current-row tooltip-effect="dark" stripe border fit>
<template slot="empty"> 请选择付费方式及区域 </template>
<el-table-column label="名称">
<template slot-scope="scope">
<el-radio :label="scope.row.flavorUuid" v-model="flavorId" @change="handleHost(scope.row)" :disabled="getSkuDisabled(scope.row)">
{{ scope.row.name }}
</el-radio>
</template>
</el-table-column>
<el-table-column label="规格编码" prop="flavorUuid"> </el-table-column>
<el-table-column label="cpu" prop="cpu"> </el-table-column>
<el-table-column label="内存(GB)" prop="memory"> </el-table-column>
</cb-table>
<div slot="footer" class="dialog-footer">
<el-button type="ghost" @click.native="dialog.visible = false">取消</el-button>
<el-button type="primary" @click.native="ok">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getFlavor, getFlavorType } from 'views/tce/services/flavors.js'
import { getDictChildrenTree } from 'views/tce/services/dictionaries.js'
const searchConfigs = [
{
label: '实例族',
type: 'Select',
value: 'className',
data: []
},
{
label: '实例类型',
type: 'Select',
value: 'familyName',
data: []
},
{
type: 'Input',
label: '名称',
value: 'name'
},
{
type: 'Const',
value: 'regionId',
initValue: ''
},
{
type: 'Const',
value: 'zone',
initValue: ''
}
]
export default {
props: {
dialog: {
type: Object
}
},
data() {
return {
searchConfigs,
flavorId: '',
params: {
page: 1,
rows: 10
},
total: 0,
list: [],
hostData: {},
typeData: {},
dictList: []
}
},
created() {
this.getDict()
this.flavorId = this.dialog.data.flavorUuid
this.handleHost(this.dialog.data)
const { regionId, zoneId } = this.dialog
this.searchConfigs[3].initValue = regionId
this.searchConfigs[4].initValue = zoneId
this.searchConfigs[0].onChange = this.changeType
},
methods: {
// master4c4g
getSkuDisabled(record) {
const { cpu, memory } = record
return this.dialog.type === 'master' && (cpu < 4 || memory < 4)
},
changeType(val, listQuery) {
if (listQuery.familyName) listQuery.familyName = ''
const map = {}
const children = this.dictList.find(item => item.value == val)?.children || []
children.forEach(item => {
map[item.value] = item.name
})
this.searchConfigs[1].data = this.typeData[val].map(item => {
return {
id: item,
name: map[item]
}
})
},
async getType(dict) {
const typeMap = {}
dict.forEach(item => {
typeMap[item.value] = item.name
})
const { regionId, zoneId } = this.dialog
const res = await getFlavorType({
region: regionId,
zone: zoneId
})
if (res.success) {
this.typeData = res.data
this.searchConfigs[0].data = Object.keys(res.data).map(item => {
return {
id: item,
name: typeMap[item]
}
})
}
},
async getDict() {
const res = await getDictChildrenTree({ value: 'specFilter' })
this.dictList = res.data
this.getType(res.data)
},
handleHost(row) {
this.hostData = row
},
//
getList() {
getFlavor(this.params).then(data => {
if (data.success) {
this.list = data.data.rows
this.total = data.data.total
}
})
},
handleSearch(params) {
this.params.page = 1
this.params.params = params
this.getList()
},
ok() {
if (this.flavorId) {
this.$emit('getHostData', this.hostData)
this.dialog.visible = false
} else {
this.$message({
type: 'warning',
message: '您还未选择机型'
})
}
}
}
}
</script>

View File

@ -0,0 +1,340 @@
<template>
<div class="wrapper-container">
<el-card>
<el-steps :active="activeStep" finish-status="success" style="margin: 0 auto 20px; width: 70%">
<el-step title="集群信息"></el-step>
<el-step title="选择机型"></el-step>
<el-step title="云主机配置"></el-step>
<el-step title="信息确认"></el-step>
</el-steps>
<el-col :span="24" class="panel-body" v-show="activeStep == 0">
<cluster-item ref="clusterItem" :addData="addData" :vendor-id="vendorId" :platform-object="platformObject" @addStep="addStep"></cluster-item>
</el-col>
<el-col :span="24" class="panel-body" v-show="activeStep == 1">
<type-item ref="typeItem" :addData="addData" :vendor-id="vendorId" :activeStep="activeStep" :platform-object="platformObject" @addStep="addStep"></type-item>
</el-col>
<el-col :span="24" class="panel-body" v-show="activeStep == 2">
<host-config-item ref="hostConfigItem" :addData="addData" :vendor-id="vendorId" :activeStep="activeStep" :platform-object="platformObject" @addStep="addStep"></host-config-item>
</el-col>
<el-col :span="24" class="panel-body" v-show="activeStep == 3">
<info-item ref="infoItem" :addData="addData" :vendor-id="vendorId" :activeStep="activeStep"></info-item>
</el-col>
<el-col :span="24" class="m-b-lg">
<el-col :span="6" :offset="5" v-if="activeStep == 0">
<el-button type="ghost" style="width: 100%" @click="goBack"></el-button>
</el-col>
<el-col :span="6" :offset="5" v-if="activeStep != 0">
<el-button type="primary" style="width: 100%" @click="addLastStep()"></el-button>
</el-col>
<el-col :span="6" :offset="1" v-if="activeStep == 3">
<el-button type="primary" style="width: 100%" @click="ok" :loading="loading">确定</el-button>
</el-col>
<el-col :span="6" :offset="1" v-if="activeStep != 3">
<el-button type="primary" style="width: 100%" :disabled="nextStepDisabled()" @click="addNextStep()"></el-button>
</el-col>
<el-button type="ghost" v-if="activeStep == 1 && masterNumber()" style="margin-left: 20px">master3</el-button>
<el-button type="ghost" v-if="activeStep == 0 && addData.checkLoading" style="margin-left: 20px">CIDR,</el-button>
</el-col>
</el-card>
</div>
</template>
<script>
import clusterItem from './clusterItem.vue'
import typeItem from './typeItem.vue'
import infoItem from './infoItem.vue'
import hostConfigItem from './hostConfigItem.vue'
import { createCluter } from 'views/tce/services/tke.js'
import { instanceParams } from './data'
export default {
props: {
platformObject: {
type: Object
}
},
components: {
clusterItem,
typeItem,
infoItem,
hostConfigItem
},
data() {
return {
loading: false,
activeStep: 0,
vendorId: parseInt(this.$route.query.vendorId),
addData: {
vendorId: parseInt(this.$route.query.vendorId),
selectionSubnet: [],
zoneList: [],
imageId: -1,
checkLoading: false,
ClusterDescription: '',
hostDialog: false,
name: '',
regionId: '',
kubernetes: '',
networkPlug: 'router',
one: 10,
two: 0,
end: 16,
oneService: 10,
twoService: 0,
endService: 16,
podNum: 64,
serviceNum: 1024,
podIpSwitch: false,
vpcNumberId: -1,
system: '',
ipvsSwitch: false,
zoneId: '',
kubernetesName: '',
regionName: '',
systemName: '',
deploy: 'deployNow',
ClusterCIDR: '',
vpcName: '',
masterList: [{ ...instanceParams }],
nodeList: [{ ...instanceParams }],
RunInstancesForNode: [
{
NodeRole: 'MASTER_ETCD',
RunInstancesPara: []
},
{
NodeRole: 'WORKER',
RunInstancesPara: []
}
],
storeDiskSwitch: true,
mountPath: '/var/lib/docker',
group: '',
loginWay: 'password',
password: '',
password2: '',
ssh: '',
sshId: '',
SecurityService: true,
MonitorService: true,
selfDefining: '',
cordon: false
}
}
},
computed: {},
methods: {
masterNumber() {
let len = 0
this.addData.masterList.forEach(item => {
len += item.InstanceCount
})
return len < 3
},
nextStepDisabled() {
if (this.activeStep == 1) {
return this.masterNumber()
} else if (this.activeStep == 0) {
return this.addData.checkLoading
} else {
return false
}
},
addStep() {
this.activeStep++
},
addNextStep() {
switch (this.activeStep) {
case 0:
this.$refs.clusterItem.getData()
break
case 1:
this.$refs.typeItem.getData()
break
case 2:
this.$refs.hostConfigItem.getData()
break
case 3:
break
default:
}
},
addLastStep() {
this.activeStep--
},
goBack() {
sessionStorage.setItem('platformId', this.vendorId)
this.$router.back(-1)
sessionStorage.setItem('needRefresh', 1)
},
handleInsParams(nodes, LoginSettings) {
const { vpcId, SecurityService, MonitorService } = this.addData
const insParams = []
nodes.forEach(node => {
const {
hostData,
DataDisks,
SystemDisk: { diskCategory, ...systemOthers },
SubnetId,
PrivateIpAddresses,
subentCreateType,
InstanceCount
} = node
const VirtualPrivateCloud = {
VpcId: vpcId,
SubnetId
}
if (subentCreateType === 'Manual') {
VirtualPrivateCloud.PrivateIpAddresses = PrivateIpAddresses
}
const temp = {
Version: '2017-03-12', //
InstanceChargeType: 'POSTPAID_BY_HOUR',
InstanceChargePrepaid: {
Period: 1,
RenewFlag: 'NOTIFY_AND_MANUAL_RENEW',
TimeUnit: 'MONTH'
},
Placement: {
Zone: hostData.zone,
ProjectId: 0
},
InstanceType: hostData.flavorUuid,
ImageId: this.addData.imageId,
SystemDisk: systemOthers,
VirtualPrivateCloud,
InternetAccessible: {
//
InternetChargeType: 'TRAFFIC_POSTPAID_BY_HOUR',
InternetMaxBandwidthOut: 0,
PublicIpAssigned: false
},
InstanceCount,
InstanceName: '',
LoginSettings,
SecurityGroupIds: [this.addData.group],
EnhancedService: {
SecurityService: {
Enabled: SecurityService
},
MonitorService: {
Enabled: MonitorService
}
},
PurchaseSource: 'ocker_dashboard',
UserData: ''
}
if (DataDisks.DiskType) {
const { diskCategory, ...others } = DataDisks
temp.DataDisks = [others]
}
insParams.push(JSON.stringify(temp))
})
return insParams
},
ok() {
let ClusterCIDRSettings = {}
switch (this.addData.networkPlug) {
case 'router':
ClusterCIDRSettings = {
ClusterCIDR: this.addData.ClusterCIDR,
IgnoreClusterCIDRConflict: false, // CIDR,
MaxNodePodNum: this.addData.podNum,
MaxClusterServiceNum: this.addData.serviceNum
}
break
case 'vpc':
ClusterCIDRSettings = {
ServiceCIDR: this.addData.ClusterCIDR,
EniSubnetIds: this.addData.selectionSubnet.map(item => item.subnetUuid),
MaxNodePodNum: this.addData.podNum,
MaxClusterServiceNum: this.addData.serviceNum
}
break
default:
}
let LoginSettings = {}
switch (this.addData.loginWay) {
case 'password':
LoginSettings = {
Password: this.addData.password
}
break
case 'secretKey':
LoginSettings = {
KeyIds: [this.addData.sshId]
}
break
default:
}
const RunInstancesParaMater = this.handleInsParams(this.addData.masterList, LoginSettings)
let RunInstancesParaNode = []
if (this.addData.deploy == 'deployNow') {
RunInstancesParaNode = this.handleInsParams(this.addData.nodeList, LoginSettings)
}
this.addData.RunInstancesForNode = [
{
NodeRole: 'MASTER_ETCD',
RunInstancesPara: RunInstancesParaMater
}
]
if (this.addData.deploy == 'deployNow') {
this.addData.RunInstancesForNode.push({
NodeRole: 'WORKER',
RunInstancesPara: RunInstancesParaNode
})
}
const { mountPath, storeDiskSwitch } = this.addData
const mount = storeDiskSwitch ? mountPath : ''
const params = {
vendorId: this.vendorId,
region: this.addData.regionId,
ClusterType: 'INDEPENDENT_CLUSTER',
ClusterBasicSettings: {
ClusterOs: this.addData.system,
ClusterVersion: this.addData.kubernetes,
ClusterName: this.addData.name,
ClusterDescription: this.addData.ClusterDescription,
VpcId: this.addData.vpcId,
ProjectId: 0,
TagSpecification: [
{
ResourceType: 'cluster',
Tags: []
}
]
},
ClusterCIDRSettings: ClusterCIDRSettings,
ClusterAdvancedSettings: {
AsEnabled: false,
IPVS: this.addData.ipvsSwitch,
NetworkType: this.addData.networkPlug == 'router' ? 'GR' : 'VPC-CNI'
},
InstanceAdvancedSettings: {
MountTarget: mount,
DockerGraphPath: mount,
Unschedulable: this.addData.cordon ? 1 : 0,
UserScript: btoa(this.addData.selfDefining)
},
Version: '2018-05-25', // ,
RunInstancesForNode: this.addData.RunInstancesForNode
}
this.loading = true
createCluter(params)
.then(data => {
if (data.success) {
this.$message({
type: 'success',
message: data.message
})
this.goBack()
}
})
.finally(() => {
this.loading = false
})
}
}
}
</script>

View File

@ -0,0 +1,94 @@
<template>
<cb-form :model="addData" ref="addData" label-width="160px">
<h1>已选配置</h1>
<el-row>
<el-col :span="12">
<cb-form-item label="集群名:" validate="required" required-message="">
{{ addData.name }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="Kubernetes版本">
{{ addData.kubernetes }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="所在地域:">
{{ addData.regionName }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="容器网络:">
{{ addData.ClusterCIDR }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="计费模式:"> 按量计费 </cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="操作系统:">
{{ addData.systemName }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="Master&Etcd机型">
<div v-for="(master, index) in addData.masterList" :key="index">
<p>可用区域{{ master.zoneName }}</p>
<p>机型{{ `${master.hostData.flavorUuid}(${master.hostData.cpu}${master.hostData.memory}GB)` }}</p>
<p>系统盘{{ master.SystemDisk.DiskSize ? `${master.SystemDisk.diskCategory} ${master.SystemDisk.DiskSize}GB` : '暂不购买' }}</p>
<p>数据盘 {{ master.DataDisks.DiskSize ? `${master.DataDisks.diskCategory} ${master.DataDisks.DiskSize}GB` : '暂不购买' }}</p>
<p>公网带宽 {{ master.publicIp.switch ? `${master.publicIp.name} ${master.publicIp.value}Mbps` : '不访问公网' }}</p>
<p>数量 {{ master.InstanceCount }}</p>
</div>
</cb-form-item>
</el-col>
</el-row>
<el-row v-if="addData.deploy == 'deployNow'">
<el-col :span="12">
<cb-form-item label="Node机型">
<div v-for="(master, index) in addData.nodeList" :key="index">
<p>可用区域{{ master.zoneName }}</p>
<p>机型{{ `${master.hostData.flavorUuid}(${master.hostData.cpu}${master.hostData.memory}GB)` }}</p>
<p>系统盘{{ master.SystemDisk.DiskSize ? `${master.SystemDisk.diskCategory} ${master.SystemDisk.DiskSize}GB` : '暂不购买' }}</p>
<p>数据盘 {{ master.DataDisks.DiskSize ? `${master.DataDisks.diskCategory} ${master.DataDisks.DiskSize}GB` : '暂不购买' }}</p>
<p>公网带宽 {{ master.publicIp.switch ? `${master.publicIp.name} ${master.publicIp.value}Mbps` : '不访问公网' }}</p>
<p>数量 {{ master.InstanceCount }}</p>
</div>
</cb-form-item>
</el-col>
</el-row>
</cb-form>
</template>
<script>
export default {
name: '',
props: {
vendorId: {
type: Number,
default: -1
},
addData: {
type: Object,
default: () => {
return {}
}
}
},
data() {
return {}
}
}
</script>

View File

@ -0,0 +1,71 @@
<template>
<div>
<el-dialog title="公网带宽" width="40%" :close-on-click-modal="false" v-if="publicIpData.dialog" :visible.sync="publicIpData.dialog">
<cb-form :model="publicIpData.data" ref="publicIpData" label-width="160px">
<el-row>
<el-col :span="24">
<cb-form-item label="" prop="switch">
<el-checkbox v-model="publicIpData.data.switch">IP</el-checkbox>
</cb-form-item>
</el-col>
</el-row>
<el-row v-if="publicIpData.data.switch">
<el-col :span="12">
<cb-form-item label="计费方式:" prop="type" validate="required" required-message="">
<el-select v-model="publicIpData.data.type" placeholder="请选择">
<el-option v-for="(item, index) in ipList" :key="index" :label="item.name" :value="item.value"> </el-option>
</el-select>
</cb-form-item>
</el-col>
<el-col :span="12">
<cb-form-item label="" prop="type" label-width="0px"> <el-input-number :min="1" :max="100" v-model.number="publicIpData.data.value"></el-input-number>Mbps </cb-form-item>
</el-col>
</el-row>
</cb-form>
<div slot="footer" class="dialog-footer">
<el-button type="ghost" @click.native="publicIpData.dialog = false">取消</el-button>
<el-button type="primary" @click.native="ok">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
/* global $ */
export default {
props: {
publicIpData: {
type: Object,
default: function () {
return {
data: {
name: '',
vendorId: -1
},
dialog: false
}
}
}
},
data() {
return {
ipList: [
{ name: '按带宽计费', value: 'bandwidth' },
{ name: '按流量计费', value: 'flow' }
]
}
},
methods: {
ok() {
this.$refs.publicIpData.validate(valid => {
if (valid) {
this.publicIpData.data.name = this.ipList.find(item => item.value == this.publicIpData.data.type).name
this.$emit('getPublicIpData', this.publicIpData)
}
})
}
},
created() {},
watch: {}
}
</script>

View File

@ -0,0 +1,320 @@
<template>
<cb-form :model="addData" ref="addData" label-width="160px">
<h1>已选配置</h1>
<el-row>
<el-col :span="12">
<cb-form-item label="集群名:" prop="name">
{{ addData.name }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="Kubernetes版本" prop="name">
{{ addData.kubernetes }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="所在地域:" prop="name">
{{ addData.regionName }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="容器网络:" prop="name">
{{ addData.ClusterCIDR }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="操作系统:" prop="name">
{{ addData.systemName }}
</cb-form-item>
</el-col>
</el-row>
<el-divider></el-divider>
<el-row>
<el-col :span="12">
<cb-form-item label="Master"> 独立部署 </cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="Node" prop="deploy">
<el-radio-group v-model="addData.deploy">
<el-radio-button label="deployNow">立即部署</el-radio-button>
<el-radio-button label="deployLater">暂不部署</el-radio-button>
</el-radio-group>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="计费模式:"> 按量计费 </cb-form-item>
</el-col>
</el-row>
<InstanceItem label="Master&Etcd机型" :nodeList="addData.masterList" :addData="addData" :zoneList="zoneList" :platform-object="platformObject"></InstanceItem>
<InstanceItem type="node" v-if="addData.deploy == 'deployNow'" label="Node机型" :nodeList="addData.nodeList" :addData="addData" :zoneList="zoneList" :platform-object="platformObject"></InstanceItem>
<el-row> </el-row>
<hostDialog :addData="addData" v-if="addData.hostDialog" :zone="zone" :vendorId="vendorId" :flavorId="flavorId" :hostIndex="hostIndex" @getHostData="getHostData"></hostDialog>
<diskDialog :diskDialog="diskDialog" v-if="diskDialog.dialog" @getDiskData="getDiskData"></diskDialog>
<publicIpDialog :publicIpData="publicIpData" v-if="publicIpData.dialog" @getPublicIpData="getPublicIpData"></publicIpDialog>
</cb-form>
</template>
<script>
import { getTKERegions } from 'views/tce/services/tke.js'
import { getZone } from 'views/tce/services/regions.js'
import { getVpc } from 'views/tce/services/vpcs.js'
import { getSubnet } from 'views/tce/services/subnets.js'
import { getFlavor } from 'views/tce/services/flavors.js'
import hostDialog from './hostDialog.vue'
import diskDialog from './diskDialog.vue'
import publicIpDialog from './publicIpDialog.vue'
import InstanceItem from './InstanceItem.vue'
export default {
components: { hostDialog, diskDialog, publicIpDialog, InstanceItem },
props: {
vendorId: {
type: Number,
default: -1
},
activeStep: {
type: Number,
default: -1
},
addData: {
type: Object,
default: () => {
return {}
}
},
platformObject: {
type: Object
}
},
data() {
return {
hostType: '',
diskType: '',
zoneList: [],
zone: '',
flavorId: '',
hostIndex: '',
publicIpData: {
hostType: '',
dialog: false,
index: -1,
data: {
switch: false
}
},
diskDialog: {
dialog: false,
hostType: '',
type: '',
index: -1,
data: {
type: '',
value: '',
diskName: ''
}
}
}
},
methods: {
getData() {
//
this.$refs.addData.validate(valid => {
if (valid) {
//
const { masterList, nodeList, deploy } = this.addData
let nodeFlag = true
const nodes = [...masterList]
if (deploy === 'deployNow') nodes.push(...nodeList)
for (const a of nodes) {
const { hostData, InstanceCount, subentCreateType, PrivateIpAddresses } = a
if (!hostData.flavorUuid) {
this.$message({
type: 'warning',
message: '请选择机型信息'
})
nodeFlag = false
break
}
if (subentCreateType === 'Manual' && InstanceCount !== PrivateIpAddresses.length) {
this.$message({
type: 'warning',
message: '指定IP与申请数量不一致请检查'
})
nodeFlag = false
break
}
}
if (!nodeFlag) return
// master
let len = 0
this.addData.masterList.forEach(item => {
len += item.InstanceCount
})
if (len < 3) {
this.$message({
type: 'warning',
message: 'master节点最少选择3台'
})
return false
}
this.$emit('addStep')
}
})
},
getPublicIpData(data) {
if (data.hostType == 'master') {
this.addData.matserList[data.index].publicIp = data.data
}
if (data.hostType == 'node') {
this.addData.nodeList[data.index].publicIp = data.data
}
this.publicIpData.dialog = false
},
openIpDialog(index, hostType) {
this.publicIpData = {
dialog: true,
index: index,
hostType: hostType,
data: {
switch: false,
type: this.addData.matserList[index].publicIp.type,
value: this.addData.matserList[index].publicIp.value
}
}
},
canceldataDiskData(type, cate, index) {
this.addData[type][index][cate] = {
type: '',
value: ''
}
},
getDiskData(data) {
switch (data.type) {
case 'systemDisk':
if (data.hostType == 'master') {
this.addData.matserList[data.index].sysDiskData = data.data
}
if (data.hostType == 'node') {
this.addData.nodeList[data.index].sysDiskData = data.data
}
break
case 'dataDisk':
if (data.hostType == 'master') {
this.addData.matserList[data.index].dataDiskData = data.data
}
if (data.hostType == 'node') {
this.addData.nodeList[data.index].dataDiskData = data.data
}
break
default:
}
this.diskDialog.dialog = false
},
openDiskDialog(type, data, index, hostType) {
this.diskType = type
this.diskDialog = {
dialog: true,
type: type,
hostType: hostType,
index: index,
data: {
type: data.type,
value: data.value,
diskName: ''
}
}
},
getHostData(data, index) {
if (this.hostType == 'master') {
this.addData.matserList[index].hostData = data
this.addData.matserList[index].sysDiskData = {
type: 'CLOUD_SSD',
value: 50,
diskName: 'SSD云硬盘'
}
}
if (this.hostType == 'node') {
this.addData.nodeList[index].hostData = data
this.addData.nodeList[index].sysDiskData = {
type: 'CLOUD_SSD',
value: 50,
diskName: 'SSD云硬盘'
}
}
},
openHostDialog(index, hostType) {
this.zone = this.addData.matserList[index].zoneId
this.flavorId = ''
this.hostIndex = index
this.addData.hostDialog = true
this.hostType = hostType
},
//
getZoomData() {
getZone({ vendorId: this.vendorId, regionId: this.addData.regionId }).then(data => {
if (data.success) {
this.zoneList = data.data
this.addData.zoneList = data.data
}
})
},
//
handleSearchSpec() {
this.params.page = 1
this.params.params = this.handleSearchParam({
vendorId: this.vendorId,
instanceChargeType: 'POSTPAID_BY_HOUR',
zone: this.addData.zoneId,
status: 'SELL',
'name:LK': this.searchData.name
})
this.getSpecList()
},
//
getSpecList() {
getFlavor(this.params).then(data => {
if (data.success) {
this.instanceTypeData = data.data.rows
this.total1 = data.data.total
}
})
},
handleSelectChange(selection) {
this.selectionIds = selection.map(item => item.id)
this.selectionSubnet = selection
}
},
created() {},
watch: {
activeStep: {
handler(val) {
if (val == 1) {
this.getZoomData()
}
},
immediate: true,
deep: true
}
}
}
</script>
<style scoped>
.hostClass {
background-color: rgb(242, 242, 242) !important;
padding-top: 10px;
margin-bottom: 20px;
}
</style>

View File

@ -0,0 +1,119 @@
<template>
<div>
<el-dialog title="删除集群" width="40%" :close-on-click-modal="false" v-if="data.dialog" :visible.sync="data.dialog">
<div v-loading="loading">
<cb-advance-table :showTools="false" :title="`该集群下拥有${total}个节点`" :data="instanceList" :columns="instanceColumns" :get-list="getInstance" :loading="loading" :height="200">
<div slot="pagination"></div>
<template #status="val">
<cb-status-icon :type="val | tceInstanceColor" v-if="val != 'STOPPED'">{{ val | tceInstanceText }}</cb-status-icon>
</template>
</cb-advance-table>
</div>
<div style="padding-top: 20px; padding-ledt: 10px">
<el-checkbox disabled checked>销毁按量计费的节点销毁后不可恢复请谨慎操作并提前备份好数据</el-checkbox>
</div>
<div slot="footer" class="dialog-footer">
<el-button type="ghost" @click.native="data.dialog = false">取消</el-button>
<el-button type="primary" @click.native="ok">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
/* global $ */
import { getInstanceList, deleteCluster } from 'views/tce/services/tke.js'
const instanceColumns = [
{
label: '节点ID',
prop: 'uuid'
},
{
label: '状态',
prop: 'status',
scopedSlots: { customRender: 'status' }
}
]
export default {
props: {
clusterInfo: {
type: Object,
default: function () {
return {
data: {},
dialog: false
}
}
},
data: {
type: Object,
default: function () {
return {
data: {
name: '',
vendorId: -1
},
dialog: false
}
}
},
vendorId: {
type: Number,
default: 0
}
},
data() {
return {
total: 0,
instanceColumns,
instanceList: [],
loading: true
}
},
methods: {
ok() {
const param = {
id: this.data.data.id,
mode: 'terminate'
}
deleteCluster(param, param.mode).then(data => {
if (data.success) {
this.$message({
type: 'success',
message: data.message
})
this.$emit('back')
this.data.dialog = false
}
})
},
getInstance() {
this.loading = true
const params = {
page: 1,
rows: 9999999,
params: JSON.stringify([{ param: { vendorId: this.vendorId, clusterId: this.clusterInfo.clusterId }, sign: 'EQ' }])
}
getInstanceList(params).then(data => {
if (data.success) {
this.loading = false
this.instanceList = data.data.rows
this.total = data.data.total
}
})
}
},
created() {},
watch: {
'data.dialog': {
handler(newVal, oldVal) {
if (newVal) {
this.getInstance()
}
},
deep: true,
immediate: true
}
}
}
</script>

View File

@ -0,0 +1,107 @@
<template>
<div class="detail">
<cb-detail class="common" :title="detail.name" @goBack="goBack">
<div slot="custom_content">
<el-tabs ref="tab" tab-position="top" type="border-card" v-model="active">
<el-tab-pane label="基本信息" name="detail">
<el-form label-width="140px" label-position="left">
<el-row>
<el-col :span="12"
><el-form-item label="集群名称">{{ detail.name }}</el-form-item></el-col
>
<el-col :span="12"
><el-form-item label="操作系统">{{ detail.clusterOs }}</el-form-item></el-col
>
<el-col :span="12"
><el-form-item label="集群ID">{{ detail.clusterId }}</el-form-item></el-col
>
<el-col :span="12"
><el-form-item label="状态">{{ detail.clusterStatus | tceClusterText }}</el-form-item></el-col
>
<el-col :span="12"
><el-form-item label="k8s版本">{{ detail.clusterVersion }}</el-form-item></el-col
>
<el-col :span="12"
><el-form-item label="节点数量">{{ detail.clusterNodeNum }}</el-form-item></el-col
>
<el-col :span="12"
><el-form-item label="所在地域">{{ detail.regionId }}</el-form-item></el-col
>
<el-col :span="12"
><el-form-item label="所属租户">{{ detail.tenantName }}</el-form-item></el-col
>
<el-col :span="12"
><el-form-item label="节点网络">{{ clusterNetWorkSetting.vpcId }}</el-form-item></el-col
>
<el-col :span="12"
><el-form-item label="容器网络">{{ clusterNetWorkSetting.clusterCIDR }}</el-form-item></el-col
>
<el-col :span="12"
><el-form-item label="创建时间">{{ detail.gmtCreate }}</el-form-item></el-col
>
<el-col :span="12"
><el-form-item label="描述">{{ detail.clusterDescription }}</el-form-item></el-col
>
</el-row>
</el-form>
</el-tab-pane>
<el-tab-pane label="节点管理" name="instance">
<instance :detail="detail"></instance>
</el-tab-pane>
<el-tab-pane label="伸缩组" name="2">
<flexible :clusterInfo="detail" :vendor-id="detail.vendorId"></flexible>
</el-tab-pane>
<el-tab-pane label="地域列表" name="3">
<region :detail="detail" :platform-object="platformObject"></region>
</el-tab-pane>
<el-tab-pane label="密钥信息" name="4">
<key :detail="detail"></key>
</el-tab-pane>
</el-tabs>
</div>
</cb-detail>
</div>
</template>
<script>
import instance from './instance/index.vue'
import key from './secrect/index'
import flexible from './flexible/index'
import region from './region/index.vue'
export default {
components: { instance, key, flexible, region },
props: {
detail: {
type: Object
},
platformObject: {
type: Object
}
},
data() {
return {
active: 'detail',
clusterNetWorkSetting: {}
}
},
created() {
sessionStorage.setItem('containerClusterInfo', JSON.stringify(this.detail))
this.clusterNetWorkSetting = this.detail.clusterNetWorkSetting ? JSON.parse(this.detail.clusterNetWorkSetting) : {}
},
methods: {
goBack() {
this.$emit('back')
}
}
}
</script>
<style scoped lang="scss">
.bill-table ::v-deep .search {
display: none !important;
}
.order-table {
::v-deep .pagination-container {
display: none;
}
}
</style>

View File

@ -0,0 +1,113 @@
<template>
<div>
<el-dialog title="新增" width="70%" :close-on-click-modal="false" v-if="addData.dialog" :visible.sync="addData.dialog" append-to-body>
<el-steps :active="active" finish-status="success">
<el-step title="启动配置"></el-step>
<el-step title="伸缩组配置"></el-step>
</el-steps>
<addConfig ref="config" v-show="active == 0" :vendorId="vendorId" :regionId="addData.data.clusterInfo.regionId" :platform-object="platformObject"></addConfig>
<addAs ref="as" v-show="active == 1" :vendorId="vendorId" :clusterInfo="clusterInfo"></addAs>
<div slot="footer" class="dialog-footer">
<el-button type="ghost" @click.native="addData.dialog = false">取消</el-button>
<el-button v-show="active == 0" type="primary" @click="next(1)"></el-button>
<el-button v-show="active == 1" type="primary" @click="next(0)"></el-button>
<el-button v-show="active == 1" type="primary" @click.native="ok" :loading="submitLoading"></el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { addAsByCluster } from 'views/tce/services/as.js'
import addConfig from './components/addConfig'
import addAs from './components/addAs'
export default {
components: { addConfig, addAs },
props: {
clusterInfo: {
type: Object,
default: function () {
return {}
}
},
vendorId: {
type: Number,
default: -1
},
addData: {
type: Object,
default: function () {
return {
data: {},
dialog: false
}
}
},
platformObject: {
type: Object
}
},
data() {
return {
active: 0,
configData: {},
asData: {},
submitLoading: false
}
},
methods: {
ok() {
this.asData = this.$refs.as.getPostData()
if (this.asData.subnetIds.length == 0) {
this.$message.error('请选择子网')
return
}
if (this.asData) {
const str1 = this.asData.autoScalingGroupName
const str2 = this.configData.launchConfigurationName
this.asData.autoScalingGroupName = str2
this.configData.launchConfigurationName = str1
const params = {
vendorId: this.vendorId,
region: this.addData.data.clusterInfo.regionId,
clusterId: this.addData.data.clusterInfo.clusterId,
autoScalingGroupPara: JSON.stringify(this.asData),
launchConfigurePara: JSON.stringify(this.configData),
instanceAdvancedSettings: {
mountTarget: '',
dockerGraphPath: '/var/lib/docker',
userScript: '',
unschedulable: 0,
labels: []
}
}
this.submitLoading = true
addAsByCluster(params).then(res => {
this.submitLoading = false
if (res.success) {
this.$message.success(res.message)
this.$emit('back')
this.addData.dialog = false
}
})
}
},
next(n) {
if (n == 1) {
this.configData = this.$refs.config.getPostData()
if (this.configData && this.configData.keypair == '设置密码' && this.configData.password != this.configData.endPassword) {
this.$message.error('两次输入密码不一致!')
return
}
if (this.configData) {
this.active = n
}
} else if (n == 0) {
this.active = n
}
}
}
}
</script>
<style></style>

View File

@ -0,0 +1,168 @@
<template>
<div>
<cb-form :model="addData" ref="addData" label-width="160px">
<el-row>
<el-col :span="24">
<cb-form-item label="节点范围">
<cb-form-item label="最小节点数">
<el-input-number :min="0" :max="200" v-model.number="addData.minSize" @change="handleMinSize" placeholder="最小节点数"></el-input-number>
</cb-form-item>
<cb-form-item label="最大节点数">
<el-input-number :min="addData.minSize" :max="200" v-model.number="addData.maxSize" placeholder="最大节点数"> </el-input-number>
</cb-form-item>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<cb-form-item label="活动重试策略">
<el-select v-model="addData.retryPolicy" placeholder="请选择">
<el-option v-for="(item, index) in retryPolicyList" :key="index" :label="item.name" :value="item.value"> </el-option>
</el-select>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<cb-form-item label="VPC">
<el-select v-model="addData.vpcId" placeholder="请选择" @change="handleVpc" disabled>
<el-option v-for="(item, index) in vpcList" :key="index" :label="`${item.vpcId} | ${item.name}`" :value="item.vpcId"> </el-option>
</el-select>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<cb-form-item label="支持子网">
<cb-advance-table ref="subnetList" :showTools="false" :data="subnetList" :columns="subnetColumns" :get-list="getVpc" :loading="tableLoading" @selection-change="handleSelectChange">
<div slot="pagination"></div>
</cb-advance-table>
</cb-form-item>
</el-col>
</el-row>
</cb-form>
</div>
</template>
<script>
import { getVpc } from 'views/tce/services/vpcs.js'
import { getSubnet } from 'views/tce/services/subnets.js'
const retryPolicyList = [
{ name: '立即重试', value: 'IMMEDIATE_RETRY' },
{ name: '间隔递增重试', value: 'INCREMENTAL_INTERVALS' }
]
const subnetColumns = [
{
type: 'selection',
disabled: true,
selectable: (row, index) => {
return true
}
},
{
label: '子网',
prop: 'subnetUuid'
},
{
label: '子网名称',
prop: 'name'
},
{
label: '可用区',
prop: 'zoneName'
}
]
export default {
props: {
vendorId: {
type: Number,
default: -1
},
clusterInfo: {
type: Object,
default: function () {
return {}
}
}
},
data() {
return {
addData: {
autoScalingGroupName: '',
maxSize: 0,
minSize: 0,
vpcId: '',
subnetIds: [],
retryPolicy: 'INCREMENTAL_INTERVALS'
},
subnetColumns,
tableLoading: false,
subnetList: [],
loading: false,
retryPolicyList,
vpcList: []
}
},
methods: {
getVpc() {
this.tableLoading = true
getVpc({
page: 1,
rows: 9999,
params: JSON.stringify([{ param: { vendorId: this.vendorId }, sign: 'EQ' }])
}).then(data => {
if (data.success) {
this.vpcList = data.data.rows
this.addData.vpcId = JSON.parse(this.clusterInfo.clusterNetWorkSetting).vpcId
this.getSubnetList(data.data.rows.find(item => item.vpcId == this.addData.vpcId).id)
}
})
},
handleVpc() {
this.getSubnetList(this.vpcList.find(item => item.vpcId == this.addData.vpcId).id)
},
getSubnetList(vpcId) {
getSubnet({
page: 1,
rows: 9999,
params: JSON.stringify([
{
param: {
vendorId: this.vendorId,
networkId: vpcId
},
sign: 'EQ'
}
])
}).then(data => {
if (data.success) {
this.tableLoading = false
this.subnetList = data.data.rows
}
})
},
handleMinSize() {
if (this.addData.maxSize < this.addData.minSize) {
this.addData.maxSize = this.addData.minSize
}
},
handleSelectChange(selection) {
this.selectionIds = selection.map(item => item.id)
this.addData.subnetIds = selection.map(item => item.subnetUuid)
},
getPostData() {
let addData = ''
this.$refs.addData.validate(valid => {
if (valid) {
addData = JSON.parse(JSON.stringify(this.addData))
addData.vendorId = this.vendorId
}
})
return addData
}
},
created() {
this.getVpc()
}
}
</script>

View File

@ -0,0 +1,343 @@
<template>
<cb-form :model="addData" ref="addData" label-width="140px">
<el-row>
<el-col :span="24">
<el-form-item label="计费方式:">
<el-radio-group v-model="addData.instanceChargeType">
<el-radio-button label="POSTPAID_BY_HOUR">按量计费</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="可用地域:" prop="regionId" validate="required" required-message="">
<el-select v-model="addData.regionId" @change="region" disabled>
<el-option v-for="(item, index) in regionList" :label="item.name" :value="item.regionId" :key="index"></el-option>
</el-select>
</cb-form-item>
</el-col>
<el-col :span="12">
<cb-form-item label="可用区域:" prop="zoneId" validate="required" required-message="">
<el-select v-model="addData.zoneId" @change="handleZone">
<el-option v-for="(item, index) in zoneList" :label="item.name" :value="item.zoneId" :key="index"></el-option>
</el-select>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="启动配置名称:" prop="launchConfigurationName" validate="required,tceConfigName" required-message="" maxlength="25">
<el-input v-model="addData.launchConfigurationName"></el-input>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="镜像类型:">
<el-radio-group v-model="addData.imageType" @change="chooseWay">
<el-radio-button :disabled="!addData.regionId" :label="item.value" :key="index" v-for="(item, index) in typeList"> {{ item.name }}</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="系统类型:" required>
<el-select filterable v-model="addData.imageName" @change="setImageId">
<el-option v-for="(value, name, index) in imageList" :key="index" :label="name" :value="name"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<cb-form-item label-width="10px" label="" prop="imageId" validate="required" required-message="">
<el-select filterable v-model="addData.imageId" @change="setOsCategory">
<el-option v-for="(item, index) in imageList[addData.imageName]" :key="index" :label="item.name" :value="item.imageUuid"></el-option>
</el-select>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24" v-if="addData.zoneId">
<!-- <flavor :add-data="addData" :vendor-id="vendorId" :az="addData.zoneId" :region="addData.regionId"></flavor> -->
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<cb-form-item label="登录方式:" validate="required">
<el-radio-group v-model="keypair" @change="chooseLoginWay">
<el-radio-button label="关联密钥"></el-radio-button>
<el-radio-button label="设置密码"></el-radio-button>
</el-radio-group>
</cb-form-item>
</el-col>
<el-col :span="24" v-if="keypair == '关联密钥'">
<cb-form-item label="云主机密钥:" prop="keypairName" validate="required" required-message="">
<el-select v-model="addData.keypairName">
<el-option v-for="item in keypairList" :key="item.id" :label="item.name" :value="item.fingerprint"></el-option>
</el-select>
</cb-form-item>
</el-col>
<el-col :span="24" v-else-if="keypair == ''">
<el-col :span="12">
<cb-form-item label="登录密码:" prop="password" validate="required,tceClusterAsPassword" required-message="">
<el-input type="password" v-model="addData.password" show-password></el-input>
</cb-form-item>
</el-col>
<el-col :span="12">
<cb-form-item label="确认密码:" prop="endPassword" validate="required,tceClusterAsPassword" required-message="">
<el-input type="password" v-model="addData.endPassword" show-password></el-input>
</cb-form-item>
</el-col>
</el-col>
<el-col v-else></el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="系统磁盘类型:" prop="systemDisk.diskType" validate="required" required-message="">
<el-select v-model="addData.systemDisk.diskType" placeholder="请选择">
<el-option :disabled="!item.available" v-for="(item, index) in diskTypeList" :key="index" :label="item.diskType | diskType" :value="item.diskType"> </el-option>
</el-select>
</cb-form-item>
</el-col>
<el-col :span="12">
<cb-form-item label="系统磁盘(GB)" prop="systemDisk.diskSize" validate="required,number" required-message="">
<el-input-number :min="50" :max="1024" v-model.number="addData.systemDisk.diskSize" style="width: 100%"> </el-input-number>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="公网IP">
<el-radio-group v-model="addData.internetAccessible.publicIpAssigned">
<el-radio-button :label="true">现在分配</el-radio-button>
<el-radio-button :label="false">暂不分配</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24" v-if="addData.internetAccessible.publicIpAssigned == true">
<cb-form-item label="目标带宽" prop="internetAccessible.internetMaxBandwidthOut" validate="required">
<el-slider v-model="addData.internetAccessible.internetMaxBandwidthOut" :min="0" :max="100"></el-slider>
<el-input-number v-model="addData.internetAccessible.internetMaxBandwidthOut" :min="0" :max="100"></el-input-number> Mbps
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="安全组:" prop="securityGroupIds" validate="required" required-message="">
<el-select v-model="addData.securityGroupIds" multiple placeholder="请选择">
<el-option v-for="item in groupList" :key="item.id" :label="item.name" :value="item.value"> </el-option>
</el-select>
</cb-form-item>
</el-col>
</el-row>
</cb-form>
</template>
<script>
import { getRegion, getZone } from 'views/tce/services/regions.js'
import { getGroup } from 'views/tce/services/sgroups.js'
import { getKey } from 'views/tce/services/keypairs.js'
import { conditionImage } from 'views/tce/services/images.js'
import { queryAvailable } from 'views/tce/services/cbs.js'
// import flavor from '../../../components/flavor'
export default {
// components: { flavor },
props: {
vendorId: {
type: Number,
default: -1
},
regionId: {
//
type: [Number, String],
default: ''
},
platformObject: {
type: Object
}
},
data() {
return {
addData: {
regionId: this.regionId,
zoneId: '',
imageId: '',
imageName: '',
imageType: 'PUBLIC',
launchConfigurationName: '',
instanceType: '',
systemDisk: {
diskType: '',
diskSize: 50
},
internetAccessible: {
internetChargeType: 'TRAFFIC_POSTPAID_BY_HOUR',
internetMaxBandwidthOut: 0,
publicIpAssigned: true
},
loginSettings: {},
securityGroupIds: [],
enhancedService: {
securityService: {
enabled: true
},
monitorService: {
enabled: true
}
},
instanceChargeType: 'POSTPAID_BY_HOUR'
},
diskTypeList: [],
zoneList: [],
tagList: [],
searchData: {
name: ''
},
params: {
page: 1,
rows: 5
},
instanceTypeData: [],
total1: 0,
groupList: [],
keypair: '关联密钥',
keypairList: [],
regionList: [],
typeList: [
{ name: '公共镜像', value: 'PUBLIC' },
{ name: '私有镜像', value: 'PRIVATE' }
],
imageList: []
}
},
methods: {
region() {
this.addData.zoneId = ''
this.addData.imageId = this.imageId || ''
this.addData.securityGroupIds = []
this.getZoomData()
this.getGroupData()
},
handleZone() {
this.getDisk()
},
getDisk() {
queryAvailable({
vendorId: this.vendorId,
regionId: this.addData.regionId,
diskChargeType: 'POSTPAID_BY_HOUR',
zones: [this.addData.zoneId],
tenantUuid: this.platformObject.tceTenantUuid
}).then(data => {
if (data.success) {
this.diskTypeList = data.data
}
})
},
//
getRegion() {
getRegion({ vendorId: this.vendorId }).then(data => {
if (data.success) {
this.regionList = data.data
}
})
},
//
getZoomData() {
getZone({ vendorId: this.vendorId, regionId: this.addData.regionId }).then(data => {
if (data.success) {
this.zoneList = data.data
}
})
},
//
getGroupData() {
getGroup({
simple: true,
params: JSON.stringify([{ param: { vendorId: this.vendorId, regionId: this.addData.regionId, tenantId: 0 }, sign: 'EQ' }])
}).then(data => {
if (data.success) {
this.groupList = data.data.rows
}
})
},
//
chooseLoginWay(item) {
if (this.keypair == '自动生成') {
this.addData.loginType = 'auto'
} else if (this.keypair == '关联密钥') {
this.addData.loginType = 'keyIds'
} else if (this.keypair == '设置密码') {
this.addData.loginType = 'password'
}
},
//
getKeypair() {
getKey({
page: 1,
rows: 99999,
params: JSON.stringify([{ param: { vendorId: this.vendorId }, sign: 'EQ' }])
}).then(data => {
if (data.success) {
this.keypairList = data.data.rows
}
})
},
setOsCategory() {
this.imageList[this.addData.imageName].forEach(item => {
if (item.id == this.addData.imageId) {
this.$emit('setOsCategory', item)
}
})
},
chooseWay() {
this.addData.imageName = ''
this.addData.imageId = ''
this.getImageData()
},
setImageId() {
this.addData.imageId = ''
if (this.vendorType == 'TENCENT') {
this.addData.imageId = this.imageList[this.addData.imageName].length ? this.imageList[this.addData.imageName][0].id : ''
this.setOsCategory()
}
},
getImageData() {
conditionImage({
condition: 'listByImageType',
vendorId: this.vendorId,
status: 'ACTIVE',
tenantId: 0,
regionId: this.addData.regionId,
imageType: this.addData.imageType
}).then(data => {
if (data.success) {
this.imageList = data.data
}
})
},
getPostData() {
let addData = ''
if (!this.addData.instanceType) {
this.$message.error('请选择规格')
return ''
}
this.$refs.addData.validate(valid => {
if (valid) {
addData = JSON.parse(JSON.stringify(this.addData))
addData.vendorId = this.vendorId
addData.keypair = this.keypair
}
})
return addData
}
},
created() {
this.getRegion()
this.getKeypair()
this.getImageData()
this.region()
}
}
</script>

View File

@ -0,0 +1,424 @@
<template>
<div>
<el-card v-if="tableData.length != 0 && wholeAllocationData">
<div class="top-text">
<h4>全局配置</h4>
<el-button type="text" @click="wholeAllocatioEidt"></el-button>
</div>
<el-form label-position="left" label-width="150px" :model="wholeAllocationData" class="top-form">
<el-form-item label="自动伸容">
{{ wholeAllocationData.isScaleDownEnabled ? '开启' : '已关闭' }}
</el-form-item>
<el-form-item label="最大并发缩容数" v-if="wholeAllocationData.isScaleDownEnabled">
{{ wholeAllocationData.maxEmptyBulkDelete }}
<el-tooltip class="item" effect="dark" content="只缩容符合完全空闲的空节点。如果存在Pod, 每次缩容最多一个节点" placement="top-start">
<i class="el-icon-info"></i>
</el-tooltip>
</el-form-item>
<el-form-item label="缩容计算方法" v-if="wholeAllocationData.isScaleDownEnabled">
Pod占用资源/可分配资源小于{{ wholeAllocationData.scaleDownUtilizationThreshold }}%开始判断缩容条件 <br />
{{ wholeAllocationData.ignoreDaemonSetsUtilization ? 'DaemonSet类型不计入pod占用资源' : '' }}
</el-form-item>
<el-form-item label="节点连续空闲" v-if="wholeAllocationData.isScaleDownEnabled"> {{ wholeAllocationData.scaleDownUnneededTime }} </el-form-item>
<el-form-item label="集群扩容" v-if="wholeAllocationData.isScaleDownEnabled"> {{ wholeAllocationData.scaleDownDelay }} </el-form-item>
<el-form-item label="不缩容节点" v-if="wholeAllocationData.isScaleDownEnabled">
{{ wholeAllocationData.skipNodesWithLocalStorage ? '含有本地存储Pod的节点' : '' }} <br />
{{ wholeAllocationData.skipNodesWithSystemPods ? '含有kube-system namespace下非DaemonSet管理的Pod的节点' : '' }}
</el-form-item>
<el-form-item label="扩容算法">
{{ wholeAllocationData.expander == 'random' ? '随机' : wholeAllocationData.expander }}
</el-form-item>
</el-form>
</el-card>
<cb-advance-table @selection-change="handleSelectChange" :data="tableData" :params="params" :columns="column" :get-list="getData" :total="total" :loading="loading">
<template v-slot:action>
<el-button type="primary" @click="addAsByCluster"> </el-button>
</template>
<template #status="val">
<cb-status-icon :type="val | tceInstanceColor" v-if="val != 'STOPPED'">{{ val | tceInstanceText }}</cb-status-icon>
</template>
<template #stretchGroup="val">
{{ val.launchConfigurationId }}<br />
{{ val.launchConfigurationName }}
</template>
<template #operate="val, record">
<el-button type="text" @click="moreOperate({ flag: 1, row: record })">调整配置</el-button>
<el-button type="text" @click="moreOperate({ flag: 2, row: record })">删除</el-button>
</template>
</cb-advance-table>
<el-dialog title="删除伸缩组" v-if="delectVisible" :visible.sync="delectVisible" width="600px" center append-to-body>
<p>
<strong>确定要删除ID为{{ delectPrams.autoScalingGroupId }}的伸缩组么</strong>
</p>
<el-checkbox v-model="delectPrams.keepInstance" :true-label="1" :false-label="0"></el-checkbox>
<span slot="footer" class="dialog-footer">
<el-button @click="delectVisible = false"> </el-button>
<el-button type="primary" v-loading="loading" @click="deleteData"> </el-button>
</span>
</el-dialog>
<el-dialog title="调整伸缩组配置" v-if="allocationVisible" :visible.sync="allocationVisible" width="600px" :close-on-click-modal="false" center append-to-body>
<el-form :model="allocationParams" class="demo-form-inline" label-width="80px">
<el-form-item label="伸缩组ID">
{{ allocationParams.AutoScalingGroupId }}
</el-form-item>
<el-form-item label="实例范围">
<el-input-number size="mini" :precision="0" :min="0" v-model="allocationParams.MinSize"></el-input-number> ~
<el-input-number size="mini" :precision="0" :min="allocationParams.MinSize" v-model="allocationParams.MaxSize"></el-input-number>
<p>在设定的实例范围内自动调节不会超出该设定范围</p>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="cancel"> </el-button>
<el-button type="primary" :loading="loading" @click="sumbit(null)"> </el-button>
</span>
</el-dialog>
<el-dialog title="设置集群伸缩全局配置" v-if="wholeAllocationVisible" :visible.sync="wholeAllocationVisible" width="60%" :close-on-click-modal="false" append-to-body>
<el-form :model="wholeAllocationForm" class="demo-form-inline" label-width="100px">
<el-form-item label="自动伸缩">
<el-checkbox v-model="wholeAllocationForm.isScaleDownEnabled"></el-checkbox> <br />
<span>集群中节点空闲资源较多时将触发缩容</span>
</el-form-item>
<el-form-item label="缩容配置" v-if="wholeAllocationForm.isScaleDownEnabled">
<div class="info">
<span
>最大并发缩容数
<el-tooltip class="item" effect="dark" content="只缩容符合完全空闲的空节点。如果存在Pod, 每次缩容最多一个节点" placement="top-start">
<i class="el-icon-info"></i>
</el-tooltip>
</span>
<el-input-number size="mini" :min="0" :max="200" v-model="wholeAllocationForm.maxEmptyBulkDelete"></el-input-number>
</div>
<div class="info">
<span>Pod占用资源/可分配资源小于</span>
<el-input-number size="mini" :min="0" :max="80" v-model="wholeAllocationForm.scaleDownUtilizationThreshold"></el-input-number>
<span>%时开始判断缩容条件</span>
<p><span></span> 占比范围为0-80</p>
<p>
<span></span>
<el-checkbox v-model="wholeAllocationForm.ignoreDaemonSetsUtilization">DaemonSetpod</el-checkbox>
</p>
</div>
<div class="info">
<span>节点连续空闲</span>
<el-input-number size="mini" :min="0" v-model="wholeAllocationForm.scaleDownUnneededTime"></el-input-number>
<span>分钟后被缩容</span>
</div>
<div class="info">
<span>集群扩容</span>
<el-input-number size="mini" :min="0" v-model="wholeAllocationForm.scaleDownDelay"></el-input-number>
<span>分钟后开始判断缩容条件</span>
</div>
<div class="info">
<span>不缩容节点</span>
<el-checkbox v-model="wholeAllocationForm.skipNodesWithLocalStorage">Pod</el-checkbox>
<p>
<span></span>
<el-checkbox v-model="wholeAllocationForm.skipNodesWithSystemPods">kube-system namespaceDaemonSetPod</el-checkbox>
</p>
</div>
</el-form-item>
<el-form-item label="扩容算法">
<el-radio-group v-model="wholeAllocationForm.expander">
<el-radio label="random">随机</el-radio>
<el-radio label="most-pods">most-pods</el-radio>
<el-radio label="least-waste">least-waste</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" :loading="loading" @click="sumbit('wholeAllocationForm')"></el-button>
<el-button @click="cancel"></el-button>
</div>
</el-dialog>
<addAsDialog
v-if="addAsByClusterData.dialog"
:addData="addAsByClusterData"
:vendorId="vendorId"
:clusterInfo="clusterInfo"
:platform-object="platformObject"
@back="
cancel()
getData()
"
></addAsDialog>
</div>
</template>
<script>
import { defineComponent, ref, reactive, watch } from 'vue'
import { getFlexibleList, delectFlexible, editFlexibleAllocation, getWholeAllocation, editWholeAllocation } from '../../../services/tke.js'
import { Message } from 'element-ui'
import addAsDialog from './addAsDialog.vue'
const columns = [
{
label: '伸缩组ID',
prop: 'autoScalingGroupId'
},
{
label: '状态',
prop: 'status',
scopedSlots: { customRender: 'status' }
},
{
label: '子机数量',
prop: 'stretchGroup.instanceCount'
},
{
label: '最小伸缩数',
prop: 'stretchGroup.minSize'
},
{
label: '最大伸缩数',
prop: 'stretchGroup.maxSize'
},
{
label: '启动配置ID/名称',
prop: 'stretchGroup',
scopedSlots: { customRender: 'stretchGroup' }
},
{
label: '操作',
prop: 'id',
width: '160px',
disabled: true,
showOverflowTooltip: false,
scopedSlots: { customRender: 'operate' }
}
]
export default defineComponent({
components: {
addAsDialog
},
props: {
clusterInfo: {
type: Object,
default: function () {
return {}
}
},
vendorId: {
type: Number,
required: true
},
platformObject: {
type: Object
}
},
setup(props, context) {
const selectionIds = ref([])
const column = ref(columns)
const selection = ref([])
const tableData = ref([])
const total = ref(0)
const loading = ref(false)
const delectVisible = ref(false)
const allocationVisible = ref(false)
const wholeAllocationVisible = ref(false)
const wholeAllocationData = ref({})
const wholeAllocationForm = ref({})
const allocationParams = ref({
MinSize: 0,
MaxSize: 0
})
const addAsByClusterData = reactive({
dialog: false,
data: {}
})
const delectPrams = reactive({
keepInstance: 1
})
const params = reactive({
page: 1,
rows: 10
})
function addAsByCluster() {
addAsByClusterData.dialog = true
addAsByClusterData.data = {
clusterInfo: props.clusterInfo
}
}
function goBack() {
context.emit('close')
}
function handleSelectChange(selection) {
selectionIds.value = selection.map(item => item.id)
selection.value = selection
}
async function getData() {
try {
loading.value = true
const listPrams = {
...params,
params: JSON.stringify([{ param: { vendorId: props.vendorId, clusterId: props.clusterInfo.clusterId }, sign: 'EQ' }])
}
const { data } = await getFlexibleList(listPrams)
tableData.value = data.rows
total.value = data.total
const WholeParams = {
...params,
params: JSON.stringify([{ param: { clusterId: props.clusterInfo.clusterId }, sign: 'EQ' }])
}
const {
data: { rows }
} = await getWholeAllocation(WholeParams)
wholeAllocationData.value = rows[0]
cancel()
} catch {
cancel()
}
}
function moreOperate(command) {
if (command.flag === 1) {
allocationParams.value.id = command.row.id
allocationParams.value.vendorId = command.row.vendorId
allocationParams.value.ClusterId = command.row.clusterId
allocationParams.value.AutoScalingGroupId = command.row.autoScalingGroupId
allocationParams.value.MinSize = command.row.stretchGroup.minSize
allocationParams.value.MaxSize = command.row.stretchGroup.maxSize
allocationParams.value.name = command.row.stretchGroup.autoScalingGroupName
allocationVisible.value = true
} else {
delectPrams.id = command.row.id
delectPrams.name = command.row.stretchGroup.autoScalingGroupName
delectPrams.autoScalingGroupId = command.row.autoScalingGroupId
delectVisible.value = true
}
}
async function deleteData() {
loading.value = true
try {
const { success } = await delectFlexible(delectPrams)
if (success) {
Message.success('删除成功')
delectVisible.value = false
getData()
}
} catch {
delectVisible.value = false
}
}
async function sumbit(type) {
if (type) {
loading.value = true
try {
const { success } = await editWholeAllocation(wholeAllocationForm.value)
loading.value = false
if (success) {
Message.success('修改成功')
cancel()
getData()
}
} catch {
loading.value = false
}
} else {
if (allocationParams.value.MinSize > allocationParams.value.MaxSize) {
Message.error('实例范围最大值不能小于最小值')
} else {
loading.value = true
try {
const { success } = await editFlexibleAllocation(allocationParams.value)
loading.value = false
if (success) {
Message.success('修改成功')
cancel()
getData()
} else {
loading.value = false
}
} catch {
loading.value = false
}
}
}
}
function wholeAllocatioEidt() {
wholeAllocationVisible.value = true
wholeAllocationForm.value = {
...wholeAllocationData.value
}
}
function cancel() {
allocationVisible.value = false
wholeAllocationVisible.value = false
loading.value = false
}
watch(
props.clusterInfo,
() => {
getData()
},
{ immediate: true }
)
return {
addAsByCluster,
addAsByClusterData,
goBack,
handleSelectChange,
tableData,
selectionIds,
params,
column,
getData,
total,
loading,
moreOperate,
delectVisible,
deleteData,
delectPrams,
allocationVisible,
allocationParams,
sumbit,
cancel,
wholeAllocationVisible,
wholeAllocationData,
wholeAllocatioEidt,
wholeAllocationForm
}
}
})
</script>
<style lang="scss" scoped>
div,
p,
h4 {
padding: 0;
margin: 0;
}
.el-card {
margin-bottom: 30px;
.top-text {
display: flex;
justify-content: space-between;
}
.top-form .el-form-item {
margin-bottom: 0px;
}
}
h4 {
display: inline-block;
margin-bottom: 10px;
}
::v-deep .el-dialog {
.info {
color: #9e9e9e;
margin-bottom: 10px;
.el-input-number {
margin-right: 3%;
}
p .el-checkbox .el-checkbox__label {
font-size: 12px;
}
}
}
.info span {
display: inline-block;
width: 30%;
margin-right: 5%;
}
.item {
margin-left: 20px;
color: #e4e464;
}
</style>

View File

@ -0,0 +1,43 @@
<template>
<div>
<cluster ref="containerTab" :platform-object="platformObject"></cluster>
</div>
</template>
<script>
import cluster from './cluster.vue'
// TCE
export default {
props: {
platformObject: {
type: Object,
default: function () {
return {
vendorId: -1
}
}
}
},
components: {
cluster
},
data() {
return {
activeName: 'containerTab'
}
},
methods: {
handleClick() {
switch (this.activeName) {
case 'containerTab':
this.$refs.containerTab.getClusterData()
break
default:
}
}
},
mounted() {
this.handleClick()
}
}
</script>

View File

@ -0,0 +1,110 @@
<template>
<div>
<el-dialog title="添加已有节点" width="40%" :close-on-click-modal="false" v-if="addData.dialog" :visible.sync="addData.dialog" append-to-body>
<div v-loading="loading">
<cb-form :model="addData.data" ref="data">
<el-row :gutter="10">
<el-col :span="24">
<cb-form-item label="可用节点:" prop="instances" validate="required" required-message="">
<el-select v-model="addData.data.instances" filterable multiple>
<el-option v-for="(item, index) in instanceList" :key="index" :label="`${item.instanceId}(${item.instanceName})`" :value="item.instanceId" :disabled="!item.usable"></el-option>
</el-select>
</cb-form-item>
</el-col>
</el-row>
</cb-form>
</div>
<div slot="footer" class="dialog-footer">
<el-button type="ghost" @click.native="addData.dialog = false">取消</el-button>
<el-button type="primary" @click.native="ok">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
/* global $ */
import { postAddInstance, getExistInstance } from 'views/tce/services/tke.js'
export default {
props: {
addData: {
type: Object,
default: function () {
return {
data: {
name: '',
vendorId: -1
},
dialog: false
}
}
},
vendorId: {
type: Number,
default: 0
},
clusterInfo: {
type: Object,
default: function () {
return {
data: {},
dialog: false
}
}
}
},
data() {
return {
loading: true,
instanceList: []
}
},
methods: {
getExistInstanceList() {
this.loading = true
this.instanceList = []
const temp = JSON.parse(this.clusterInfo.clusterNetWorkSetting)
const param = {
vpcId: temp.vpcId,
vendorId: this.vendorId,
id: this.clusterInfo.id
}
getExistInstance(param, this.clusterInfo.id).then(data => {
if (data.success) {
this.loading = false
this.instanceList = data.data
}
})
},
ok() {
this.$refs.data.validate(async valid => {
if (valid) {
const param = {
vendorId: this.vendorId,
ClusterId: this.addData.data.id,
InstanceIds: this.addData.data.instances
}
const { success, message } = await postAddInstance(param)
if (success) {
this.$message.success(message)
this.$emit('back')
this.addData.dialog = false
}
}
})
}
},
created() {},
watch: {
'addData.dialog': {
handler(newVal, oldVal) {
if (newVal) {
this.getExistInstanceList()
}
},
deep: true,
immediate: true
}
}
}
</script>

View File

@ -0,0 +1,337 @@
<template>
<cb-form :model="addData" ref="addData" label-width="160px">
<h1>已选配置</h1>
<el-row>
<el-col :span="12">
<cb-form-item label="集群名:" prop="name">
{{ addData.name }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="Kubernetes版本" prop="name">
{{ addData.kubernetesName }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="所在地域:" prop="name">
{{ addData.regionName }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="容器网络:" prop="name">
{{ addData.clusterCIDR }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="操作系统:" prop="name">
{{ addData.systemName }}
</cb-form-item>
</el-col>
</el-row>
<el-divider></el-divider>
<el-row>
<el-col :span="6">
<cb-form-item label="系统盘:" prop="SystemDisk.DiskType" validate="required" required-message="">
<el-select v-model="addData.SystemDisk.DiskType" @change="changeDiskCategory('system')">
<el-option v-for="(item, index) in diskTypeList" :key="index" :value="item.diskType" :label="item.diskType | diskTypeFilter(item)" :disabled="!item.available"></el-option>
</el-select>
</cb-form-item>
</el-col>
<el-col :span="6">
<cb-form-item label="" label-width="40px" prop="SystemDisk.DiskSize" validate="required" required-message="">
<el-slider :min="50" :max="1024" v-model="addData.SystemDisk.DiskSize" show-input>G </el-slider>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="6">
<cb-form-item label="数据盘:" prop="DataDiskFlag" validate="required" required-message="">
<el-radio-group v-model="addData.DataDiskFlag">
<el-radio-button label="0">暂不购买</el-radio-button>
<el-radio-button label="1">立即购买</el-radio-button>
</el-radio-group>
</cb-form-item>
</el-col>
</el-row>
<el-row v-if="addData.DataDiskFlag == 1">
<el-col :span="6">
<cb-form-item label="数据盘:" prop="DataDisks.DiskType" validate="required" required-message="">
<el-select v-model="addData.DataDisks.DiskType" @change="changeDiskCategory('data')">
<el-option v-for="(item, index) in diskTypeList" :key="index" :value="item.diskType" :label="item.diskType | diskTypeFilter(item)" :disabled="!item.available"></el-option>
</el-select>
</cb-form-item>
</el-col>
<el-col :span="6">
<cb-form-item label="" label-width="40px" prop="DataDisks.DiskSize" validate="required" required-message="">
<el-slider :min="addData.DataDisks.minDiskSize" :max="32000" v-model="addData.DataDisks.DiskSize" show-input>G </el-slider>
</cb-form-item>
</el-col>
</el-row>
<el-row v-if="addData.DataDiskFlag == 1">
<el-col :span="8">
<cb-form-item label="数据盘挂载:">
<el-checkbox v-model="addData.storeDiskSwitch"></el-checkbox>
</cb-form-item>
</el-col>
<el-col :span="6" v-if="addData.storeDiskSwitch">
<cb-form-item label="" label-width="0px" prop="mountPath" validate="required" required-message="">
<el-select v-model="addData.mountPath">
<el-option v-for="(item, index) in mountList" :key="index" :label="item.name" :value="item.value"></el-option>
</el-select>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<cb-form-item label="登录方式:" prop="loginWay" validate="required" required-message="">
<el-radio-group v-model="addData.loginWay">
<el-radio-button label="password">设置密码</el-radio-button>
<el-radio-button label="secretKey">立即关联密钥</el-radio-button>
</el-radio-group>
</cb-form-item>
</el-row>
<el-row v-if="addData.loginWay != 'auto'">
<cb-form-item label="用户名:">
{{ username }}
</cb-form-item>
</el-row>
<el-row v-if="addData.loginWay == 'password'" key="password">
<cb-form-item label="密码:" prop="password" validate="required,tceClusterPassword" required-message="">
<el-input type="password" v-model="addData.password"></el-input>
</cb-form-item>
</el-row>
<el-row v-if="addData.loginWay == 'password'" key="password2">
<cb-form-item label="确认密码:" prop="password2" validate="required,tceClusterPassword" required-message="">
<el-input type="password" v-model="addData.password2"></el-input>
</cb-form-item>
</el-row>
<el-row v-if="addData.loginWay == 'secretKey'" key="ssh">
<cb-form-item label="SSH密钥" prop="ssh" validate="required" required-message="SSH">
<el-select v-model="addData.ssh">
<el-option v-for="(item, index) in keyList" :key="index" :label="item.name" :value="item.id"></el-option>
</el-select>
</cb-form-item>
</el-row>
<el-row>
<el-col :span="6">
<cb-form-item label="主机名:" prop="instanceFlag" validate="required" required-message="">
<el-radio-group v-model="addData.instanceFlag">
<el-radio-button label="0">自动命名</el-radio-button>
<el-radio-button label="1">手动命名</el-radio-button>
</el-radio-group>
</cb-form-item>
</el-col>
<el-col :span="12" v-if="addData.instanceFlag == 1">
<cb-form-item label="主机名:" prop="InstanceName" validate="required" required-message="" maxlength="60">
<el-input v-model="addData.InstanceName"></el-input>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<cb-form-item label="安全组:" prop="group" validate="required" required-message="">
<el-select v-model="addData.group">
<el-option v-for="(item, index) in groupList" :key="index" :label="item.name" :value="item.groupUuid"></el-option>
</el-select>
</cb-form-item>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="安全加固:">
<el-checkbox v-model="addData.SecurityService"></el-checkbox>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="云监控:">
<el-checkbox v-model="addData.MonitorService"></el-checkbox>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="自定义数据:">
<el-input type="textarea" :rows="5" v-model="addData.selfDefining"></el-input>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="封锁cordon">
<el-checkbox v-model="addData.cordon"></el-checkbox>
</cb-form-item>
</el-col>
</el-row>
</cb-form>
</template>
<script>
import { getGroup } from 'views/tce/services/sgroups.js'
import { getKey } from 'views/tce/services/keypairs.js'
import { queryAvailable } from 'views/tce/services/cbs.js'
import { computed } from 'vue'
const mountList = [
{ name: '/var/lib/docker', value: '/var/lib/docker' },
{ name: '/data', value: '/data' },
{ name: '/opt', value: '/opt' }
]
export default {
components: {},
props: {
vendorId: {
type: Number,
default: -1
},
activeStep: {
type: Number,
default: -1
},
addData: {
type: Object,
default: () => {
return {}
}
},
platformObject: {
type: Object
}
},
data() {
return {
mountList,
groupList: [],
keyList: [],
diskTypeList: []
}
},
watch: {
'addData.zoneId'(val) {
this.getDiskType(val)
},
activeStep: {
handler(val) {
if (val == 2) {
this.getGroup()
this.getKey()
}
},
immediate: true,
deep: true
}
},
filters: {
diskTypeFilter(val, item) {
const map = {
CLOUD_BASIC: '普通云硬盘',
CLOUD_PREMIUM: '高性能云硬盘',
CLOUD_SSD: 'SSD云硬盘'
}
return `${!item.available ? '【售罄】' : ''}${map[val]}`
}
},
setup(props) {
const username = computed(() => {
const { systemName } = props.addData
if (systemName.includes('ubuntu')) return 'ubuntu'
return 'root'
})
return {
username
}
},
methods: {
changeDiskCategory(type) {
const {
DataDisks,
DataDisks: { DiskSize, DiskType }
} = this.addData
// SSD100
if (DiskType === 'CLOUD_SSD' && type === 'data') {
DataDisks.minDiskSize = 100
if (DiskSize < 100) this.addData.DataDisks.DiskSize = 100
} else {
DataDisks.minDiskSize = 50
}
},
getDiskType(az) {
queryAvailable({
vendorId: this.vendorId,
diskChargeType: 'POSTPAID_BY_HOUR',
zones: [az],
regionId: this.addData.data.regionId,
tenantUuid: this.platformObject.tceTenantUuid
}).then(data => {
if (data.success) {
this.diskTypeList = data.data
}
})
},
getData() {
//
if (this.addData.loginWay == 'password' && this.addData.password !== this.addData.password2) return this.$message.error('两次密码输入不一致')
this.$refs.addData.validate(valid => {
if (valid) {
if (this.addData.loginWay == 'secretKey') {
this.addData.sshId = this.keyList.find(item => item.id == this.addData.ssh).fingerprint
}
this.$emit('addStep')
}
})
},
getKey() {
this.keyList = []
getKey({
page: 1,
rows: 9999,
params: JSON.stringify([
{
param: {
vendorId: this.vendorId
},
sign: 'EQ'
}
])
}).then(data => {
if (data.success) {
this.keyList = data.data.rows
}
})
},
getGroup() {
this.groupList = []
getGroup({
page: 1,
rows: 9999,
params: JSON.stringify([
{
param: {
vendorId: this.vendorId
},
sign: 'EQ'
}
])
}).then(data => {
if (data.success) {
this.groupList = data.data.rows
}
})
}
}
}
</script>
<style scoped>
.hostClass {
background-color: rgb(242, 242, 242) !important;
padding-top: 10px;
margin-bottom: 20px;
}
</style>

View File

@ -0,0 +1,271 @@
<template>
<div class="wrapper-container" v-loading="loading">
<el-card>
<el-steps :active="activeStep" finish-status="success" style="margin: 0 auto 20px; width: 70%">
<el-step title="集群信息"></el-step>
<el-step title="选择机型"></el-step>
<el-step title="云主机配置"></el-step>
<el-step title="信息确认"></el-step>
</el-steps>
<el-col :span="24" class="panel-body" v-show="activeStep == 0"> </el-col>
<el-col :span="24" class="panel-body" v-show="activeStep == 1">
<type-item v-if="vendorId && addData.regionId" ref="typeItem" :addData="addData" :vendor-id="vendorId" :platform-object="platformObject" :activeStep="activeStep" @addStep="addStep"></type-item>
</el-col>
<el-col :span="24" class="panel-body" v-show="activeStep == 2">
<host-config-item v-if="vendorId" ref="hostConfigItem" :addData="addData" :vendor-id="vendorId" :activeStep="activeStep" @addStep="addStep"></host-config-item>
</el-col>
<el-col :span="24" class="panel-body" v-show="activeStep == 3">
<info-item ref="infoItem" :addData="addData" :vendor-id="vendorId" :activeStep="activeStep"></info-item>
</el-col>
<el-col :span="24" class="m-b-lg">
<el-col :span="6" :offset="5" v-if="activeStep == 1">
<el-button type="ghost" style="width: 100%" @click="goBack"></el-button>
</el-col>
<el-col :span="6" :offset="5" v-if="activeStep > 1">
<el-button type="primary" style="width: 100%" @click="addLastStep()"></el-button>
</el-col>
<el-col :span="6" :offset="1" v-if="activeStep == 3">
<el-button type="primary" style="width: 100%" @click="ok" :loading="loading">确定</el-button>
</el-col>
<el-col :span="6" :offset="1" v-if="activeStep != 3">
<el-button type="primary" style="width: 100%" @click="addNextStep()"></el-button>
</el-col>
</el-col>
</el-card>
</div>
</template>
<script>
import typeItem from './typeItem.vue'
import { getTKERegions, createInstance, getClusterDetail } from 'views/tce/services/tke.js'
import { getVpc } from 'views/tce/services/vpcs.js'
import hostConfigItem from './hostConfigItem.vue'
import infoItem from './infoItem.vue'
export default {
props: {
platformObject: {
type: Object
}
},
components: {
typeItem,
hostConfigItem,
infoItem
},
name: '',
data() {
return {
vpcList: [],
activeStep: 1,
vendorId: 0,
clusterInfo: {},
loading: true,
addData: {
vpcName: '',
vpcNumberId: '',
InstanceCount: 1,
name: '',
kubernetesName: '',
regionId: '', //
clusterCIDR: '',
vpcId: '',
systemName: '',
hostDialog: false,
SystemDisk: {
DiskType: '',
name: '',
DiskSize: 50
},
DataDisks: {
DiskType: '',
name: '',
DiskSize: 50
},
DataDiskFlag: 0,
storeDiskSwitch: true,
mountPath: '/var/lib/docker',
group: '',
cordon: '',
selfDefining: '',
ssh: '',
sshId: '',
password: '',
password2: '',
loginWay: 'password',
instanceFlag: 0,
InstanceName: '',
subentCreateType: 'Automatic',
PrivateIpAddresses: [],
SecurityService: true,
MonitorService: true,
SubnetId: '',
specData: {}
}
}
},
async created() {
this.vendorId = this.$route.query.vendorId / 1
await this.getClusterInfo()
this.getVpc()
},
methods: {
async getRegionName(regionId) {
const res = await getTKERegions({
tenantUuid: this.platformObject.tceTenantUuid,
vendorId: this.vendorId
})
if (res.success) {
return res.data.regionInstanceSet.find(item => item.alias === regionId).regionName
}
},
async getClusterInfo() {
const res = await getClusterDetail(this.$route.query.clusterId)
const { clusterName, clusterVersion, clusterId, imageId, region: regionId, clusterNetWorkSetting, clusterOs, vendorId } = res.data
const { clusterCIDR, vpcId } = clusterNetWorkSetting
this.addData = {
...this.addData,
name: clusterName,
kubernetesName: clusterVersion,
regionId,
regionName: await this.getRegionName(regionId),
systemName: clusterOs,
clusterCIDR,
vpcId,
clusterId,
vendorId,
imageId
}
this.loading = false
},
getVpc() {
getVpc({
page: 1,
rows: 9999,
params: JSON.stringify([{ param: { vendorId: this.vendorId }, sign: 'EQ' }])
}).then(data => {
if (data.success) {
this.vpcList = data.data.rows
const item = data.data.rows.find(item => item.vpcId == this.addData.vpcId)
this.addData.vpcName = item.name
this.addData.vpcNumberId = item.id
}
})
},
goBack() {
this.$router.push({
name: 'tceContainer'
})
},
ok() {
let LoginSettings = {}
switch (this.addData.loginWay) {
case 'password':
LoginSettings = {
Password: this.addData.password
}
break
case 'secretKey':
LoginSettings = {
KeyIds: [this.addData.sshId]
}
break
default:
}
const { SubnetId, PrivateIpAddresses, clusterId, subentCreateType, vpcId, zoneId, SystemDisk, group, DataDisks, DataDiskFlag, specData, InstanceCount, imageId, SecurityService, MonitorService } = this.addData
const VirtualPrivateCloud = {
VpcId: vpcId,
SubnetId,
AsVpcGateway: false
}
if (subentCreateType === 'Manual') {
VirtualPrivateCloud.PrivateIpAddresses = PrivateIpAddresses
}
const RunInstancePara = {
InstanceChargeType: 'POSTPAID_BY_HOUR',
Placement: {
Zone: zoneId,
ProjectId: 0
},
InstanceType: specData.flavorUuid,
SystemDisk: {
DiskType: SystemDisk.DiskType,
DiskSize: SystemDisk.DiskSize
},
DataDisks: [
{
DiskType: DataDisks.DiskType || 'CLOUD_SSD',
DiskSize: DataDiskFlag == 1 ? DataDisks.DiskSize : 0
}
],
VirtualPrivateCloud,
InternetAccessible: {
//
InternetChargeType: 'TRAFFIC_POSTPAID_BY_HOUR',
InternetMaxBandwidthOut: 0,
PublicIpAssigned: false
},
InstanceCount,
ImageId: imageId,
InstanceName: this.addData.instanceFlag == 1 ? this.addData.InstanceName : clusterId + '_worker',
LoginSettings: LoginSettings,
SecurityGroupIds: [group],
EnhancedService: {
SecurityService: {
Enabled: SecurityService
},
MonitorService: {
Enabled: MonitorService
}
}
}
const params = {
vendorId: this.vendorId,
ClusterId: clusterId,
InstanceAdvancedSettings: {
MountTarget: this.addData.mountPath,
DockerGraphPath: this.addData.mountPath,
UserScript: btoa(this.addData.selfDefining),
Unschedulable: this.addData.cordon ? 1 : 0
},
RunInstancePara: JSON.stringify(RunInstancePara)
}
this.loading = true
createInstance(params)
.then(data => {
if (data.success) {
this.$message({
type: 'success',
message: data.message
})
this.$router.push({
name: 'tceContainer'
})
}
})
.finally(() => {
this.loading = false
})
},
addNextStep() {
switch (this.activeStep) {
case 1:
this.$refs.typeItem.getData()
break
case 2:
this.$refs.hostConfigItem.getData()
break
case 3:
break
default:
}
},
addLastStep() {
this.activeStep--
},
addStep() {
this.activeStep++
}
}
}
</script>

View File

@ -0,0 +1,94 @@
<template>
<cb-form :model="addData" ref="addData" label-width="160px">
<h1>已选配置</h1>
<el-row>
<el-col :span="12">
<cb-form-item label="集群名:" validate="required" required-message="">
{{ addData.name }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="Kubernetes版本">
{{ addData.kubernetesName }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="所在地域:">
{{ addData.regionName }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="容器网络:">
{{ addData.clusterCIDR }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="计费模式:"> 按量计费 </cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="规格:">
{{ `${addData.specData.flavorUuid}(${addData.specData.cpu}${addData.specData.memory}GB)` }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="系统盘:"> {{ diskTypeFilter(addData.SystemDisk.DiskType) }}({{ addData.SystemDisk.DiskSize }}G) </cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="数据盘:">
{{ addData.DataDiskFlag == 1 ? `${diskTypeFilter(addData.DataDisks.DiskType)}(${addData.DataDisks.DiskSize})G` : `暂不购买` }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="操作系统:">
{{ addData.systemName }}
</cb-form-item>
</el-col>
</el-row>
</cb-form>
</template>
<script>
export default {
props: {
vendorId: {
type: Number,
default: -1
},
addData: {
type: Object,
default: () => {
return {}
}
}
},
setup() {
function diskTypeFilter(val) {
const map = {
CLOUD_BASIC: '普通云硬盘',
CLOUD_PREMIUM: '高性能云硬盘',
CLOUD_SSD: 'SSD云硬盘'
}
return map[val]
}
return {
diskTypeFilter
}
}
}
</script>

View File

@ -0,0 +1,213 @@
<template>
<cb-form :model="addData" ref="addData" label-width="160px">
<h1>已选配置</h1>
<el-row>
<el-col :span="12">
<cb-form-item label="集群名:" prop="name">
{{ addData.name }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="Kubernetes版本" prop="name">
{{ addData.kubernetesName }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="所在地域:" prop="name">
{{ addData.regionName }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="容器网络:" prop="name">
{{ addData.clusterCIDR }}
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="操作系统:" prop="name">
{{ addData.systemName }}
</cb-form-item>
</el-col>
</el-row>
<el-divider></el-divider>
<el-row>
<el-col :span="12">
<cb-form-item label="计费模式:"> 按量计费 </cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="可用区:" prop="zoneId" validate="required" required-message="">
<el-select v-model="addData.zoneId" @change="handleZone()">
<el-option v-for="(item, index) in zoneList" :label="item.name" :value="item.zoneId" :key="index"></el-option>
</el-select>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<cb-form-item label="节点网络:" prop="SubnetId" validate="required" required-message="">
<el-col :span="11" class="m-r-xs">
<el-input v-model="addData.vpcName" disabled></el-input>
</el-col>
<el-col :span="11">
<el-select v-model="addData.SubnetId" v-if="subnetList.length != 0" placeholder="请选择子网" @change="setIp([])">
<el-option v-for="(item, index) in subnetList" :label="item.name" :value="item.subnetUuid" :key="index"></el-option>
</el-select>
<span v-else style="color: red">此私有网络在该可用区无有效子网</span>
</el-col>
</cb-form-item>
</el-row>
<el-row v-if="addData.SubnetId">
<el-col :span="12">
<cb-form-item label="分配IP">
<el-radio-group v-model="addData.subentCreateType" class="m-r">
<el-radio-button label="Automatic">自动分配</el-radio-button>
<el-radio-button label="Manual">手动分配</el-radio-button>
</el-radio-group>
<template v-if="addData.subentCreateType == 'Manual'">
<el-button type="text" @click="openIpDialog()">IP</el-button>
<el-tag v-for="item in addData.PrivateIpAddresses" :key="item" class="m-l-sm">{{ item }}</el-tag>
</template>
</cb-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<cb-form-item label="数量:">
<el-input-number :min="1" :max="100" v-model="addData.InstanceCount"></el-input-number>
</cb-form-item>
</el-col>
</el-row>
<SpecList :add-data="addData" :vendor-id="vendorId" :az="addData.zoneId" :region="addData.regionId" v-if="addData.zoneId" category="container"></SpecList>
<AssignIp v-if="assignIpDialog.visible" :dialog="assignIpDialog" @setIp="setIp"></AssignIp>
</cb-form>
</template>
<script>
import SpecList from 'views/platform/components/abcstack/flavor.vue'
import AssignIp from '../../creatCluster/AssignIpItem.vue'
import { getSubnet } from 'views/tce/services/subnets.js'
import { getZone } from 'views/tce/services/regions.js'
export default {
components: { SpecList, AssignIp },
name: '',
props: {
vendorId: {
type: Number,
default: -1
},
activeStep: {
type: Number,
default: -1
},
addData: {
type: Object,
default: () => {
return {}
}
}
},
data() {
return {
subnetList: [],
zoneList: [],
assignIpDialog: {}
}
},
methods: {
getData() {
//
this.$refs.addData.validate(valid => {
if (valid) {
if (!this.addData.specData.flavorUuid) {
return this.$message.error('请选择主机规格')
}
const { PrivateIpAddresses, InstanceCount } = this.addData
if (this.addData.subentCreateType === 'Manual' && InstanceCount !== PrivateIpAddresses.length) {
return this.$message.error('指定IP与申请数量不一致请检查')
}
this.$emit('addStep')
}
})
},
openIpDialog() {
const { SubnetId, PrivateIpAddresses, InstanceCount } = this.addData
const obj = this.subnetList.find(item => item.subnetUuid == SubnetId)
this.assignIpDialog = {
visible: true,
data: {
cidr: obj.cidr,
PrivateIpAddresses,
InstanceCount,
allIpAddress: [],
SubnetId,
vendorId: this.vendorId
}
}
},
setIp(data) {
this.addData.PrivateIpAddresses = data
},
//
getZoomData() {
getZone({ vendorId: this.vendorId, regionId: this.addData.regionId }).then(data => {
if (data.success) {
this.zoneList = data.data
this.addData.zoneList = data.data
}
})
},
handleZone() {
this.addData.SubnetId = ''
this.getSubnet()
},
getSubnet() {
getSubnet({
page: 1,
rows: 9999,
params: JSON.stringify([
{
param: {
vendorId: this.vendorId,
networkId: this.addData.vpcNumberId,
type: 0
},
sign: 'EQ'
}
])
}).then(data => {
if (data.success) {
this.subnetList = data.data.rows.filter(temp => temp.zone == this.addData.zoneId)
}
})
}
},
watch: {
activeStep: {
handler(val) {
if (val == 1) {
this.getZoomData()
}
},
immediate: true,
deep: true
}
}
}
</script>
<style scoped>
.hostClass {
background-color: rgb(242, 242, 242) !important;
padding-top: 10px;
margin-bottom: 20px;
}
</style>

View File

@ -0,0 +1,70 @@
<template>
<div>
<el-dialog title="提示" :visible.sync="deleteData.dialog" width="30%" append-to-body>
<p style="font-size: 18px">是否删除节点</p>
<el-checkbox v-model="deleteData.data.mode"></el-checkbox>
<span slot="footer" class="dialog-footer">
<el-button type="ghost" @click="deleteData.dialog = false"> </el-button>
<el-button type="primary" @click="ok"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
/* global $ */
import { deleteInstance } from 'views/tce/services/tke.js'
export default {
props: {
vendorId: {
type: Number,
default: -1
},
clusterInfo: {
type: Object,
default: function () {
return {}
}
},
deleteData: {
type: Object,
default: function () {
return {
data: {
labelIds: []
},
dialog: false
}
}
}
},
data() {
return {
vmList: []
}
},
methods: {
ok() {
const params = {
vendorId: this.deleteData.data.vendorId,
id: this.deleteData.data.id,
instanceId: this.deleteData.data.instanceId,
mode: this.deleteData.data.mode ? 'terminate' : 'retain'
}
deleteInstance(params, {
instanceId: params.instanceId,
mode: params.mode
}).then(data => {
if (data.success) {
this.$message({
type: 'success',
message: data.message
})
this.deleteData.dialog = false
}
})
}
},
created() {}
}
</script>

View File

@ -0,0 +1,60 @@
<template>
<div>
<el-dialog title="提示" :visible.sync="addData.dialog" width="30%" append-to-body>
<p style="font-size: 18px">是否驱逐节点</p>
<span style="display: block">节点驱逐后将会把节点内的所有Pod不包含DaemonSet管理的Pod从节点中驱逐到集群内其他节点并将节点设置为封锁状态</span>
<span style="color: red; display: block">注意本地存储的Pod被驱逐后数据将丢失请谨慎操作</span>
<span slot="footer" class="dialog-footer">
<el-button type="ghost" @click="addData.dialog = false"> </el-button>
<el-button type="primary" @click="ok"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
/* global $ */
import { drain } from 'views/tce/services/tke.js'
export default {
props: {
vendorId: {
type: Number,
default: -1
},
addData: {
type: Object,
default: function () {
return {
data: {
labelIds: []
},
dialog: false
}
}
}
},
data() {
return {
vmList: []
}
},
methods: {
ok() {
const { instanceId, clusterId } = this.addData.data
const params = {
vendorId: this.vendorId,
id: clusterId,
instanceId
}
drain(params).then(data => {
if (data.success) {
this.$message.success(data.message)
this.$emit('back')
this.addData.dialog = false
}
})
}
},
created() {}
}
</script>

View File

@ -0,0 +1,241 @@
<template>
<div>
<cb-advance-table @selection-change="handleSelectChange" :data="tableData" :params="params" :columns="columns" :get-list="getData" :total="total" :loading="loading">
<template v-slot:action>
<el-button type="primary" @click="creatInstance()"> </el-button>
<el-button type="primary" @click="addExistInstance()"> </el-button>
<el-button type="ghost" @click="handleDelete()" :disabled="selectionIds.length == 0"> 批量删除 </el-button>
</template>
<template #status="val">
<cb-status-icon :type="val | tceInstanceColor" v-if="val != 'STOPPED'">{{ val | tceInstanceText }}</cb-status-icon>
</template>
<template #operate="val, record">
<el-button type="text" @click="drain(record)" :disabled="record.instanceRole == 'MASTER_ETCD'"> 驱逐 </el-button>
<div class="action-divider"></div>
<el-button type="text" @click="remove([record.instanceId])" :disabled="record.instanceRole == 'MASTER_ETCD' || detail.tenantName"> 删除 </el-button>
<div class="action-divider"></div>
<el-button type="text" :disabled="record.instanceRole == 'MASTER_ETCD'" v-if="record.drainStatus === 'false'" @click.native="handleOperate('block', record)"> </el-button>
<el-button type="text" :disabled="record.instanceRole == 'MASTER_ETCD'" v-else @click.native="handleOperate('unblock', record)"> 解封 </el-button>
</template>
</cb-advance-table>
<drain-instance :add-data="drainData" :vendorId="detail.vendorId" v-if="drainData.dialog" :detail="detail"></drain-instance>
<delete-instance :delete-data="deleteData" v-if="deleteData.dialog"></delete-instance>
<add-exist-instance :add-data="addExistInstanceData" :vendor-id="detail.vendorId" :clusterInfo="detail" v-if="addExistInstanceData.dialog"></add-exist-instance>
</div>
</template>
<script>
import drainInstance from './drainInstance.vue'
import deleteInstance from './deleteInstance.vue'
import { getInstanceList, operateInstance } from 'views/tce/services/tke.js'
import addExistInstance from './addExistInstance.vue'
const columns = [
{
type: 'selection',
disabled: true,
selectable: (row, index) => {
if (['MASTER_ETCD'].indexOf(row.instanceRole) == -1) {
return true
} else {
return false
}
}
},
{
label: '节点ID',
prop: 'instanceId'
},
{
label: '状态',
prop: 'status',
scopedSlots: { customRender: 'status' }
},
{
label: '角色',
prop: 'instanceRole'
},
{
label: '云盘类型',
prop: 'usageType'
},
{
label: '内网ip',
prop: 'privateIpAddresses'
},
{
label: '外网ip',
prop: 'publicIpAddresses'
},
{
label: '云盘类型',
prop: 'diskType'
},
{
label: '云盘大小',
prop: 'diskSize'
},
{
label: 'cpu核数',
prop: 'cpu'
},
{
label: '内存',
prop: 'memory'
},
{
label: '是否封锁',
prop: 'drainStatus',
customRender(value) {
if (value === 'true') return '是'
else return '否'
}
},
{
label: '创建时间',
prop: 'createTime'
},
{
label: '操作',
prop: 'id',
width: '200px',
disabled: true,
showOverflowTooltip: false,
scopedSlots: { customRender: 'operate' }
}
]
export default {
components: {
drainInstance,
deleteInstance,
addExistInstance
},
props: {
detail: {
type: Object
}
},
name: '',
data() {
return {
selectionIds: [],
selection: [],
columns,
params: {
page: 1,
rows: 10
},
total: 0,
tableData: [],
loading: false,
drainData: {
dialog: false,
data: {}
},
deleteData: {
dialog: false,
data: {}
},
addExistInstanceData: {
dialog: false,
data: {}
}
}
},
methods: {
onmessage(data) {
const arr = ['tke.drain.instance', 'tke.delete.instance']
if (arr.indexOf(data.operate) != -1) {
this.getData()
}
},
async handleOperate(action, record) {
const { clusterId, vendorId, privateIpAddresses: instanceIps } = record
this.$confirm(`您确定要${action === 'block' ? '封锁' : '解封'}${record.instanceId}】吗?`, '提示', {
confirmButtonText: '确定',
confirmButtonClass: 'el-button--danger',
type: 'warning'
}).then(async () => {
const res = await operateInstance({
action,
clusterId,
vendorId,
instanceIps
})
if (res.success) {
this.$message.success(res.message)
this.getData()
}
})
},
creatInstance() {
this.$router.push({ name: 'tceInstanceAdd', query: { clusterId: this.detail.id, vendorId: this.detail.vendorId } })
},
addExistInstance() {
this.addExistInstanceData = {
dialog: true,
data: {
name: this.detail.name,
id: this.detail.clusterId,
instances: []
}
}
},
drain(row) {
this.drainData = {
dialog: true,
data: {
clusterId: this.detail.id,
instanceId: row.instanceId
}
}
},
handleSelectChange(selection) {
this.selectionIds = selection.map(item => item.id)
this.selection = selection
},
handleDelete() {
const arr = this.selection.map(item => item.instanceId)
this.remove(arr)
},
remove(instanceId) {
this.deleteData = {
dialog: true,
data: {
vendorId: this.detail.vendorId,
id: this.detail.id,
instanceId: instanceId,
mode: false
}
}
},
goBack() {
this.$emit('close')
},
getData() {
this.loading = true
const params = {
page: this.params.page,
rows: this.params.rows,
params: JSON.stringify([{ param: { vendorId: this.detail.vendorId, clusterId: this.detail.clusterId }, sign: 'EQ' }])
}
getInstanceList(params).then(data => {
if (data.success) {
this.loading = false
this.tableData = data.data.rows
this.total = data.data.total
}
})
}
},
watch: {
detail: {
handler(newVal, oldVal) {
this.getData()
},
deep: true,
immediate: true
}
}
}
</script>

View File

@ -0,0 +1,75 @@
<template>
<div>
<cb-advance-table title="" :data="data" :params="params" :columns="columns" :get-list="getList" :total="total" :loading="loading" :search-configs="searchConfigs"> </cb-advance-table>
</div>
</template>
<script>
import { getLogList } from 'views/tce/services/tke.js'
const columns = [
{
label: 'ID',
prop: 'clusterId',
disabled: true
},
{
label: '名称',
prop: 'name',
disabled: true
},
{
label: '状态',
prop: ''
},
{
label: '类型',
prop: 'inputType'
},
{
label: '创建时间',
prop: 'gmtCreate'
},
{
label: '集群',
prop: 'clusterName'
}
]
export default {
props: ['vendorId'],
components: {},
data() {
return {
columns,
loading: false,
params: {
page: 1,
rows: 10
},
data: [],
total: 0,
searchConfigs: [{ type: 'Const', value: 'vendorId', initValue: this.vendorId }]
}
},
methods: {
getList() {
this.loading = true
getLogList(this.params).then(res => {
this.loading = false
if (res.success) {
this.data = res.data.rows
this.total = res.data.total
}
})
}
},
watch: {
vendorId: {
handler(newVal, oldVal) {
this.getList()
},
deep: true,
immediate: true
}
}
}
</script>

View File

@ -0,0 +1,67 @@
<template>
<div>
<el-dialog title="编辑集群" width="40%" :close-on-click-modal="false" v-if="addData.dialog" :visible.sync="addData.dialog">
<cb-form :model="addData.data" ref="data">
<el-row :gutter="10">
<el-col :span="24">
<cb-form-item label="名称:" prop="name" validate="required" required-message="" maxlength="60">
<el-input v-model="addData.data.name"></el-input>
</cb-form-item>
</el-col>
</el-row>
</cb-form>
<div slot="footer" class="dialog-footer">
<el-button type="ghost" @click.native="addData.dialog = false">取消</el-button>
<el-button type="primary" @click.native="ok">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
/* global $ */
import { modifyInstance } from 'views/tce/services/tke.js'
export default {
props: {
addData: {
type: Object,
default: function () {
return {
data: {
name: '',
vendorId: -1
},
dialog: false
}
}
},
vendorId: {
type: Number,
default: 0
}
},
data() {
return {}
},
methods: {
ok() {
this.$refs.data.validate(valid => {
if (valid) {
const param = {
id: this.addData.data.id,
newName: this.addData.data.name
}
modifyInstance(param).then(data => {
if (data.success) {
this.$message.success(data.message)
this.$emit('back')
this.addData.dialog = false
}
})
}
})
}
},
created() {}
}
</script>

View File

@ -0,0 +1,67 @@
<template>
<div>
<cb-advance-table :data="tableData" :params="params" :columns="columns" :get-list="getData" :total="total" :loading="loading">
<template #instanceState="val">
<cb-status-icon :type="val | tceInstanceColor">{{ val | tceInstanceText }}</cb-status-icon>
</template>
<div slot="pagination"></div>
</cb-advance-table>
</div>
</template>
<script>
import { getTKERegions } from 'views/tce/services/tke.js'
const columns = [
{
label: '名称',
prop: 'regionName'
}
]
export default {
props: {
detail: {
type: Object
},
platformObject: {
type: Object
}
},
name: '',
data() {
return {
columns,
params: {
page: 1,
rows: 10
},
total: 0,
tableData: [],
loading: false
}
},
methods: {
goBack() {
this.$emit('close')
},
getData() {
getTKERegions({
tenantUuid: this.platformObject.tceTenantUuid,
vendorId: this.detail.vendorId
}).then(data => {
if (data.success) {
this.tableData = data.data.regionInstanceSet
}
})
}
},
watch: {
detail: {
handler(newVal, oldVal) {
this.getData()
},
deep: true,
immediate: true
}
}
}
</script>

View File

@ -0,0 +1,79 @@
<template>
<div>
<cb-advance-table :data="tableData" :params="params" :columns="columns" :get-list="getData" :total="total" :loading="loading">
<template #instanceState="val">
<cb-status-icon :type="val | tceInstanceColor">{{ val | tceInstanceText }}</cb-status-icon>
</template>
</cb-advance-table>
</div>
</template>
<script>
import { getsecrectList } from 'views/tce/services/tke.js'
const columns = [
{
label: '名称',
prop: 'name'
},
{
label: '类型',
prop: 'type'
},
{
label: 'lables',
prop: 'lables'
},
{
label: '创建时间',
prop: 'creationTimestamp'
}
]
export default {
props: {
detail: {
type: Object
}
},
name: '',
data() {
return {
columns,
params: {
page: 1,
rows: 10
},
total: 0,
tableData: [],
loading: false
}
},
methods: {
goBack() {
this.$emit('close')
},
getData() {
const params = {
page: this.params.page,
rows: this.params.rows,
params: JSON.stringify([{ param: { vendorId: this.detail.vendorId, clusterId: this.detail.clusterId }, sign: 'EQ' }])
}
getsecrectList(params).then(data => {
if (data.success) {
this.loading = false
this.tableData = data.data.rows
this.total = data.data.total
}
})
}
},
watch: {
detail: {
handler(newVal, oldVal) {
this.getData()
},
deep: true,
immediate: true
}
}
}
</script>

View File

@ -0,0 +1,89 @@
<template>
<div>
<cb-detail :title="clusterInfo.name + ' 服务'" @goBack="goBack">
<template v-slot:item_container>
<cb-advance-table :data="tableData" :params="params" :columns="columns" :get-list="getData" :total="total" :loading="loading">
<template v-slot:action> </template>
<template #instanceState="val">
<cb-status-icon :type="val | tceInstanceColor">{{ val | tceInstanceText }}</cb-status-icon>
</template>
<template #operate="val, record">
<div class="action-divider"></div>
<el-button type="text" @click="remove(record.id)" :disabled="record.status === 'BIND'"> 删除 </el-button>
<div class="action-divider"></div>
</template>
</cb-advance-table>
</template>
</cb-detail>
</div>
</template>
<script>
import { getServiceList } from 'views/tce/services/tke.js'
const columns = [
{
label: '名称',
prop: 'serviceName'
},
{
label: '类型',
prop: 'type'
},
{
label: 'ip',
prop: 'ipAddress'
},
{
label: '创建时间',
prop: 'createTime'
}
]
export default {
props: ['vendorId', 'clusterInfo'],
name: '',
data() {
return {
searchConfigs: [{ type: 'Input', label: '名称', value: 'serviceName' }],
searchData: {
serviceName: ''
},
columns,
params: {
page: 1,
rows: 10
},
total: 0,
tableData: [],
loading: false
}
},
methods: {
goBack() {
this.$emit('close')
},
getData() {
const params = {
page: this.params.page,
rows: this.params.rows,
params: JSON.stringify([{ param: { vendorId: this.vendorId, clusterId: this.clusterInfo.clusterId }, sign: 'EQ' }])
}
getServiceList(params).then(data => {
if (data.success) {
this.loading = false
this.tableData = data.data.rows
this.total = data.data.total
}
})
}
},
watch: {
clusterInfo: {
handler(newVal, oldVal) {
this.getData()
},
deep: true,
immediate: true
}
}
}
</script>

View File

@ -11,5 +11,7 @@ export default {
ctstackKey: () => import('views/resource/ctstack/page/secretKey.vue'),
ctstackServer: () => import('views/resource/ctstack/page/serverAdd.vue'),
ctstackFloatIp: () => import('views/resource/ctstack/page/floatIp/index.vue'),
ctstackSnapshot: () => import('views/resource/ctstack/page/snapshot/index.vue')
ctstackSnapshot: () => import('views/resource/ctstack/page/snapshot/index.vue'),
ctstackContainer: () => import('views/resource/ctstack/page/container/index.vue'),
ctstackCreateCluster: () => import('views/resource/ctstack/page/container/creatCluster/index.vue')
}

View File

@ -0,0 +1,159 @@
import { request } from '@cmp/cmp-element'
import { wrapperParams, downloadFile } from 'utils'
const createCluterUrl = '/cmp/plugins/tce/v1/tke'
export function createCluter(params) {
return request.post(createCluterUrl, wrapperParams(params))
}
const describeVersionsUrl = '/cmp/plugins/tce/v1/tke/describeVersions'
export function getDescribeVersions(params) {
return request.get(`${describeVersionsUrl}`, { params })
}
// 集群 查询地域
const regionsUrl = '/cmp/plugins/tce/v1/tke/describe/regions'
export function getTKERegions(params) {
return request.get(regionsUrl, {
params
})
}
// 集群 创建 查询镜像信息
const createCluterImagesUrl = '/cmp/plugins/tce/v1/tke/describe/images'
export function createCluterImages(params) {
return request.get(createCluterImagesUrl, {
params
})
}
// 集群 创建 校验CIDR
const checkCIDRUrl = '/cmp/plugins/tce/v1/tke/check/CIDR'
export function checkCIDR(params) {
return request.get(checkCIDRUrl, {
params
})
}
const clusterListUrl = '/cmp/plugins/tce/v1/tke'
// 获取伸缩组
export function getFlexibleList(params) {
return request.get(`${clusterListUrl}/group/list`, {
params
})
}
// 删除伸缩组
export function delectFlexible(params) {
return request.get(`${clusterListUrl}/group/del/${params.id}`, {
params
})
}
// 修改伸缩组配置
export function editFlexibleAllocation(params) {
return request.post(`${clusterListUrl}/group/update/${params.id}`, wrapperParams(params))
}
// 查询全局配置
export function getWholeAllocation(params) {
return request.get(`${clusterListUrl}/option/list`, {
params
})
}
export function editWholeAllocation(params) {
return request.post(`${clusterListUrl}/option/update/${params.id}`, wrapperParams(params))
}
export function getClusterDetail(id) {
return request.get(`${clusterListUrl}/${id}`)
}
export function postAddInstance(params) {
return request.post(`${clusterListUrl}/add/instance`, wrapperParams(params))
}
// 集群 节点 创建
const createInstanceUrl = '/cmp/plugins/tce/v1/tke/instance '
export function createInstance(params) {
return request.post(createInstanceUrl, wrapperParams(params))
}
// 集群 已存在节点
const existInstanceUrl = '/cmp/plugins/tce/v1/tke/exist/instance'
export function getExistInstance(params, id) {
return request.get(`${existInstanceUrl}/${params.vendorId}/${id}`, {
params
})
}
// 集群 节点
const instanceListUrl = '/cmp/plugins/tce/v1/tke/instance'
export function getInstanceList(params) {
return request.get(instanceListUrl, {
params
})
}
// 集群 节点 删除
export function deleteInstance(params, data) {
return request.delete(`${instanceListUrl}/${params.vendorId}/${params.id}`, {
data: data
})
}
// 集群 节点 驱逐
const drainUrl = '/cmp/plugins/tce/v1/tke/drain'
export function drain(params) {
return request.delete(`${drainUrl}/${params.vendorId}/${params.id}`, {
data: { instanceId: params.instanceId }
})
}
export function operateInstance(params) {
return request.post('/cmp/plugins/tce/v1/tke/block/operation', wrapperParams(params))
}
const secrectListUrl = '/cmp/plugins/tce/v1/tke/cluster'
export function getsecrectList(params) {
return request.get(secrectListUrl, {
params
})
}
// 集群 服务
const serviceListUrl = '/cmp/plugins/tce/v1/tke/service'
export function getServiceList(params) {
return request.get(serviceListUrl, {
params
})
}
// 集群 编辑
export function modifyInstance({ id, newName }) {
return request.put(`${clusterListUrl}/${id}`, {
newName
})
}
// 集群 删除
export function deleteCluster(params, mode) {
const formData = new FormData()
formData.append('mode', mode)
return request.delete(`${clusterListUrl}/${params.id}`, {
data: formData
})
}
// 容器服务 日志列表
export function getLogList(params) {
return request.get('/cmp/plugins/tce/v1/tke/logCollectorController', { params })
}
export function getClusterList(params) {
return request.get(clusterListUrl, {
params
})
}

View File

@ -22,7 +22,7 @@ export default {
const leftConfigs = ref([])
const rightConfigs = ref([])
const loading = ref(true)
const leftKeys = ['企业微信配置', '钉钉配置', 'LDAP配置']
const leftKeys = ['企业微信配置', '钉钉配置', 'LDAP配置', '邮箱配置', '短信配置']
async function getConfigs() {
loading.value = true
const res = await getSystemTreeConfigs({ category: '系统对接' })
@ -39,7 +39,7 @@ export default {
}
getConfigs()
const showTest = name => {
return !['短信配置', '阵地信息', '大屏展示配置'].includes(name)
return !['短信配置', '阵地信息', '大屏展示配置', '运控系统地址'].includes(name)
}
return {
leftConfigs,