This is an automated email from the ASF dual-hosted git repository.
sureshanaparti pushed a commit to branch 4.16
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.16 by this push:
new 0f926b5 UI: Add s3 provider option to create secondary storage (#5726)
0f926b5 is described below
commit 0f926b5d68975e93f924e41ae6f4b137868f58c1
Author: Hoang Nguyen <[email protected]>
AuthorDate: Mon Jan 10 20:31:53 2022 +0700
UI: Add s3 provider option to create secondary storage (#5726)
* add s3 provider option to create secondary storage
* fixes label name
* add storagepolicy for swift provider
---
ui/public/locales/en.json | 3 +
ui/src/views/infra/AddSecondaryStorage.vue | 178 +++++++++++++++++++--
ui/src/views/infra/zone/StaticInputsForm.vue | 24 ++-
ui/src/views/infra/zone/ZoneWizardAddResources.vue | 33 ++--
ui/src/views/infra/zone/ZoneWizardLaunchZone.vue | 6 +
5 files changed, 212 insertions(+), 32 deletions(-)
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index 9dbbd85..ad01734 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -2949,6 +2949,9 @@
"message.error.retrieve.kubeconfig": "Unable to retrieve Kubernetes cluster
config",
"message.error.s3nfs.path": "Please enter S3 NFS Path",
"message.error.s3nfs.server": "Please enter S3 NFS Server",
+"message.error.swift.account": "Please enter account",
+"message.error.swift.key": "Please enter key",
+"message.error.swift.username": "Please enter username",
"message.error.save.setting": "There was an error saving this setting.",
"message.error.sbdomain": "Please enter SMB Domain",
"message.error.sbdomain.password": "Please enter SMB Domain Password",
diff --git a/ui/src/views/infra/AddSecondaryStorage.vue
b/ui/src/views/infra/AddSecondaryStorage.vue
index 0d34e6f..f7b6203 100644
--- a/ui/src/views/infra/AddSecondaryStorage.vue
+++ b/ui/src/views/infra/AddSecondaryStorage.vue
@@ -42,7 +42,7 @@
>{{ prov }}</a-select-option>
</a-select>
</a-form-item>
- <div v-if="provider !== 'Swift'">
+ <div v-if="!['Swift', 'S3'].includes(provider)">
<a-form-item :label="$t('label.zone')">
<a-select
v-decorator="[
@@ -163,6 +163,91 @@
/>
</a-form-item>
</div>
+ <div v-if="provider === 'S3'">
+ <a-form-item :label="$t('label.zone')">
+ <a-select
+ v-decorator="[
+ 'zone',
+ {
+ initialValue: this.zoneSelected,
+ rules: [{ required: true, message:
`${$t('label.required')}`}]
+ }]"
+ @change="val => { zoneSelected = val }"
+ showSearch
+ optionFilterProp="children"
+ :filterOption="(input, option) => {
+ return
option.componentOptions.propsData.label.toLowerCase().indexOf(input.toLowerCase())
>= 0
+ }" >
+ <a-select-option
+ :value="zone.id"
+ v-for="(zone) in zones"
+ :key="zone.id"
+ :label="zone.name">
+ <span>
+ <resource-icon v-if="zone.icon"
:image="zone.icon.base64image" size="1x" style="margin-right: 5px"/>
+ <a-icon v-else type="global" style="margin-right: 5px" />
+ {{ zone.name }}
+ </span>
+ </a-select-option>
+ </a-select>
+ </a-form-item>
+ <a-form-item :label="$t('label.s3.access.key')">
+ <a-input
+ v-decorator="[
+ 'secondaryStorageAccessKey',
+ {
+ rules: [{ required: true, message: `${$t('label.required')}`
}]
+ }]"/>
+ </a-form-item>
+ <a-form-item :label="$t('label.s3.secret.key')">
+ <a-input
+ v-decorator="[
+ 'secondaryStorageSecretKey',
+ {
+ rules: [{ required: true, message: `${$t('label.required')}`
}]
+ }]"/>
+ </a-form-item>
+ <a-form-item :label="$t('label.s3.bucket')">
+ <a-input
+ v-decorator="[
+ 'secondaryStorageBucket',
+ {
+ rules: [{ required: true, message: `${$t('label.required')}`
}]
+ }]"/>
+ </a-form-item>
+ <a-form-item :label="$t('label.s3.endpoint')">
+ <a-input v-decorator="['secondaryStorageEndpoint']"/>
+ </a-form-item>
+ <a-form-item :label="$t('label.s3.use.https')">
+ <a-switch v-decorator="['secondaryStorageHttps', { initialValue:
true }]" :default-checked="true" />
+ </a-form-item>
+ <a-form-item :label="$t('label.s3.connection.timeout')">
+ <a-input v-decorator="['secondaryStorageConnectionTimeout']"/>
+ </a-form-item>
+ <a-form-item :label="$t('label.s3.max.error.retry')">
+ <a-input v-decorator="['secondaryStorageMaxError']"/>
+ </a-form-item>
+ <a-form-item :label="$t('label.s3.socket.timeout')">
+ <a-input v-decorator="['secondaryStorageSocketTimeout']"/>
+ </a-form-item>
+ <a-form-item
:label="$t('label.create.nfs.secondary.staging.storage')">
+ <a-switch v-decorator="['secondaryStorageNFSStaging']"
@change="val => secondaryStorageNFSStaging = val" />
+ </a-form-item>
+ </div>
+ <div v-if="secondaryStorageNFSStaging">
+ <a-form-item :label="$t('label.s3.nfs.server')">
+ <a-input
+ v-decorator="['secondaryStorageNFSServer', {
+ rules: [{ required: true, message: `${$t('label.required')}` }]
+ }]"/>
+ </a-form-item>
+ <a-form-item :label="$t('label.s3.nfs.path')">
+ <a-input
+ v-decorator="['secondaryStorageNFSPath', {
+ rules: [{ required: true, message: `${$t('label.required')}` }]
+ }]"/>
+ </a-form-item>
+ </div>
<div :span="24" class="action-button">
<a-button @click="closeModal">{{ $t('label.cancel') }}</a-button>
<a-button type="primary" ref="submit" @click="handleSubmit">{{
$t('label.ok') }}</a-button>
@@ -189,11 +274,12 @@ export default {
inject: ['parentFetchData'],
data () {
return {
- providers: ['NFS', 'SMB/CIFS', 'Swift'],
+ providers: ['NFS', 'SMB/CIFS', 'S3', 'Swift'],
provider: '',
zones: [],
zoneSelected: '',
- loading: false
+ loading: false,
+ secondaryStorageNFSStaging: false
}
},
beforeCreate () {
@@ -245,7 +331,7 @@ export default {
handleSubmit (e) {
e.preventDefault()
if (this.loading) return
- this.form.validateFieldsAndScroll((err, values) => {
+ this.form.validateFieldsAndScroll(async (err, values) => {
if (err) {
return
}
@@ -283,26 +369,100 @@ export default {
data['details[' + index.toString() + '].key'] = key
data['details[' + index.toString() + '].value'] = swiftParams[key]
})
+ } else if (provider === 'S3') {
+ let detailIdx = 0
+ const s3Params = {
+ accesskey: values.secondaryStorageAccessKey,
+ secretkey: values.secondaryStorageSecretKey,
+ bucket: values.secondaryStorageBucket,
+ usehttps: values.secondaryStorageHttps ?
values.secondaryStorageHttps : false
+ }
+ Object.keys(s3Params).forEach((key, index) => {
+ data['details[' + index.toString() + '].key'] = key
+ data['details[' + index.toString() + '].value'] = s3Params[key]
+ detailIdx = index
+ })
+
+ if (values.secondaryStorageEndpoint &&
values.secondaryStorageEndpoint.length > 0) {
+ detailIdx++
+ data['details[' + detailIdx.toString() + '].key'] = 'endpoint'
+ data['details[' + detailIdx.toString() + '].value'] =
values.secondaryStorageEndpoint
+ }
+
+ if (values.secondaryStorageConnectionTimeout &&
values.secondaryStorageConnectionTimeout.length > 0) {
+ detailIdx++
+ data['details[' + detailIdx.toString() + '].key'] =
'connectiontimeout'
+ data['details[' + detailIdx.toString() + '].value'] =
values.secondaryStorageConnectionTimeout
+ }
+
+ if (values.secondaryStorageMaxError &&
values.secondaryStorageMaxError.length > 0) {
+ detailIdx++
+ data['details[' + detailIdx.toString() + '].key'] = 'maxerrorretry'
+ data['details[' + detailIdx.toString() + '].value'] =
values.secondaryStorageMaxError
+ }
+
+ if (values.secondaryStorageSocketTimeout &&
values.secondaryStorageSocketTimeout.length > 0) {
+ detailIdx++
+ data['details[' + detailIdx.toString() + '].key'] = 'sockettimeout'
+ data['details[' + detailIdx.toString() + '].value'] =
values.secondaryStorageSocketTimeout
+ }
}
- data.url = url
+ if (provider !== 'S3') {
+ data.url = url
+ }
data.provider = provider
- if (values.zone && provider !== 'Swift') {
+ if (values.zone && !['Swift', 'S3'].includes(provider)) {
data.zoneid = values.zone
}
+ const nfsParams = {}
+ if (values.secondaryStorageNFSStaging) {
+ const nfsServer = values.secondaryStorageNFSServer
+ const path = values.secondaryStorageNFSPath
+ const nfsUrl = this.nfsURL(nfsServer, path)
+
+ nfsParams.provider = 'nfs'
+ nfsParams.zoneid = values.zone
+ nfsParams.url = nfsUrl
+ }
+
this.loading = true
- api('addImageStore', data).then(json => {
+
+ try {
+ await this.addImageStore(data)
+
+ if (values.secondaryStorageNFSStaging) {
+ await this.createSecondaryStagingStore(nfsParams)
+ }
this.$notification.success({
message: this.$t('label.add.secondary.storage'),
description: this.$t('label.add.secondary.storage')
})
+ this.loading = false
this.closeModal()
this.parentFetchData()
- }).catch(error => {
+ } catch (error) {
this.$notifyError(error)
- }).finally(() => {
this.loading = false
+ }
+ })
+ },
+ addImageStore (params) {
+ return new Promise((resolve, reject) => {
+ api('addImageStore', params).then(json => {
+ resolve()
+ }).catch(error => {
+ reject(error)
+ })
+ })
+ },
+ createSecondaryStagingStore (params) {
+ return new Promise((resolve, reject) => {
+ api('createSecondaryStagingStore', params).then(json => {
+ resolve()
+ }).catch(error => {
+ reject(error)
})
})
}
diff --git a/ui/src/views/infra/zone/StaticInputsForm.vue
b/ui/src/views/infra/zone/StaticInputsForm.vue
index 97590a3..dc93c09 100644
--- a/ui/src/views/infra/zone/StaticInputsForm.vue
+++ b/ui/src/views/infra/zone/StaticInputsForm.vue
@@ -216,21 +216,19 @@ export default {
if (!conditions || Object.keys(conditions).length === 0) {
return true
}
- let isShow = false
+ let isShow = true
Object.keys(conditions).forEach(key => {
- const condition = conditions[key]
- const fieldVal = this.form.getFieldValue(key)
- ? this.form.getFieldValue(key)
- : (this.prefillContent[key] ? this.prefillContent[key].value : null)
- if (Array.isArray(condition) && condition.includes(fieldVal)) {
- isShow = true
- return false
- } else if (!Array.isArray(condition) && fieldVal === condition) {
- isShow = true
- return false
+ if (isShow) {
+ const condition = conditions[key]
+ const fieldVal = this.form.getFieldValue(key)
+ ? this.form.getFieldValue(key)
+ : (this.prefillContent[key] ? this.prefillContent[key].value :
null)
+ if (Array.isArray(condition) && !condition.includes(fieldVal)) {
+ isShow = false
+ } else if (!Array.isArray(condition) && fieldVal !== condition) {
+ isShow = false
+ }
}
-
- return true
})
return isShow
diff --git a/ui/src/views/infra/zone/ZoneWizardAddResources.vue
b/ui/src/views/infra/zone/ZoneWizardAddResources.vue
index 5804676..ee16e26 100644
--- a/ui/src/views/infra/zone/ZoneWizardAddResources.vue
+++ b/ui/src/views/infra/zone/ZoneWizardAddResources.vue
@@ -570,7 +570,7 @@ export default {
}
},
{
- title: 'label.s3.access_key',
+ title: 'label.s3.access.key',
key: 'secondaryStorageAccessKey',
required: true,
placeHolder: 'message.error.access.key',
@@ -579,7 +579,7 @@ export default {
}
},
{
- title: 'label.s3.secret_key',
+ title: 'label.s3.secret.key',
key: 'secondaryStorageSecretKey',
required: true,
placeHolder: 'message.error.secret.key',
@@ -605,7 +605,7 @@ export default {
}
},
{
- title: 'label.s3.use_https',
+ title: 'label.s3.use.https',
key: 'secondaryStorageHttps',
required: false,
switch: true,
@@ -615,7 +615,7 @@ export default {
}
},
{
- title: 'label.s3.connection_timeoutt',
+ title: 'label.s3.connection.timeout',
key: 'secondaryStorageConnectionTimeout',
required: false,
display: {
@@ -623,7 +623,7 @@ export default {
}
},
{
- title: 'label.s3.max_error_retry',
+ title: 'label.s3.max.error.retry',
key: 'secondaryStorageMaxError',
required: false,
display: {
@@ -631,7 +631,7 @@ export default {
}
},
{
- title: 'label.s3.socket_timeout',
+ title: 'label.s3.socket.timeout',
key: 'secondaryStorageSocketTimeout',
required: false,
display: {
@@ -653,7 +653,8 @@ export default {
required: true,
placeHolder: 'message.error.s3nfs.server',
display: {
- secondaryStorageProvider: ['S3']
+ secondaryStorageProvider: ['S3'],
+ secondaryStorageNFSStaging: true
}
},
{
@@ -662,7 +663,8 @@ export default {
required: true,
placeHolder: 'message.error.s3nfs.path',
display: {
- secondaryStorageProvider: ['S3']
+ secondaryStorageProvider: ['S3'],
+ secondaryStorageNFSStaging: true
}
},
{
@@ -677,7 +679,8 @@ export default {
{
title: 'label.account',
key: 'secondaryStorageAccount',
- required: false,
+ required: true,
+ placeHolder: 'message.error.swift.account',
display: {
secondaryStorageProvider: ['Swift']
}
@@ -685,7 +688,8 @@ export default {
{
title: 'label.username',
key: 'secondaryStorageUsername',
- required: false,
+ required: true,
+ placeHolder: 'message.error.swift.username',
display: {
secondaryStorageProvider: ['Swift']
}
@@ -693,6 +697,15 @@ export default {
{
title: 'label.key',
key: 'secondaryStorageKey',
+ required: true,
+ placeHolder: 'message.error.swift.key',
+ display: {
+ secondaryStorageProvider: ['Swift']
+ }
+ },
+ {
+ title: 'label.storagepolicy',
+ key: 'secondaryStoragePolicy',
required: false,
display: {
secondaryStorageProvider: ['Swift']
diff --git a/ui/src/views/infra/zone/ZoneWizardLaunchZone.vue
b/ui/src/views/infra/zone/ZoneWizardLaunchZone.vue
index eb18181..d22cd35 100644
--- a/ui/src/views/infra/zone/ZoneWizardLaunchZone.vue
+++ b/ui/src/views/infra/zone/ZoneWizardLaunchZone.vue
@@ -1471,6 +1471,12 @@ export default {
params['details[' + index.toString() + '].value'] =
this.prefillContent.secondaryStorageKey.value
index++
}
+ if (this.prefillContent.secondaryStoragePolicy &&
+ this.prefillContent.secondaryStoragePolicy.value.length > 0) {
+ params['details[' + index.toString() + '].key'] = 'storagepolicy'
+ params['details[' + index.toString() + '].value'] =
this.prefillContent.secondaryStoragePolicy.value
+ index++
+ }
}
try {