mirror of
https://github.com/apache/cloudstack.git
synced 2025-11-02 11:52:28 +01:00
compute: add CKS support (#247)
Adds CKS UI support in Primate. Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
0aeda824ee
commit
0b8867e6d5
66
ui/src/assets/icons/kubernetes.svg
Normal file
66
ui/src/assets/icons/kubernetes.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 9.8 KiB |
@ -24,7 +24,8 @@
|
||||
<div class="avatar">
|
||||
<slot name="avatar">
|
||||
<os-logo v-if="resource.ostypeid || resource.ostypename" :osId="resource.ostypeid" :osName="resource.ostypename" size="4x" />
|
||||
<a-icon v-else style="font-size: 36px" :type="$route.meta.icon" />
|
||||
<a-icon v-else-if="typeof $route.meta.icon ==='string'" style="font-size: 36px" :type="$route.meta.icon" />
|
||||
<a-icon v-else style="font-size: 36px" :component="$route.meta.icon" />
|
||||
</slot>
|
||||
</div>
|
||||
<slot name="name">
|
||||
|
||||
@ -15,6 +15,8 @@
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
import kubernetes from '@/assets/icons/kubernetes.svg?inline'
|
||||
|
||||
export default {
|
||||
name: 'compute',
|
||||
title: 'Compute',
|
||||
@ -328,15 +330,74 @@ export default {
|
||||
}
|
||||
]
|
||||
},
|
||||
/*
|
||||
{
|
||||
name: 'demo',
|
||||
title: 'Demo',
|
||||
icon: 'radar-chart',
|
||||
permission: [ 'listVirtualMachines' ],
|
||||
component: () => import('@/components/Test.vue')
|
||||
name: 'kubernetes',
|
||||
title: 'Kubernetes',
|
||||
icon: kubernetes,
|
||||
permission: ['listKubernetesClusters'],
|
||||
columns: ['name', 'state', 'size', 'cpunumber', 'memory', 'account', 'zonename'],
|
||||
details: ['name', 'description', 'zonename', 'kubernetesversionname', 'size', 'masternodes', 'cpunumber', 'memory', 'keypair', 'associatednetworkname', 'account', 'domain', 'zonename'],
|
||||
tabs: [{
|
||||
name: 'k8s',
|
||||
component: () => import('@/views/compute/KubernetesServiceTab.vue')
|
||||
}],
|
||||
actions: [
|
||||
{
|
||||
api: 'createKubernetesCluster',
|
||||
icon: 'plus',
|
||||
label: 'Create Kubernetes Cluster',
|
||||
listView: true,
|
||||
popup: true,
|
||||
component: () => import('@/views/compute/CreateKubernetesCluster.vue')
|
||||
},
|
||||
{
|
||||
api: 'startKubernetesCluster',
|
||||
icon: 'caret-right',
|
||||
label: 'Start Kubernetes Cluster',
|
||||
dataView: true,
|
||||
show: (record) => { return ['Stopped'].includes(record.state) }
|
||||
},
|
||||
{
|
||||
api: 'stopKubernetesCluster',
|
||||
icon: 'stop',
|
||||
label: 'Stop Kubernetes Cluster',
|
||||
dataView: true,
|
||||
show: (record) => { return !['Stopped'].includes(record.state) }
|
||||
},
|
||||
// {
|
||||
// api: 'getKubernetesClusterConfig',
|
||||
// icon: 'cloud-download',
|
||||
// label: 'Download Cluster Config',
|
||||
// dataView: true,
|
||||
// show: (record) => { return !['Stopped'].includes(record.state) }
|
||||
// },
|
||||
{
|
||||
api: 'scaleKubernetesCluster',
|
||||
icon: 'swap',
|
||||
label: 'Scale Kubernetes Cluster',
|
||||
dataView: true,
|
||||
show: (record) => { return ['Created', 'Running'].includes(record.state) },
|
||||
popup: true,
|
||||
component: () => import('@/views/compute/ScaleKubernetesCluster.vue')
|
||||
},
|
||||
{
|
||||
api: 'upgradeKubernetesCluster',
|
||||
icon: 'plus-circle',
|
||||
label: 'Upgrade Kubernetes Cluster',
|
||||
dataView: true,
|
||||
show: (record) => { return ['Created', 'Running'].includes(record.state) },
|
||||
popup: true,
|
||||
component: () => import('@/views/compute/UpgradeKubernetesCluster.vue')
|
||||
},
|
||||
{
|
||||
api: 'deleteKubernetesCluster',
|
||||
icon: 'delete',
|
||||
label: 'Delete Kubernetes Cluster',
|
||||
dataView: true,
|
||||
show: (record) => { return !['Destroyed', 'Destroying'].includes(record.state) }
|
||||
}
|
||||
]
|
||||
},
|
||||
*/
|
||||
{
|
||||
name: 'vmgroup',
|
||||
title: 'Instance Groups',
|
||||
|
||||
@ -15,6 +15,8 @@
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
import kubernetes from '@/assets/icons/kubernetes.svg?inline'
|
||||
|
||||
export default {
|
||||
name: 'image',
|
||||
title: 'Images',
|
||||
@ -196,6 +198,38 @@ export default {
|
||||
groupAction: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'kubernetesiso',
|
||||
title: 'Kubernetes ISOs',
|
||||
icon: kubernetes,
|
||||
permission: ['listKubernetesSupportedVersions'],
|
||||
columns: ['name', 'state', 'semanticversion', 'isostate', 'mincpunumber', 'minmemory', 'zonename'],
|
||||
details: ['name', 'semanticversion', 'zoneid', 'zonename', 'isoid', 'isoname', 'isostate', 'mincpunumber', 'minmemory', 'supportsha', 'state'],
|
||||
actions: [
|
||||
{
|
||||
api: 'addKubernetesSupportedVersion',
|
||||
icon: 'plus',
|
||||
label: 'Add Kubernetes Version',
|
||||
listView: true,
|
||||
popup: true,
|
||||
component: () => import('@/views/image/AddKubernetesSupportedVersion.vue')
|
||||
},
|
||||
{
|
||||
api: 'updateKubernetesSupportedVersion',
|
||||
icon: 'edit',
|
||||
label: 'Update Kuberntes Version',
|
||||
dataView: true,
|
||||
popup: true,
|
||||
component: () => import('@/views/image/UpdateKubernetesSupportedVersion.vue')
|
||||
},
|
||||
{
|
||||
api: 'deleteKubernetesSupportedVersion',
|
||||
icon: 'delete',
|
||||
label: 'Delete Kubernetes Version',
|
||||
dataView: true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -57,6 +57,7 @@
|
||||
"Virtual Routers": "Virtual Routers",
|
||||
"Volumes": "Volumes",
|
||||
"Zones": "Zones",
|
||||
"access": "Access",
|
||||
"accesskey": "Access Key",
|
||||
"account": "Account",
|
||||
"accountId": "Account",
|
||||
@ -114,9 +115,10 @@
|
||||
"certificate": "Certificate",
|
||||
"certificateid": "Certificate ID",
|
||||
"chassis": "Chassis",
|
||||
"checksum": "checksum",
|
||||
"checksum": "Checksum",
|
||||
"cidr": "Super CIDR for Guest Networks",
|
||||
"cidrlist": "CIDR list",
|
||||
"cks.cluster.size": "Cluster size (Worker nodes)",
|
||||
"cleanup": "Clean up",
|
||||
"clusterId": "Cluster",
|
||||
"clusterid": "Cluster",
|
||||
@ -209,9 +211,11 @@
|
||||
"esplifetime": "ESP Lifetime (second)",
|
||||
"esppolicy": "ESP policy",
|
||||
"expunge": "Expunge",
|
||||
"externalloadbalanceripaddress": "External load balancer IP address",
|
||||
"externalid": "External Id",
|
||||
"extra": "Extra Arguments",
|
||||
"fingerprint": "FingerPrint",
|
||||
"firewall": "Firewall",
|
||||
"firstname": "First Name",
|
||||
"forced": "Force Stop",
|
||||
"forceencap": "Force UDP Encapsulation of ESP Packets",
|
||||
@ -321,11 +325,13 @@
|
||||
"isextractable": "Extractable",
|
||||
"isfeatured": "Featured",
|
||||
"iso": "ISO",
|
||||
"isoid": "ISO",
|
||||
"isolatedpvlantype": "Secondary Isolated VLAN Type",
|
||||
"isolatedpvlanid": "Secondary Isolated VLAN ID",
|
||||
"isolationmethods": "Isolation method",
|
||||
"isolationuri": "Isolation URI",
|
||||
"isoname": "Attached ISO",
|
||||
"isostate": "ISO State",
|
||||
"ispersistent": "Persistent ",
|
||||
"isportable": "Cross Zones",
|
||||
"ispublic": "Public",
|
||||
@ -340,6 +346,8 @@
|
||||
"key": "Key",
|
||||
"keyboardType": "Keyboard type",
|
||||
"keypair": "SSH Key Pair",
|
||||
"kubernetesversionid": "Kubernetes version",
|
||||
"kubernetesversionname": "Kubernetes version",
|
||||
"kvmnetworklabel": "KVM Traffic Label",
|
||||
"l2gatewayserviceuuid": "L2 Gateway Service Uuid",
|
||||
"l3gatewayserviceuuid": "L3 Gateway Service Uuid",
|
||||
@ -681,6 +689,7 @@
|
||||
"limitcpuuse": "CPU Cap",
|
||||
"linklocalip": "Link Local IP Address",
|
||||
"loadbalancerinstance": "Assigned VMs",
|
||||
"loadbalancing": "Load Balancing",
|
||||
"loadbalancerrule": "Load balancing rule",
|
||||
"localstorageenabled": "Enable local storage for User VMs",
|
||||
"localstorageenabledforsystemvm": "Enable local storage for System VMs",
|
||||
@ -691,6 +700,7 @@
|
||||
"makeredundant": "Make redundant",
|
||||
"managedstate": "Managed State",
|
||||
"managementServers": "Number of Management Servers",
|
||||
"masternodes": "Master nodes",
|
||||
"maxuser_vm": "Max. user VMs",
|
||||
"maxpublic_ip": "Max. public IPs",
|
||||
"maxvolume": "Max. volumes",
|
||||
@ -728,10 +738,10 @@
|
||||
"message.network.removeNIC": "Please confirm that want to remove this NIC, which will also remove the associated network from the VM.",
|
||||
"message.network.secondaryIP" : "Please confirm that you would like to acquire a new secondary IP for this NIC. \n NOTE: You need to manually configure the newly-acquired secondary IP inside the virtual machine.",
|
||||
"message.network.updateIp": "Please confirm that you would like to change the IP address for this NIC on VM.",
|
||||
"minCPUNumber": "Min CPU Cores",
|
||||
"mincpunumber": "Min CPU Cores",
|
||||
"minInstance": "Min Instances",
|
||||
"minIops": "Min IOPS",
|
||||
"minMemory": "Min Memory (in MB)",
|
||||
"minmemory": "Min Memory (in MB)",
|
||||
"min_balance": "Min Balance",
|
||||
"miniops": "Min IOPS",
|
||||
"name": "Name",
|
||||
@ -766,6 +776,7 @@
|
||||
"nfsCacheZoneid": "Zone",
|
||||
"nfsServer": "NFS Server",
|
||||
"nicAdapterType": "NIC adapter type",
|
||||
"noderootdisksize": "Node root disk size (in GB)",
|
||||
"number": "#Rule",
|
||||
"numberOfRouterRequiresUpgrade": "Total of Virtual Routers that require upgrade",
|
||||
"numretries": "Number of Retries",
|
||||
@ -806,6 +817,7 @@
|
||||
"podname": "Pod name",
|
||||
"port": "Port",
|
||||
"portableipaddress": "Portable IPs",
|
||||
"portforwarding": "Port Forwarding",
|
||||
"powerstate": "Power State",
|
||||
"primaryStorageLimit": "Primary Storage limits (GiB)",
|
||||
"primarystoragetotal": "Primary Storage",
|
||||
@ -889,6 +901,7 @@
|
||||
"securityGroups": "Security Groups",
|
||||
"securitygroup": "Security Group",
|
||||
"select": "Select",
|
||||
"semanticversion": "Semantic version",
|
||||
"sent": "Date",
|
||||
"sentbytes": "Bytes Sent",
|
||||
"server": "Server",
|
||||
|
||||
442
ui/src/views/compute/CreateKubernetesCluster.vue
Normal file
442
ui/src/views/compute/CreateKubernetesCluster.vue
Normal file
@ -0,0 +1,442 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
<template>
|
||||
<div class="form-layout">
|
||||
<a-spin :spinning="loading">
|
||||
<a-form
|
||||
:form="form"
|
||||
@submit="handleSubmit"
|
||||
layout="vertical">
|
||||
<a-form-item :label="$t('name')">
|
||||
<a-input
|
||||
v-decorator="['name', {
|
||||
rules: [{ required: true, message: 'Please enter Kubernetes cluster name' }]
|
||||
}]"
|
||||
:placeholder="apiParams.name.description"/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('description')">
|
||||
<a-input
|
||||
v-decorator="['description', {
|
||||
rules: [{ message: 'Please enter name' }]
|
||||
}]"
|
||||
:placeholder="apiParams.description.description"/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('zoneid')">
|
||||
<a-select
|
||||
id="zone-selection"
|
||||
v-decorator="['zoneid', {
|
||||
rules: [{ required: true }]
|
||||
}]"
|
||||
showSearch
|
||||
optionFilterProp="children"
|
||||
:filterOption="(input, option) => {
|
||||
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
:loading="zoneLoading"
|
||||
:placeholder="apiParams.zoneid.description"
|
||||
@change="val => { this.handleZoneChanged(this.zones[val]) }">
|
||||
<a-select-option v-for="(opt, optIndex) in this.zones" :key="optIndex">
|
||||
{{ opt.name || opt.description }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('kubernetesversionid')">
|
||||
<a-select
|
||||
id="version-selection"
|
||||
v-decorator="['kubernetesversionid', {
|
||||
rules: [{ required: true }]
|
||||
}]"
|
||||
showSearch
|
||||
optionFilterProp="children"
|
||||
:filterOption="(input, option) => {
|
||||
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
:loading="kubernetesVersionLoading"
|
||||
:placeholder="apiParams.kubernetesversionid.description"
|
||||
@change="val => { this.handleKubernetesVersionChange(this.kubernetesVersions[val]) }">
|
||||
<a-select-option v-for="(opt, optIndex) in this.kubernetesVersions" :key="optIndex">
|
||||
{{ opt.name || opt.description }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('serviceofferingid')">
|
||||
<a-select
|
||||
id="offering-selection"
|
||||
v-decorator="['serviceofferingid', {
|
||||
rules: [{ required: true }]
|
||||
}]"
|
||||
showSearch
|
||||
optionFilterProp="children"
|
||||
:filterOption="(input, option) => {
|
||||
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
:loading="serviceOfferingLoading"
|
||||
:placeholder="apiParams.serviceofferingid.description">
|
||||
<a-select-option v-for="(opt, optIndex) in this.serviceOfferings" :key="optIndex">
|
||||
{{ opt.name || opt.description }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('noderootdisksize')">
|
||||
<a-input
|
||||
v-decorator="['noderootdisksize', {
|
||||
rules: [{
|
||||
validator: (rule, value, callback) => {
|
||||
if (value && (isNaN(value) || value <= 0)) {
|
||||
callback('Please enter a valid number')
|
||||
}
|
||||
callback()
|
||||
}
|
||||
}]
|
||||
}]"
|
||||
:placeholder="apiParams.noderootdisksize.description"/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('networkid')">
|
||||
<a-select
|
||||
id="network-selection"
|
||||
v-decorator="['networkid', {}]"
|
||||
showSearch
|
||||
optionFilterProp="children"
|
||||
:filterOption="(input, option) => {
|
||||
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
:loading="networkLoading"
|
||||
:placeholder="apiParams.networkid.description">
|
||||
<a-select-option v-for="(opt, optIndex) in this.networks" :key="optIndex">
|
||||
{{ opt.name || opt.description }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('haenable')" v-if="this.selectedKubernetesVersion != null && this.selectedKubernetesVersion != undefined && this.selectedKubernetesVersion.supportsha === true">
|
||||
<a-switch v-decorator="['haenable', {initialValue: this.haEnabled}]" :checked="this.haEnabled" @change="val => { this.haEnabled = val }" />
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('masternodes')" v-if="this.haEnabled">
|
||||
<a-input
|
||||
v-decorator="['masternodes', {
|
||||
initialValue: '1',
|
||||
rules: [{ required: true, message: 'Please enter value' },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (value && (isNaN(value) || value <= 0)) {
|
||||
callback('Please enter a valid number')
|
||||
}
|
||||
callback()
|
||||
}
|
||||
}
|
||||
]
|
||||
}]"
|
||||
:placeholder="apiParams.masternodes.description"/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('externalloadbalanceripaddress')" v-if="this.haEnabled">
|
||||
<a-input
|
||||
v-decorator="['externalloadbalanceripaddress', {}]"
|
||||
:placeholder="apiParams.externalloadbalanceripaddress.description"/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('cks.cluster.size')">
|
||||
<a-input
|
||||
v-decorator="['size', {
|
||||
initialValue: '1',
|
||||
rules: [{ required: true, message: 'Please enter value' },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (value && (isNaN(value) || value <= 0)) {
|
||||
callback('Please enter a valid number')
|
||||
}
|
||||
callback()
|
||||
}
|
||||
}
|
||||
]
|
||||
}]"
|
||||
:placeholder="apiParams.size.description"/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('keypair')">
|
||||
<a-select
|
||||
id="keypair-selection"
|
||||
v-decorator="['keypair', {}]"
|
||||
showSearch
|
||||
optionFilterProp="children"
|
||||
:filterOption="(input, option) => {
|
||||
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
:loading="keyPairLoading"
|
||||
:placeholder="apiParams.keypair.description">
|
||||
<a-select-option v-for="(opt, optIndex) in this.keyPairs" :key="optIndex">
|
||||
{{ opt.name || opt.description }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<div :span="24" class="action-button">
|
||||
<a-button @click="closeAction">{{ this.$t('Cancel') }}</a-button>
|
||||
<a-button :loading="loading" type="primary" @click="handleSubmit">{{ this.$t('OK') }}</a-button>
|
||||
</div>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
|
||||
export default {
|
||||
name: 'CreateKubernetesCluster',
|
||||
props: {},
|
||||
data () {
|
||||
return {
|
||||
zones: [],
|
||||
zoneLoading: false,
|
||||
selectedZone: {},
|
||||
kubernetesVersions: [],
|
||||
kubernetesVersionLoading: false,
|
||||
selectedKubernetesVersion: {},
|
||||
serviceOfferings: [],
|
||||
serviceOfferingLoading: false,
|
||||
networks: [],
|
||||
networkLoading: false,
|
||||
keyPairs: [],
|
||||
keyPairLoading: false,
|
||||
haEnabled: false,
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
this.form = this.$form.createForm(this)
|
||||
this.apiConfig = this.$store.getters.apis.createKubernetesCluster || {}
|
||||
this.apiParams = {}
|
||||
this.apiConfig.params.forEach(param => {
|
||||
this.apiParams[param.name] = param
|
||||
})
|
||||
},
|
||||
created () {
|
||||
this.networks = [
|
||||
{
|
||||
id: null,
|
||||
name: ''
|
||||
}
|
||||
]
|
||||
this.keyPairs = [
|
||||
{
|
||||
id: null,
|
||||
name: ''
|
||||
}
|
||||
]
|
||||
},
|
||||
mounted () {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
fetchData () {
|
||||
this.fetchZoneData()
|
||||
this.fetchNetworkData()
|
||||
this.fetchKeyPairData()
|
||||
},
|
||||
isValidValueForKey (obj, key) {
|
||||
return key in obj && obj[key] != null
|
||||
},
|
||||
arrayHasItems (array) {
|
||||
return array !== null && array !== undefined && Array.isArray(array) && array.length > 0
|
||||
},
|
||||
isObjectEmpty (obj) {
|
||||
return !(obj !== null && obj !== undefined && Object.keys(obj).length > 0 && obj.constructor === Object)
|
||||
},
|
||||
fetchZoneData () {
|
||||
const params = {}
|
||||
this.zoneLoading = true
|
||||
api('listZones', params).then(json => {
|
||||
const listZones = json.listzonesresponse.zone
|
||||
this.zones = this.zones.concat(listZones)
|
||||
}).finally(() => {
|
||||
this.zoneLoading = false
|
||||
if (this.arrayHasItems(this.zones)) {
|
||||
this.form.setFieldsValue({
|
||||
zoneid: 0
|
||||
})
|
||||
this.handleZoneChange(this.zones[0])
|
||||
}
|
||||
})
|
||||
},
|
||||
handleZoneChange (zone) {
|
||||
this.selectedZone = zone
|
||||
this.fetchKubernetesVersionData()
|
||||
},
|
||||
fetchKubernetesVersionData () {
|
||||
this.kubernetesVersions = []
|
||||
const params = {}
|
||||
if (!this.isObjectEmpty(this.selectedZone)) {
|
||||
params.zoneid = this.selectedZone.id
|
||||
}
|
||||
this.kubernetesVersionLoading = true
|
||||
api('listKubernetesSupportedVersions', params).then(json => {
|
||||
const versionObjs = json.listkubernetessupportedversionsresponse.kubernetessupportedversion
|
||||
if (this.arrayHasItems(versionObjs)) {
|
||||
for (var i = 0; i < versionObjs.length; i++) {
|
||||
if (versionObjs[i].state === 'Enabled' && versionObjs[i].isostate === 'Ready') {
|
||||
this.kubernetesVersions.push(versionObjs[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}).finally(() => {
|
||||
this.kubernetesVersionLoading = false
|
||||
if (this.arrayHasItems(this.kubernetesVersions)) {
|
||||
this.form.setFieldsValue({
|
||||
kubernetesversionid: 0
|
||||
})
|
||||
this.handleKubernetesVersionChange(this.kubernetesVersions[0])
|
||||
}
|
||||
})
|
||||
},
|
||||
handleKubernetesVersionChange (version) {
|
||||
this.selectedKubernetesVersion = version
|
||||
this.fetchServiceOfferingData()
|
||||
},
|
||||
fetchServiceOfferingData () {
|
||||
this.serviceOfferings = []
|
||||
const params = {}
|
||||
this.serviceOfferingLoading = true
|
||||
api('listServiceOfferings', params).then(json => {
|
||||
var items = json.listserviceofferingsresponse.serviceoffering
|
||||
var minCpu = 2
|
||||
var minMemory = 2048
|
||||
if (!this.isObjectEmpty(this.selectedKubernetesVersion)) {
|
||||
minCpu = this.selectedKubernetesVersion.mincpunumber
|
||||
minMemory = this.selectedKubernetesVersion.minmemory
|
||||
}
|
||||
if (items != null) {
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (items[i].iscustomized === false &&
|
||||
items[i].cpunumber >= minCpu && items[i].memory >= minMemory) {
|
||||
this.serviceOfferings.push(items[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}).finally(() => {
|
||||
this.serviceOfferingLoading = false
|
||||
if (this.arrayHasItems(this.serviceOfferings)) {
|
||||
this.form.setFieldsValue({
|
||||
serviceofferingid: 0
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
fetchNetworkData () {
|
||||
const params = {}
|
||||
this.networkLoading = true
|
||||
api('listNetworks', params).then(json => {
|
||||
const listNetworks = json.listnetworksresponse.network
|
||||
if (this.arrayHasItems(listNetworks)) {
|
||||
this.networks = this.networks.concat(listNetworks)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.networkLoading = false
|
||||
if (this.arrayHasItems(this.networks)) {
|
||||
this.form.setFieldsValue({
|
||||
networkid: 0
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
fetchKeyPairData () {
|
||||
const params = {}
|
||||
this.keyPairLoading = true
|
||||
api('listSSHKeyPairs', params).then(json => {
|
||||
const listKeyPairs = json.listsshkeypairsresponse.sshkeypair
|
||||
if (this.arrayHasItems(listKeyPairs)) {
|
||||
for (var i = 0; i < listKeyPairs.length; i++) {
|
||||
this.keyPairs.push({
|
||||
id: listKeyPairs[i].name,
|
||||
description: listKeyPairs[i].name
|
||||
})
|
||||
}
|
||||
}
|
||||
}).finally(() => {
|
||||
this.keyPairLoading = false
|
||||
if (this.arrayHasItems(this.keyPairs)) {
|
||||
this.form.setFieldsValue({
|
||||
keypair: 0
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handleSubmit (e) {
|
||||
e.preventDefault()
|
||||
this.form.validateFields((err, values) => {
|
||||
if (err) {
|
||||
return
|
||||
}
|
||||
this.loading = true
|
||||
const params = {
|
||||
name: values.name,
|
||||
description: values.description,
|
||||
zoneid: this.zones[values.zoneid].id,
|
||||
kubernetesversionid: this.kubernetesVersions[values.kubernetesversionid].id,
|
||||
serviceofferingid: this.serviceOfferings[values.serviceofferingid].id,
|
||||
size: values.size
|
||||
}
|
||||
if (this.isValidValueForKey(values, 'noderootdisksize') && values.noderootdisksize > 0) {
|
||||
params.noderootdisksize = values.noderootdisksize
|
||||
}
|
||||
if (this.isValidValueForKey(values, 'masternodes') && values.masternodes > 0) {
|
||||
params.masternodes = values.masternodes
|
||||
}
|
||||
if (this.isValidValueForKey(values, 'externalloadbalanceripaddress') && values.externalloadbalanceripaddress !== '') {
|
||||
params.externalloadbalanceripaddress = values.externalloadbalanceripaddress
|
||||
}
|
||||
if (this.isValidValueForKey(values, 'networkid') && this.arrayHasItems(this.networks) && this.networks[values.networkid].id != null) {
|
||||
params.networkid = this.networks[values.networkid].id
|
||||
}
|
||||
if (this.isValidValueForKey(values, 'keypair') && this.arrayHasItems(this.keyPairs) && this.keyPairs[values.keypair].id != null) {
|
||||
params.keypair = this.keyPairs[values.keypair].id
|
||||
}
|
||||
api('createKubernetesCluster', params).then(json => {
|
||||
this.$message.success('Successfully created Kubernetes cluster: ' + values.name)
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: 'Request Failed',
|
||||
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
|
||||
})
|
||||
}).finally(() => {
|
||||
this.$emit('refresh-data')
|
||||
this.loading = false
|
||||
this.closeAction()
|
||||
})
|
||||
})
|
||||
},
|
||||
closeAction () {
|
||||
this.$emit('close-action')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.form-layout {
|
||||
width: 80vw;
|
||||
|
||||
@media (min-width: 700px) {
|
||||
width: 550px;
|
||||
}
|
||||
}
|
||||
|
||||
.action-button {
|
||||
text-align: right;
|
||||
|
||||
button {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
432
ui/src/views/compute/KubernetesServiceTab.vue
Normal file
432
ui/src/views/compute/KubernetesServiceTab.vue
Normal file
@ -0,0 +1,432 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
<template>
|
||||
<a-spin :spinning="networkLoading">
|
||||
<a-tabs
|
||||
:activeKey="currentTab"
|
||||
:tabPosition="device === 'tablet' || device === 'mobile' ? 'top' : 'left'"
|
||||
:animated="false"
|
||||
@change="handleChangeTab">
|
||||
<a-tab-pane :tab="$t('details')" key="details">
|
||||
<DetailsTab :resource="resource" :loading="loading" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane :tab="$t('access')" key="access">
|
||||
<a-card title="Kubernetes Cluster Config" :loading="this.versionLoading">
|
||||
<div v-if="this.clusterConfig !== ''">
|
||||
<a-textarea :value="this.clusterConfig" :rows="5" readonly />
|
||||
<div :span="24" class="action-button">
|
||||
<a-button @click="downloadKubernetesClusterConfig" type="primary">{{ this.$t('Download') }}</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p>Kubernetes cluster kubeconfig not available currently</p>
|
||||
</div>
|
||||
</a-card>
|
||||
<a-card title="Using CLI" :loading="this.versionLoading">
|
||||
<a-timeline>
|
||||
<a-timeline-item>
|
||||
<p>
|
||||
Download kubeconfig for the cluster<br><br>
|
||||
The <code>kubectl</code> command-line tool uses kubeconfig files to find the information it needs to choose a cluster and communicate with the API server of a cluster.
|
||||
</p>
|
||||
</a-timeline-item>
|
||||
<a-timeline-item>
|
||||
<p>
|
||||
Download <code>kubectl</code> tool for cluster's Kubernetes version<br><br>
|
||||
Linux: <a :href="this.kubectlLinuxLink">{{ this.kubectlLinuxLink }}</a><br>
|
||||
MacOS: <a :href="this.kubectlMacLink">{{ this.kubectlMacLink }}</a><br>
|
||||
Windows: <a :href="this.kubectlWindowsLink">{{ this.kubectlWindowsLink }}</a>
|
||||
</p>
|
||||
</a-timeline-item>
|
||||
<a-timeline-item>
|
||||
<p>
|
||||
Use <code>kubectl</code> and <code>kubeconfig</code> file to access cluster<br><br>
|
||||
<code><b>kubectl --kubeconfig /custom/path/kube.conf {COMMAND}</b></code><br><br>
|
||||
|
||||
<em>List pods</em><br>
|
||||
<code>kubectl --kubeconfig /custom/path/kube.conf get pods --all-namespaces</code><br>
|
||||
<em>List nodes</em><br>
|
||||
<code>kubectl --kubeconfig /custom/path/kube.conf get nodes --all-namespaces</code><br>
|
||||
<em>List services</em><br>
|
||||
<code>kubectl --kubeconfig /custom/path/kube.conf get services --all-namespaces</code>
|
||||
</p>
|
||||
</a-timeline-item>
|
||||
</a-timeline>
|
||||
</a-card>
|
||||
<a-card title="Kubernetes Dashboard UI">
|
||||
<a-timeline>
|
||||
<a-timeline-item>
|
||||
<p>
|
||||
Run proxy locally<br><br>
|
||||
<code><b>kubectl --kubeconfig /custom/path/kube.conf proxy</b></code>
|
||||
</p>
|
||||
</a-timeline-item>
|
||||
<a-timeline-item>
|
||||
<p>
|
||||
Open URL in browser<br><br>
|
||||
<a href="http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/"><code>http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/</code></a>
|
||||
</p>
|
||||
</a-timeline-item>
|
||||
<a-timeline-item>
|
||||
<p>
|
||||
Token for dashboard login can be retrieved using following command<br><br>
|
||||
<code><b>kubectl --kubeconfig /custom/path/kube.conf describe secret $(kubectl --kubeconfig /custom/path/kube.conf get secrets -n kubernetes-dashboard | grep kubernetes-dashboard-token | awk '{print $1}') -n kubernetes-dashboard</b></code>
|
||||
</p>
|
||||
</a-timeline-item>
|
||||
</a-timeline>
|
||||
<p>More about accessing dashboard UI, <a href="https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/#accessing-the-dashboard-ui">https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/#accessing-the-dashboard-ui</a></p>
|
||||
</a-card>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane :tab="$t('instances')" key="instances">
|
||||
<a-table
|
||||
class="table"
|
||||
size="small"
|
||||
:columns="this.vmColumns"
|
||||
:dataSource="this.virtualmachines"
|
||||
:rowKey="item => item.id"
|
||||
:pagination="false"
|
||||
>
|
||||
<template slot="name" slot-scope="text, record">
|
||||
<router-link :to="{ path: '/vm/' + record.id }">{{ record.name }}</router-link>
|
||||
</template>
|
||||
<template slot="state" slot-scope="text">
|
||||
<status :text="text ? text : ''" displayText />
|
||||
</template>
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane :tab="$t('firewall')" key="firewall">
|
||||
<FirewallRules :resource="this.publicIpAddress" :loading="this.networkLoading" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane :tab="$t('portforwarding')" key="portforwarding">
|
||||
<PortForwarding :resource="this.publicIpAddress" :loading="this.networkLoading" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane :tab="$t('loadbalancing')" key="loadbalancing">
|
||||
<LoadBalancing :resource="this.publicIpAddress" :loading="this.networkLoading" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
import { mixinDevice } from '@/utils/mixin.js'
|
||||
import DetailsTab from '@/components/view/DetailsTab'
|
||||
import FirewallRules from '@/views/network/FirewallRules'
|
||||
import PortForwarding from '@/views/network/PortForwarding'
|
||||
import LoadBalancing from '@/views/network/LoadBalancing'
|
||||
import Status from '@/components/widgets/Status'
|
||||
|
||||
export default {
|
||||
name: 'KubernetesServiceTab',
|
||||
components: {
|
||||
DetailsTab,
|
||||
FirewallRules,
|
||||
PortForwarding,
|
||||
LoadBalancing,
|
||||
Status
|
||||
},
|
||||
mixins: [mixinDevice],
|
||||
props: {
|
||||
resource: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
clusterConfigLoading: false,
|
||||
clusterConfig: '',
|
||||
versionLoading: false,
|
||||
kubernetesVersion: {},
|
||||
kubectlLinuxLink: 'https://storage.googleapis.com/kubernetes-release/release/v1.16.0/bin/linux/amd64/kubectl',
|
||||
kubectlMacLink: 'https://storage.googleapis.com/kubernetes-release/release/v1.16.0/bin/darwin/amd64/kubectl',
|
||||
kubectlWindowsLink: 'https://storage.googleapis.com/kubernetes-release/release/v1.16.0/bin/windows/amd64/kubectl.exe',
|
||||
instanceLoading: false,
|
||||
virtualmachines: [],
|
||||
vmColumns: [],
|
||||
networkLoading: false,
|
||||
network: {},
|
||||
publicIpAddress: {},
|
||||
currentTab: 'details'
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (this.isAdminOrDomainAdmin()) {
|
||||
this.vmColumns = [
|
||||
{
|
||||
title: this.$t('name'),
|
||||
dataIndex: 'name',
|
||||
scopedSlots: { customRender: 'name' }
|
||||
},
|
||||
{
|
||||
title: this.$t('state'),
|
||||
dataIndex: 'state',
|
||||
scopedSlots: { customRender: 'state' }
|
||||
},
|
||||
{
|
||||
title: this.$t('instancename'),
|
||||
dataIndex: 'instancename'
|
||||
},
|
||||
{
|
||||
title: this.$t('ipaddress'),
|
||||
dataIndex: 'ipaddress'
|
||||
},
|
||||
{
|
||||
title: this.$t('zonename'),
|
||||
dataIndex: 'zonename'
|
||||
}
|
||||
]
|
||||
} else {
|
||||
this.vmColumns = [
|
||||
{
|
||||
title: this.$t('name'),
|
||||
dataIndex: 'name'
|
||||
},
|
||||
{
|
||||
title: this.$t('displayname'),
|
||||
dataIndex: 'displayname'
|
||||
},
|
||||
{
|
||||
title: this.$t('ipaddress'),
|
||||
dataIndex: 'ipaddress'
|
||||
},
|
||||
{
|
||||
title: this.$t('zonename'),
|
||||
dataIndex: 'zonename'
|
||||
},
|
||||
{
|
||||
title: this.$t('state'),
|
||||
dataIndex: 'state'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.handleFetchData()
|
||||
},
|
||||
watch: {
|
||||
loading (newData, oldData) {
|
||||
if (!newData && this.resource.id) {
|
||||
this.handleFetchData()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isAdminOrDomainAdmin () {
|
||||
return ['Admin', 'DomainAdmin'].includes(this.$store.getters.userInfo.roletype)
|
||||
},
|
||||
isValidValueForKey (obj, key) {
|
||||
return key in obj && obj[key] != null
|
||||
},
|
||||
arrayHasItems (array) {
|
||||
return array !== null && array !== undefined && Array.isArray(array) && array.length > 0
|
||||
},
|
||||
isObjectEmpty (obj) {
|
||||
return !(obj !== null && obj !== undefined && Object.keys(obj).length > 0 && obj.constructor === Object)
|
||||
},
|
||||
handleChangeTab (e) {
|
||||
this.currentTab = e
|
||||
},
|
||||
handleFetchData () {
|
||||
this.fetchKubernetesClusterConfig()
|
||||
this.fetchKubernetesVersion()
|
||||
this.fetchInstances()
|
||||
this.fetchPublicIpAddress()
|
||||
},
|
||||
fetchKubernetesClusterConfig () {
|
||||
this.clusterConfigLoading = true
|
||||
this.clusterConfig = ''
|
||||
if (!this.isObjectEmpty(this.resource)) {
|
||||
var params = {}
|
||||
params.id = this.resource.id
|
||||
api('getKubernetesClusterConfig', params).then(json => {
|
||||
const config = json.getkubernetesclusterconfigresponse.clusterconfig
|
||||
if (!this.isObjectEmpty(config) &&
|
||||
this.isValidValueForKey(config, 'configdata') &&
|
||||
config.configdata !== '') {
|
||||
this.clusterConfig = config.configdata
|
||||
} else {
|
||||
this.$notification.error({
|
||||
message: 'Request Failed',
|
||||
description: 'Unable to retrieve Kubernetes cluster config'
|
||||
})
|
||||
}
|
||||
}).finally(() => {
|
||||
this.clusterConfigLoading = false
|
||||
if (!this.isObjectEmpty(this.kubernetesVersion) && this.isValidValueForKey(this.kubernetesVersion, 'semanticversion')) {
|
||||
this.kubectlLinuxLink = 'https://storage.googleapis.com/kubernetes-release/release/v' + this.kubernetesVersion.semanticversion + '/bin/linux/amd64/kubectl'
|
||||
this.kubectlMacLink = 'https://storage.googleapis.com/kubernetes-release/release/v' + this.kubernetesVersion.semanticversion + '/bin/darwin/amd64/kubectl'
|
||||
this.kubectlWindowsLink = 'https://storage.googleapis.com/kubernetes-release/release/v' + this.kubernetesVersion.semanticversion + '/bin/windows/amd64/kubectl.exe'
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
fetchKubernetesVersion () {
|
||||
this.versionLoading = true
|
||||
this.virtualmachines = []
|
||||
if (!this.isObjectEmpty(this.resource) && this.isValidValueForKey(this.resource, 'kubernetesversionid') &&
|
||||
this.resource.kubernetesversionid !== '') {
|
||||
var params = {}
|
||||
params.id = this.resource.kubernetesversionid
|
||||
api('listKubernetesSupportedVersions', params).then(json => {
|
||||
const versionObjs = json.listkubernetessupportedversionsresponse.kubernetessupportedversion
|
||||
if (this.arrayHasItems(versionObjs)) {
|
||||
this.kubernetesVersion = versionObjs[0]
|
||||
}
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: 'Request Failed',
|
||||
description: error.response.headers['x-description']
|
||||
})
|
||||
}).finally(() => {
|
||||
this.versionLoading = false
|
||||
if (!this.isObjectEmpty(this.kubernetesVersion) && this.isValidValueForKey(this.kubernetesVersion, 'semanticversion')) {
|
||||
this.kubectlLinuxLink = 'https://storage.googleapis.com/kubernetes-release/release/v' + this.kubernetesVersion.semanticversion + '/bin/linux/amd64/kubectl'
|
||||
this.kubectlMacLink = 'https://storage.googleapis.com/kubernetes-release/release/v' + this.kubernetesVersion.semanticversion + '/bin/darwin/amd64/kubectl'
|
||||
this.kubectlWindowsLink = 'https://storage.googleapis.com/kubernetes-release/release/v' + this.kubernetesVersion.semanticversion + '/bin/windows/amd64/kubectl.exe'
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
fetchInstances () {
|
||||
this.instanceLoading = true
|
||||
this.virtualmachines = []
|
||||
if (!this.isObjectEmpty(this.resource) && this.arrayHasItems(this.resource.virtualmachineids)) {
|
||||
var params = {}
|
||||
if (this.isValidValueForKey(this.resource, 'projectid') &&
|
||||
this.resource.projectid !== '') {
|
||||
params.projectid = this.resource.projectid
|
||||
}
|
||||
params.ids = this.resource.virtualmachineids.join()
|
||||
api('listVirtualMachines', params).then(json => {
|
||||
const listVms = json.listvirtualmachinesresponse.virtualmachine
|
||||
if (this.arrayHasItems(listVms)) {
|
||||
for (var i = 0; i < listVms.length; ++i) {
|
||||
var vm = listVms[i]
|
||||
if (vm.nic && vm.nic.length > 0 && vm.nic[0].ipaddress) {
|
||||
vm.ipaddress = vm.nic[0].ipaddress
|
||||
listVms[i] = vm
|
||||
}
|
||||
}
|
||||
this.virtualmachines = this.virtualmachines.concat(listVms)
|
||||
}
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: 'Request Failed',
|
||||
description: error.response.headers['x-description']
|
||||
})
|
||||
}).finally(() => {
|
||||
this.instanceLoading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
fetchPublicIpAddress () {
|
||||
this.networkLoading = true
|
||||
var params = {
|
||||
listAll: true,
|
||||
forvirtualnetwork: true
|
||||
}
|
||||
if (!this.isObjectEmpty(this.resource)) {
|
||||
if (this.isValidValueForKey(this.resource, 'projectid') &&
|
||||
this.resource.projectid !== '') {
|
||||
params.projectid = this.resource.projectid
|
||||
}
|
||||
if (this.isValidValueForKey(this.resource, 'associatednetworkid')) {
|
||||
params.associatednetworkid = this.resource.associatednetworkid
|
||||
}
|
||||
}
|
||||
api('listPublicIpAddresses', params).then(json => {
|
||||
const ips = json.listpublicipaddressesresponse.publicipaddress
|
||||
if (this.arrayHasItems(ips)) {
|
||||
this.publicIpAddress = ips[0]
|
||||
}
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: 'Request Failed',
|
||||
description: error.response.headers['x-description']
|
||||
})
|
||||
}).finally(() => {
|
||||
this.networkLoading = false
|
||||
})
|
||||
},
|
||||
downloadKubernetesClusterConfig () {
|
||||
var blob = new Blob([this.clusterConfig], { type: 'text/plain' })
|
||||
var filename = 'kube.conf'
|
||||
if (window.navigator.msSaveOrOpenBlob) {
|
||||
window.navigator.msSaveBlob(blob, filename)
|
||||
} else {
|
||||
var elem = window.document.createElement('a')
|
||||
elem.href = window.URL.createObjectURL(blob)
|
||||
elem.download = filename
|
||||
document.body.appendChild(elem)
|
||||
elem.click()
|
||||
document.body.removeChild(elem)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.list {
|
||||
|
||||
&__item,
|
||||
&__row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__item {
|
||||
margin-bottom: -20px;
|
||||
}
|
||||
|
||||
&__col {
|
||||
flex: 1;
|
||||
margin-right: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.table {
|
||||
margin-top: 20px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
margin-top: 10px;
|
||||
text-align: right;
|
||||
|
||||
button {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
210
ui/src/views/compute/ScaleKubernetesCluster.vue
Normal file
210
ui/src/views/compute/ScaleKubernetesCluster.vue
Normal file
@ -0,0 +1,210 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
<template>
|
||||
<div class="form-layout">
|
||||
<a-spin :spinning="loading">
|
||||
<a-form
|
||||
:form="form"
|
||||
@submit="handleSubmit"
|
||||
layout="vertical">
|
||||
<a-form-item :label="$t('cks.cluster.size')">
|
||||
<a-input
|
||||
v-decorator="['size', {
|
||||
initialValue: '1',
|
||||
rules: [{
|
||||
validator: (rule, value, callback) => {
|
||||
if (value && (isNaN(value) || value <= 0)) {
|
||||
callback('Please enter a valid number')
|
||||
}
|
||||
callback()
|
||||
}
|
||||
}]
|
||||
}]"
|
||||
:placeholder="apiParams.size.description"/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('serviceofferingid')">
|
||||
<a-select
|
||||
id="offering-selection"
|
||||
v-decorator="['serviceofferingid', {}]"
|
||||
showSearch
|
||||
optionFilterProp="children"
|
||||
:filterOption="(input, option) => {
|
||||
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
:loading="serviceOfferingLoading"
|
||||
:placeholder="apiParams.serviceofferingid.description">
|
||||
<a-select-option v-for="(opt, optIndex) in this.serviceOfferings" :key="optIndex">
|
||||
{{ opt.name || opt.description }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<div :span="24" class="action-button">
|
||||
<a-button @click="closeAction">{{ this.$t('Cancel') }}</a-button>
|
||||
<a-button :loading="loading" type="primary" @click="handleSubmit">{{ this.$t('OK') }}</a-button>
|
||||
</div>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
|
||||
export default {
|
||||
name: 'ScaleKubernetesCluster',
|
||||
props: {
|
||||
resource: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
originalSize: 1,
|
||||
serviceOfferings: [],
|
||||
serviceOfferingLoading: false,
|
||||
minCpu: 2,
|
||||
minMemory: 2048,
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
this.form = this.$form.createForm(this)
|
||||
this.apiConfig = this.$store.getters.apis.scaleKubernetesCluster || {}
|
||||
this.apiParams = {}
|
||||
this.apiConfig.params.forEach(param => {
|
||||
this.apiParams[param.name] = param
|
||||
})
|
||||
},
|
||||
created () {
|
||||
},
|
||||
mounted () {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
fetchData () {
|
||||
this.originalSize = !this.isObjectEmpty(this.resource) ? 1 : this.resource.size
|
||||
this.fetchKubernetesVersionData()
|
||||
},
|
||||
isValidValueForKey (obj, key) {
|
||||
return key in obj && obj[key] != null
|
||||
},
|
||||
arrayHasItems (array) {
|
||||
return array !== null && array !== undefined && Array.isArray(array) && array.length > 0
|
||||
},
|
||||
isObjectEmpty (obj) {
|
||||
return !(obj !== null && obj !== undefined && Object.keys(obj).length > 0 && obj.constructor === Object)
|
||||
},
|
||||
fetchKubernetesVersionData () {
|
||||
const params = {}
|
||||
if (!this.isObjectEmpty(this.resource)) {
|
||||
params.id = this.resource.kubernetesversionid
|
||||
}
|
||||
api('listKubernetesSupportedVersions', params).then(json => {
|
||||
const versionObjs = json.listkubernetessupportedversionsresponse.kubernetessupportedversion
|
||||
if (this.arrayHasItems(versionObjs) && !this.isObjectEmpty(versionObjs[0])) {
|
||||
this.minCpu = versionObjs[0].mincpunumber
|
||||
this.minMemory = versionObjs[0].minmemory
|
||||
}
|
||||
}).finally(() => {
|
||||
this.fetchServiceOfferingData()
|
||||
})
|
||||
},
|
||||
fetchServiceOfferingData () {
|
||||
this.serviceOfferings = []
|
||||
const params = {}
|
||||
this.serviceOfferingLoading = true
|
||||
api('listServiceOfferings', params).then(json => {
|
||||
var items = json.listserviceofferingsresponse.serviceoffering
|
||||
if (items != null) {
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (items[i].iscustomized === false &&
|
||||
items[i].cpunumber >= this.minCpu && items[i].memory >= this.minMemory) {
|
||||
this.serviceOfferings.push(items[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}).finally(() => {
|
||||
this.serviceOfferingLoading = false
|
||||
if (this.arrayHasItems(this.serviceOfferings)) {
|
||||
for (var i = 0; i < this.serviceOfferings.length; i++) {
|
||||
if (this.serviceOfferings[i].id === this.resource.serviceofferingid) {
|
||||
this.form.setFieldsValue({
|
||||
serviceofferingid: i
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
handleSubmit (e) {
|
||||
e.preventDefault()
|
||||
this.form.validateFields((err, values) => {
|
||||
if (err) {
|
||||
return
|
||||
}
|
||||
this.loading = true
|
||||
const params = {
|
||||
id: this.resource.id
|
||||
}
|
||||
if (this.isValidValueForKey(values, 'size') && values.size > 0) {
|
||||
params.kubernetesversionid = this.kubernetesVersions[values.kubernetesversionid].id
|
||||
}
|
||||
if (this.isValidValueForKey(values, 'serviceofferingid') && this.arrayHasItems(this.serviceOfferings)) {
|
||||
params.serviceofferingid = this.serviceOfferings[values.serviceofferingid].id
|
||||
}
|
||||
api('scaleKubernetesCluster', params).then(json => {
|
||||
this.$message.success('Successfully scaled Kubernetes cluster: ' + this.resource.name)
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: 'Request Failed',
|
||||
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
|
||||
})
|
||||
}).finally(() => {
|
||||
this.$emit('refresh-data')
|
||||
this.loading = false
|
||||
this.closeAction()
|
||||
})
|
||||
})
|
||||
},
|
||||
closeAction () {
|
||||
this.$emit('close-action')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.form-layout {
|
||||
width: 60vw;
|
||||
|
||||
@media (min-width: 500px) {
|
||||
width: 450px;
|
||||
}
|
||||
}
|
||||
|
||||
.action-button {
|
||||
text-align: right;
|
||||
|
||||
button {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
186
ui/src/views/compute/UpgradeKubernetesCluster.vue
Normal file
186
ui/src/views/compute/UpgradeKubernetesCluster.vue
Normal file
@ -0,0 +1,186 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
<template>
|
||||
<div class="form-layout">
|
||||
<a-spin :spinning="loading">
|
||||
<a-form
|
||||
:form="form"
|
||||
@submit="handleSubmit"
|
||||
layout="vertical">
|
||||
<a-form-item :label="$t('kubernetesversionid')">
|
||||
<a-select
|
||||
id="version-selection"
|
||||
v-decorator="['kubernetesversionid', {
|
||||
rules: [{ required: true }]
|
||||
}]"
|
||||
showSearch
|
||||
optionFilterProp="children"
|
||||
:filterOption="(input, option) => {
|
||||
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
:loading="kubernetesVersionLoading"
|
||||
:placeholder="apiParams.kubernetesversionid.description">
|
||||
<a-select-option v-for="(opt, optIndex) in this.kubernetesVersions" :key="optIndex">
|
||||
{{ opt.name || opt.description }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<div :span="24" class="action-button">
|
||||
<a-button @click="closeAction">{{ this.$t('Cancel') }}</a-button>
|
||||
<a-button :loading="loading" type="primary" @click="handleSubmit">{{ this.$t('OK') }}</a-button>
|
||||
</div>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
|
||||
export default {
|
||||
name: 'UpgradeKubernetesCluster',
|
||||
props: {
|
||||
resource: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
kubernetesVersions: [],
|
||||
kubernetesVersionLoading: false,
|
||||
minCpu: 2,
|
||||
minMemory: 2048,
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
this.form = this.$form.createForm(this)
|
||||
this.apiConfig = this.$store.getters.apis.upgradeKubernetesCluster || {}
|
||||
this.apiParams = {}
|
||||
this.apiConfig.params.forEach(param => {
|
||||
this.apiParams[param.name] = param
|
||||
})
|
||||
},
|
||||
created () {
|
||||
},
|
||||
mounted () {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
fetchData () {
|
||||
this.fetchKubernetesVersionData()
|
||||
},
|
||||
isValidValueForKey (obj, key) {
|
||||
return key in obj && obj[key] != null
|
||||
},
|
||||
arrayHasItems (array) {
|
||||
return array !== null && array !== undefined && Array.isArray(array) && array.length > 0
|
||||
},
|
||||
isObjectEmpty (obj) {
|
||||
return !(obj !== null && obj !== undefined && Object.keys(obj).length > 0 && obj.constructor === Object)
|
||||
},
|
||||
fetchKubernetesVersionData () {
|
||||
this.kubernetesVersions = []
|
||||
const params = {}
|
||||
if (!this.isObjectEmpty(this.resource)) {
|
||||
params.minimumkubernetesversionid = this.resource.kubernetesversionid
|
||||
}
|
||||
this.kubernetesVersionLoading = true
|
||||
api('listKubernetesSupportedVersions', params).then(json => {
|
||||
const versionObjs = json.listkubernetessupportedversionsresponse.kubernetessupportedversion
|
||||
if (this.arrayHasItems(versionObjs)) {
|
||||
var clusterVersion = null
|
||||
for (var j = 0; j < versionObjs.length; j++) {
|
||||
if (versionObjs[j].id === this.resource.kubernetesversionid) {
|
||||
clusterVersion = versionObjs[j]
|
||||
break
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < versionObjs.length; i++) {
|
||||
if (versionObjs[i].id !== this.resource.kubernetesversionid &&
|
||||
(clusterVersion == null || (clusterVersion != null && versionObjs[i].semanticversion !== clusterVersion.semanticversion)) &&
|
||||
versionObjs[i].state === 'Enabled' && versionObjs[i].isostate === 'Ready') {
|
||||
this.kubernetesVersions.push({
|
||||
id: versionObjs[i].id,
|
||||
description: versionObjs[i].name
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}).finally(() => {
|
||||
this.kubernetesVersionLoading = false
|
||||
if (this.arrayHasItems(this.kubernetesVersions)) {
|
||||
this.form.setFieldsValue({
|
||||
kubernetesversionid: 0
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handleSubmit (e) {
|
||||
e.preventDefault()
|
||||
this.form.validateFields((err, values) => {
|
||||
if (err) {
|
||||
return
|
||||
}
|
||||
this.loading = true
|
||||
const params = {
|
||||
id: this.resource.id
|
||||
}
|
||||
if (this.isValidValueForKey(values, 'kubernetesversionid') && this.arrayHasItems(this.kubernetesVersions)) {
|
||||
params.kubernetesversionid = this.kubernetesVersions[values.kubernetesversionid].id
|
||||
}
|
||||
api('upgradeKubernetesCluster', params).then(json => {
|
||||
this.$message.success('Successfully upgraded Kubernetes cluster: ' + this.resource.name)
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: 'Request Failed',
|
||||
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
|
||||
})
|
||||
}).finally(() => {
|
||||
this.$emit('refresh-data')
|
||||
this.loading = false
|
||||
this.closeAction()
|
||||
})
|
||||
})
|
||||
},
|
||||
closeAction () {
|
||||
this.$emit('close-action')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.form-layout {
|
||||
width: 60vw;
|
||||
|
||||
@media (min-width: 500px) {
|
||||
width: 450px;
|
||||
}
|
||||
}
|
||||
|
||||
.action-button {
|
||||
text-align: right;
|
||||
|
||||
button {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
243
ui/src/views/image/AddKubernetesSupportedVersion.vue
Normal file
243
ui/src/views/image/AddKubernetesSupportedVersion.vue
Normal file
@ -0,0 +1,243 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
<template>
|
||||
<div class="form-layout">
|
||||
<a-spin :spinning="loading">
|
||||
<a-form
|
||||
:form="form"
|
||||
@submit="handleSubmit"
|
||||
layout="vertical">
|
||||
<a-form-item :label="$t('semanticversion')">
|
||||
<a-input
|
||||
v-decorator="['semanticversion', {
|
||||
rules: [{ required: true, message: 'Please enter Kubernetes semantic version' }]
|
||||
}]"
|
||||
:placeholder="apiParams.semanticversion.description"/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('name')">
|
||||
<a-input
|
||||
v-decorator="['name', {
|
||||
rules: [{ message: 'Please enter name' }]
|
||||
}]"
|
||||
:placeholder="$t('name')"/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('zoneid')">
|
||||
<a-select
|
||||
id="zone-selection"
|
||||
v-decorator="['zoneid', {
|
||||
rules: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (value && value.length > 1 && value.indexOf(0) !== -1) {
|
||||
callback('All Zones cannot be combined with any other zone')
|
||||
}
|
||||
callback()
|
||||
}
|
||||
}
|
||||
]
|
||||
}]"
|
||||
showSearch
|
||||
optionFilterProp="children"
|
||||
:filterOption="(input, option) => {
|
||||
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
:loading="zoneLoading"
|
||||
:placeholder="apiParams.zoneid.description">
|
||||
<a-select-option v-for="(opt, optIndex) in this.zones" :key="optIndex">
|
||||
{{ opt.name || opt.description }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('url')">
|
||||
<a-input
|
||||
v-decorator="['url', {
|
||||
rules: [{ required: true, message: 'Please enter binaries ISO URL' }]
|
||||
}]"
|
||||
:placeholder="apiParams.url.description" />
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('checksum')">
|
||||
<a-input
|
||||
v-decorator="['checksum', {
|
||||
rules: [{ required: false, message: 'Please enter input' }]
|
||||
}]"
|
||||
:placeholder="apiParams.checksum.description" />
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('mincpunumber')">
|
||||
<a-input
|
||||
v-decorator="['mincpunumber', {
|
||||
rules: [{ required: true, message: 'Please enter value' },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (value && (isNaN(value) || value <= 0)) {
|
||||
callback('Please enter a valid number')
|
||||
}
|
||||
callback()
|
||||
}
|
||||
}
|
||||
]
|
||||
}]"
|
||||
:placeholder="apiParams.mincpunumber.description"/>
|
||||
</a-form-item>
|
||||
<a-form-item :label="$t('minmemory')">
|
||||
<a-input
|
||||
v-decorator="['minmemory', {
|
||||
rules: [{ required: true, message: 'Please enter value' },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (value && (isNaN(value) || value <= 0)) {
|
||||
callback('Please enter a valid number')
|
||||
}
|
||||
callback()
|
||||
}
|
||||
}
|
||||
]
|
||||
}]"
|
||||
:placeholder="apiParams.minmemory.description"/>
|
||||
</a-form-item>
|
||||
|
||||
<div :span="24" class="action-button">
|
||||
<a-button @click="closeAction">{{ this.$t('Cancel') }}</a-button>
|
||||
<a-button :loading="loading" type="primary" @click="handleSubmit">{{ this.$t('OK') }}</a-button>
|
||||
</div>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
|
||||
export default {
|
||||
name: 'AddKubernetesSupportedVersion',
|
||||
props: {},
|
||||
data () {
|
||||
return {
|
||||
zones: [],
|
||||
zoneLoading: false,
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
this.form = this.$form.createForm(this)
|
||||
this.apiConfig = this.$store.getters.apis.addKubernetesSupportedVersion || {}
|
||||
this.apiParams = {}
|
||||
this.apiConfig.params.forEach(param => {
|
||||
this.apiParams[param.name] = param
|
||||
})
|
||||
},
|
||||
created () {
|
||||
this.zones = [
|
||||
{
|
||||
id: null,
|
||||
name: this.$t('label.all.zone')
|
||||
}
|
||||
]
|
||||
},
|
||||
mounted () {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
fetchData () {
|
||||
this.fetchZoneData()
|
||||
},
|
||||
isValidValueForKey (obj, key) {
|
||||
return key in obj && obj[key] != null
|
||||
},
|
||||
arrayHasItems (array) {
|
||||
return array !== null && array !== undefined && Array.isArray(array) && array.length > 0
|
||||
},
|
||||
fetchZoneData () {
|
||||
const params = {}
|
||||
params.listAll = true
|
||||
this.zoneLoading = true
|
||||
api('listZones', params).then(json => {
|
||||
const listZones = json.listzonesresponse.zone
|
||||
this.zones = this.zones.concat(listZones)
|
||||
}).finally(() => {
|
||||
this.zoneLoading = false
|
||||
if (this.arrayHasItems(this.zones)) {
|
||||
this.form.setFieldsValue({
|
||||
zoneid: 0
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
handleSubmit (e) {
|
||||
e.preventDefault()
|
||||
this.form.validateFields((err, values) => {
|
||||
if (err) {
|
||||
return
|
||||
}
|
||||
this.loading = true
|
||||
const params = {
|
||||
semanticversion: values.semanticversion,
|
||||
url: values.url
|
||||
}
|
||||
if (this.isValidValueForKey(values, 'name')) {
|
||||
params.name = values.name
|
||||
}
|
||||
if (this.isValidValueForKey(values, 'checksum')) {
|
||||
params.checksum = values.checksum
|
||||
}
|
||||
if (this.isValidValueForKey(values, 'zoneid')) {
|
||||
params.zoneid = this.zones[values.zoneid].id
|
||||
}
|
||||
if (this.isValidValueForKey(values, 'mincpunumber') && values.mincpunumber > 0) {
|
||||
params.mincpunumber = values.mincpunumber
|
||||
}
|
||||
if (this.isValidValueForKey(values, 'minmemory') && values.minmemory > 0) {
|
||||
params.minmemory = values.minmemory
|
||||
}
|
||||
api('addKubernetesSupportedVersion', params).then(json => {
|
||||
this.$message.success('Successfully added Kubernetes version: ' + values.semanticversion)
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: 'Request Failed',
|
||||
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
|
||||
})
|
||||
}).finally(() => {
|
||||
this.$emit('refresh-data')
|
||||
this.loading = false
|
||||
this.closeAction()
|
||||
})
|
||||
})
|
||||
},
|
||||
closeAction () {
|
||||
this.$emit('close-action')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.form-layout {
|
||||
width: 80vw;
|
||||
|
||||
@media (min-width: 700px) {
|
||||
width: 550px;
|
||||
}
|
||||
}
|
||||
|
||||
.action-button {
|
||||
text-align: right;
|
||||
|
||||
button {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
168
ui/src/views/image/UpdateKubernetesSupportedVersion.vue
Normal file
168
ui/src/views/image/UpdateKubernetesSupportedVersion.vue
Normal file
@ -0,0 +1,168 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
<template>
|
||||
<div class="form-layout">
|
||||
<a-spin :spinning="loading">
|
||||
<a-form
|
||||
:form="form"
|
||||
@submit="handleSubmit"
|
||||
layout="vertical">
|
||||
<a-form-item :label="$t('state')">
|
||||
<a-select
|
||||
id="state-selection"
|
||||
v-decorator="['state', {
|
||||
rules: [{ required: true }]
|
||||
}]"
|
||||
showSearch
|
||||
optionFilterProp="children"
|
||||
:filterOption="(input, option) => {
|
||||
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}"
|
||||
:loading="stateLoading"
|
||||
:placeholder="apiParams.state.description">
|
||||
<a-select-option v-for="(opt, optIndex) in this.states" :key="optIndex">
|
||||
{{ opt.name || opt.description }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<div :span="24" class="action-button">
|
||||
<a-button @click="closeAction">{{ this.$t('Cancel') }}</a-button>
|
||||
<a-button :loading="loading" type="primary" @click="handleSubmit">{{ this.$t('OK') }}</a-button>
|
||||
</div>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { api } from '@/api'
|
||||
|
||||
export default {
|
||||
name: 'UpdateKubernetesSupportedVersion',
|
||||
props: {
|
||||
resource: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
states: [],
|
||||
stateLoading: false,
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
beforeCreate () {
|
||||
this.form = this.$form.createForm(this)
|
||||
this.apiConfig = this.$store.getters.apis.updateKubernetesSupportedVersion || {}
|
||||
this.apiParams = {}
|
||||
this.apiConfig.params.forEach(param => {
|
||||
this.apiParams[param.name] = param
|
||||
})
|
||||
},
|
||||
created () {
|
||||
this.states = [
|
||||
{
|
||||
id: 'Enabled',
|
||||
name: this.$t('enabled')
|
||||
},
|
||||
{
|
||||
id: 'Disabled',
|
||||
name: this.$t('disabled')
|
||||
}
|
||||
]
|
||||
},
|
||||
mounted () {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
fetchData () {
|
||||
var selectedState = 0
|
||||
if (!this.isObjectEmpty(this.resource)) {
|
||||
for (var i = 0; i < this.states.length; ++i) {
|
||||
if (this.states[i].id === this.resource.state) {
|
||||
selectedState = i
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
this.form.setFieldsValue({
|
||||
state: selectedState
|
||||
})
|
||||
},
|
||||
isValidValueForKey (obj, key) {
|
||||
return key in obj && obj[key] != null
|
||||
},
|
||||
arrayHasItems (array) {
|
||||
return array !== null && array !== undefined && Array.isArray(array) && array.length > 0
|
||||
},
|
||||
isObjectEmpty (obj) {
|
||||
return !(obj !== null && obj !== undefined && Object.keys(obj).length > 0 && obj.constructor === Object)
|
||||
},
|
||||
handleSubmit (e) {
|
||||
e.preventDefault()
|
||||
this.form.validateFields((err, values) => {
|
||||
if (err) {
|
||||
return
|
||||
}
|
||||
this.loading = true
|
||||
const params = {
|
||||
id: this.resource.id
|
||||
}
|
||||
if (this.isValidValueForKey(values, 'state') && this.arrayHasItems(this.states)) {
|
||||
params.state = this.states[values.state].id
|
||||
}
|
||||
api('updateKubernetesSupportedVersion', params).then(json => {
|
||||
this.$message.success('Successfully updated Kubernetes supported version: ' + this.resource.name)
|
||||
}).catch(error => {
|
||||
this.$notification.error({
|
||||
message: 'Request Failed',
|
||||
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
|
||||
})
|
||||
}).finally(() => {
|
||||
this.$emit('refresh-data')
|
||||
this.loading = false
|
||||
this.closeAction()
|
||||
})
|
||||
})
|
||||
},
|
||||
closeAction () {
|
||||
this.$emit('close-action')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.form-layout {
|
||||
width: 60vw;
|
||||
|
||||
@media (min-width: 500px) {
|
||||
width: 450px;
|
||||
}
|
||||
}
|
||||
|
||||
.action-button {
|
||||
text-align: right;
|
||||
|
||||
button {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
x
Reference in New Issue
Block a user