blob: 40123a57d517133709d844d752ecd3dc425236db [file]
// 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>
<a-card class="ant-form-text" style="text-align: justify; margin: 10px 0; padding: 15px;" v-html="$t('message.desc.register.template')" />
<a-table
:columns="columns"
:dataSource="predefinedTemplates"
:rowSelection="rowSelection"
:loading="loading"
:scroll="{ y: 450 }"
size="middle"
:rowKey="record => record.id"
:pagination="false"
class="form-content"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'name'">
<os-logo :osName="record.name" size="xl" />
{{ record.name }}
</template>
</template>
</a-table>
<div class="form-action">
<a-button class="button-back" @click="handleDone">{{ selectedRowKeys.length > 0 ? $t('label.done') : $t('label.skip') }}</a-button>
<a-button class="button-next" type="primary" @click="handleSubmit" ref="submit">{{ $t('label.register.template') }}</a-button>
</div>
<a-modal
:visible="showAlert"
:footer="null"
style="top: 20px;"
centered
width="auto"
@cancel="showAlert = false"
>
<template #title>
{{ $t('label.warning') }}
</template>
<a-alert type="warning">
<template #message>
<span v-html="$t('message.warn.select.template')" />
</template>
</a-alert>
<a-divider style="margin-top: 0;"></a-divider>
</a-modal>
</div>
</template>
<script>
import { getAPI, postAPI } from '@/api'
import { genericCompare } from '@/utils/sort.js'
import OsLogo from '@/components/widgets/OsLogo'
export default {
name: 'ZoneWizardRegisterTemplate',
components: {
OsLogo
},
props: {
zoneid: {
type: String,
required: false
},
arch: {
type: String,
required: false
},
zoneSuperType: {
type: String,
required: false
}
},
data: () => ({
columns: null,
loading: false,
predefinedTemplates: [],
rowKey: 0,
selectedRowKeys: [],
defaultOsTypeId: null,
deployedTemplates: {},
showAlert: false
}),
created () {
this.initForm()
},
mounted () {
this.fetchPredefinedTemplates()
},
computed: {
rowSelection () {
return {
selectedRowKeys: this.selectedRowKeys || [],
onChange: this.onSelectRow,
getCheckboxProps: (record) => {
return {
disabled: record.disabled
}
}
}
}
},
methods: {
async initForm () {
this.columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
width: 240,
sorter: (a, b) => { return genericCompare(a.name || '', b.name || '') }
},
{
title: 'Arch',
dataIndex: 'arch',
key: 'arch',
width: 80,
sorter: (a, b) => { return genericCompare(a.arch || '', b.arch || '') }
},
{
title: 'URL',
dataIndex: 'url',
key: 'url'
}
]
this.defaultOsTypeId = await this.fetchOsTypeId('Other Linux (64-bit)')
},
handleDone () {
this.$emit('refresh-data')
},
async handleSubmit () {
await this.stepRegisterTemplates()
},
onSelectRow (value) {
this.selectedRowKeys = value
},
async registerTemplate (templateData) {
const params = {
displaytext: templateData.name + ' ' + templateData.arch,
format: this.getImageFormat(templateData.url),
hypervisor: 'KVM',
name: templateData.name,
arch: templateData.arch,
url: templateData.url,
ostypeid: await this.fetchOsTypeId(templateData.name),
zoneid: this.zoneid
}
if (this.zoneSuperType === 'Edge') {
params.directdownload = true
}
return new Promise((resolve, reject) => {
postAPI('registerTemplate', params).then(json => {
const result = json.registertemplateresponse.template[0]
resolve(result)
}).catch(error => {
const message = error.response.headers['x-description']
reject(message)
})
})
},
async stepRegisterTemplates () {
const templatesToRegister = this.predefinedTemplates.filter(template => this.selectedRowKeys.includes(template.id) && this.deployedTemplates[template.id] !== true)
if (templatesToRegister.length === 0) {
this.showAlert = true
return
}
const registrationResults = []
for (const templateData of templatesToRegister) {
const promise = this.registerTemplate(templateData)
.then(() => ({
id: templateData.id,
status: 'success',
name: templateData.name
}))
.catch(() => ({
id: templateData.id,
status: 'error',
name: templateData.name
}))
registrationResults.push(promise)
}
const results = await Promise.all(registrationResults)
const successful = results.filter(r => r.status === 'success')
const failed = results.filter(r => r.status === 'error')
if (successful.length > 0) {
this.$notification.success({
message: this.$t('label.register.template'),
description: 'Successfully registered templates: ' + successful.map(r => r.name).join(', ')
})
successful.forEach(r => {
this.deployedTemplates[r.id] = true
this.predefinedTemplates.find(t => t.id === r.id).disabled = true
})
}
if (failed.length > 0) {
this.$notification.error({
message: this.$t('label.register.template'),
description: 'Failed registering templates: ' + failed.map(r => r.name).join(', ')
})
failed.forEach(r => {
this.predefinedTemplates.find(t => t.id === r.id).disabled = true
this.selectedRowKeys = this.selectedRowKeys.filter(id => id !== r.id)
})
}
},
async fetchOsTypeId (osName) {
let osTypeId = this.defaultOsTypeId
this.loading = true
try {
const json = await getAPI('listOsTypes', { keyword: osName, filter: 'name,id' })
if (json && json.listostypesresponse && json.listostypesresponse.ostype && json.listostypesresponse.ostype.length > 0) {
osTypeId = json.listostypesresponse.ostype[0].id
}
} catch (error) {
console.error('Error fetching OS types:', error)
} finally {
this.loading = false
}
return osTypeId
},
getImageFormat (url) {
const fileExtension = url.split('.').pop()
var format = fileExtension
switch (fileExtension) {
case 'img':
format = 'RAW'
break
case 'qcow2':
format = 'qcow2'
break
default:
format = 'RAW'
}
return format
},
async fetchPredefinedTemplates () {
this.loading = true
try {
const response = await fetch('./cloud-image-templates.json')
if (!response.ok) {
throw new Error(`Error fetching predefined templates, status_code: ${response.status}`)
}
const templates = await response.json()
this.predefinedTemplates = this.arch
? templates.filter(template => template.arch === this.arch)
: templates
// Replace 'https' with 'http' in all URLs for EdgeZone
if (this.zoneSuperType === 'Edge') {
this.predefinedTemplates.forEach(template => {
if (template.url.startsWith('https://')) {
template.url = template.url.replace('https://', 'http://')
}
})
}
} catch (error) {
console.error('Error fetching predefined templates:', error)
this.predefinedTemplates = []
} finally {
this.loading = false
}
}
}
}
</script>
<style lang="less" scoped>
.form-content {
border: 1px dashed #e9e9e9;
border-radius: 6px;
background-color: #fafafa;
min-height: 440px;
text-align: center;
vertical-align: center;
padding: 8px;
padding-top: 16px;
margin-top: 8px;
overflow-y: auto;
}
</style>