Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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)
}
}
49 changes: 41 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 :href="ssvmOrigin" target="_blank" rel="noopener noreferrer">
<a-button>{{ $t('label.ssvm.open.cert.page') }}</a-button>
</a>
Comment thread
abh1sar marked this conversation as resolved.
Outdated
<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 @@ -346,6 +363,8 @@ export default {
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 +508,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 +532,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 +560,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 +616,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 :href="ssvmOrigin" target="_blank" rel="noopener noreferrer">
<a-button>{{ $t('label.ssvm.open.cert.page') }}</a-button>
</a>
Comment thread
abh1sar marked this conversation as resolved.
Outdated
<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