Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ui/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1617,6 +1617,8 @@
"label.offeringid": "Offering ID",
"label.offeringtype": "Compute Offering type",
"label.ok": "OK",
"label.ssvm.open.cert.page": "Open Certificate Page",
"label.retry.upload": "Retry Upload",
"label.only.end.date.and.time": "Only end date and time",
"label.only.start.date.and.time": "Only start date and time",
"label.open.documentation": "Open documentation",
Expand Down Expand Up @@ -3667,6 +3669,9 @@
"message.upload.iso.failed.description": "Failed to upload ISO.",
"message.upload.template.failed.description": "Failed to upload Template",
"message.upload.volume.failed": "Volume upload failed",
"message.ssvm.cert.untrusted": "Unable to reach the upload server.",
"message.ssvm.cert.trust.instructions": "The upload server may be using a self-signed or untrusted certificate. Click 'Open Certificate Page' to open the server in a new browser tab, accept the certificate warning, then return here and click 'Retry Upload'. If the server remains unreachable, contact your administrator.",
"message.ssvm.unreachable.retry": "The upload server is still unreachable. If it uses a self-signed certificate, please accept it in the opened tab and try again.",
"message.user.not.permitted.api": "User is not permitted to use the API",
"message.validate.equalto": "Please enter the same value again.",
"message.validate.max": "Please enter a value less than or equal to {0}.",
Expand Down
30 changes: 30 additions & 0 deletions ui/src/utils/ssvmProbe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// 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.

const SSVM_PROBE_TIMEOUT_MS = 5000
export async function probeSsvmCert (origin) {
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), SSVM_PROBE_TIMEOUT_MS)
try {
await fetch(origin, { method: 'HEAD', mode: 'no-cors', signal: controller.signal })
return true
} catch (e) {
return false
} finally {
clearTimeout(timeoutId)
}
}
50 changes: 42 additions & 8 deletions ui/src/views/image/RegisterOrUploadIso.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,27 @@
<div
class="form-layout"
@keyup.ctrl.enter="handleSubmit">
<span v-if="uploadPercentage > 0">
<span v-if="uploading">
<loading-outlined />
{{ $t('message.upload.file.processing') }}
<a-progress :percent="uploadPercentage" />
</span>
Comment thread
abh1sar marked this conversation as resolved.
<div v-else-if="ssvmCertUntrusted" class="ssvm-cert-warning">
<a-alert
type="warning"
show-icon
:message="$t('message.ssvm.cert.untrusted')"
:description="$t('message.ssvm.cert.trust.instructions')" />
<div class="action-button" style="margin-top: 16px">
<a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
<a-button :href="ssvmOrigin" target="_blank" rel="noopener noreferrer">
{{ $t('label.ssvm.open.cert.page') }}
</a-button>
<a-button type="primary" :loading="loading" @click="retryUpload">
{{ $t('label.retry.upload') }}
</a-button>
</div>
</div>
<a-spin :spinning="loading" v-else>
<a-form
:ref="formRef"
Expand Down Expand Up @@ -311,6 +327,7 @@ import { api } from '@/api'
import store from '@/store'
import { axios } from '../../utils/request'
import { mixinForm } from '@/utils/mixin'
import { probeSsvmCert } from '@/utils/ssvmProbe'
import ResourceIcon from '@/components/view/ResourceIcon'
import TooltipLabel from '@/components/widgets/TooltipLabel'

Expand Down Expand Up @@ -343,9 +360,12 @@ export default {
userdatapolicy: null,
userdatapolicylist: {},
loading: false,
uploading: false,
allowed: false,
uploadParams: null,
uploadPercentage: 0,
ssvmCertUntrusted: false,
ssvmOrigin: '',
currentForm: ['plus-outlined', 'PlusOutlined'].includes(this.action.currentAction.icon) ? 'Create' : 'Upload',
domains: [],
accounts: [],
Expand Down Expand Up @@ -489,6 +509,17 @@ export default {
this.form.file = file
return false
},
async retryUpload () {
this.loading = true
const reachable = await probeSsvmCert(this.ssvmOrigin)
this.loading = false
if (!reachable) {
this.$message.warning(this.$t('message.ssvm.unreachable.retry'))
return
}
this.ssvmCertUntrusted = false
this.handleUpload()
},
handleUpload () {
const { fileList } = this
if (this.fileList.length > 1) {
Expand All @@ -502,6 +533,7 @@ export default {
fileList.forEach(file => {
formData.append('files[]', file)
})
this.uploading = true
this.uploadPercentage = 0
axios.post(this.uploadParams.postURL,
formData,
Expand Down Expand Up @@ -529,6 +561,8 @@ export default {
description: `${this.$t('message.upload.iso.failed.description')} - ${e}`,
duration: 0
})
}).finally(() => {
this.uploading = false
})
},
handleSubmit (e) {
Expand Down Expand Up @@ -583,18 +617,18 @@ export default {
}
params.format = 'ISO'
this.loading = true
api('getUploadParamsForIso', params).then(json => {
api('getUploadParamsForIso', params).then(async json => {
this.uploadParams = (json.postuploadisoresponse && json.postuploadisoresponse.getuploadparams) ? json.postuploadisoresponse.getuploadparams : ''
const response = this.handleUpload()
if (this.userdataid !== null) {
this.linkUserdataToTemplate(this.userdataid, json.postuploadisoresponse.iso[0].id)
}
if (response === 'upload successful') {
this.$notification.success({
message: this.$t('message.success.upload'),
description: this.$t('message.success.upload.iso.description')
})
this.ssvmOrigin = new URL(this.uploadParams.postURL).origin
const trusted = await probeSsvmCert(this.ssvmOrigin)
if (!trusted) {
this.ssvmCertUntrusted = true
return
}
this.handleUpload()
}).catch(error => {
this.$notifyError(error)
}).finally(() => {
Expand Down
45 changes: 42 additions & 3 deletions ui/src/views/image/RegisterOrUploadTemplate.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,27 @@
<div
:class="'form-layout'"
@keyup.ctrl.enter="handleSubmit">
<span v-if="uploadPercentage > 0">
<span v-if="uploading">
<loading-outlined />
{{ $t('message.upload.file.processing') }}
<a-progress :percent="uploadPercentage" />
</span>
<div v-else-if="ssvmCertUntrusted" class="ssvm-cert-warning">
<a-alert
type="warning"
show-icon
:message="$t('message.ssvm.cert.untrusted')"
:description="$t('message.ssvm.cert.trust.instructions')" />
<div class="action-button" style="margin-top: 16px">
<a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
<a-button :href="ssvmOrigin" target="_blank" rel="noopener noreferrer">
{{ $t('label.ssvm.open.cert.page') }}
</a-button>
<a-button type="primary" :loading="loading" @click="retryUpload">
{{ $t('label.retry.upload') }}
</a-button>
</div>
</div>
<a-spin :spinning="loading" v-else>
<a-form
:ref="formRef"
Expand Down Expand Up @@ -472,6 +488,7 @@ import { api } from '@/api'
import store from '@/store'
import { axios } from '../../utils/request'
import { mixinForm } from '@/utils/mixin'
import { probeSsvmCert } from '@/utils/ssvmProbe'
import ResourceIcon from '@/components/view/ResourceIcon'
import TooltipLabel from '@/components/widgets/TooltipLabel'

Expand All @@ -497,6 +514,8 @@ export default {
uploadPercentage: 0,
uploading: false,
fileList: [],
ssvmCertUntrusted: false,
ssvmOrigin: '',
zones: {},
defaultZone: '',
hyperVisor: {},
Expand Down Expand Up @@ -610,12 +629,24 @@ export default {
this.form.file = file
return false
},
async retryUpload () {
this.loading = true
const reachable = await probeSsvmCert(this.ssvmOrigin)
this.loading = false
if (!reachable) {
this.$message.warning(this.$t('message.ssvm.unreachable.retry'))
return
}
this.ssvmCertUntrusted = false
this.handleUpload()
},
handleUpload () {
const { fileList } = this
const formData = new FormData()
fileList.forEach(file => {
formData.append('files[]', file)
})
this.uploading = true
this.uploadPercentage = 0
axios.post(this.uploadParams.postURL,
formData,
Expand All @@ -639,6 +670,8 @@ export default {
this.closeAction()
}).catch(e => {
this.$notifyError(e)
}).finally(() => {
this.uploading = false
})
},
fetchCustomHypervisorName () {
Expand Down Expand Up @@ -1124,12 +1157,18 @@ export default {
duration: 0
})
Comment thread
abh1sar marked this conversation as resolved.
}
api('getUploadParamsForTemplate', params).then(json => {
api('getUploadParamsForTemplate', params).then(async json => {
this.uploadParams = (json.postuploadtemplateresponse && json.postuploadtemplateresponse.getuploadparams) ? json.postuploadtemplateresponse.getuploadparams : ''
this.handleUpload()
if (this.userdataid !== null) {
this.linkUserdataToTemplate(this.userdataid, json.postuploadtemplateresponse.template[0].id)
}
this.ssvmOrigin = new URL(this.uploadParams.postURL).origin
const trusted = await probeSsvmCert(this.ssvmOrigin)
if (!trusted) {
this.ssvmCertUntrusted = true
return
}
this.handleUpload()
}).catch(error => {
this.$notifyError(error)
}).finally(() => {
Expand Down
Loading
Loading