Skip to content

Commit 40705b7

Browse files
committed
framework: register api and fix UI
1 parent eb960e3 commit 40705b7

4 files changed

Lines changed: 130 additions & 7 deletions

File tree

framework/extensions/src/main/java/org/apache/cloudstack/framework/extensions/manager/ExtensionsManagerImpl.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2260,6 +2260,7 @@ public List<Class<?>> getCommands() {
22602260
cmds.add(UpdateExtensionCmd.class);
22612261
cmds.add(RegisterExtensionCmd.class);
22622262
cmds.add(UnregisterExtensionCmd.class);
2263+
cmds.add(UpdateRegisteredExtensionCmd.class);
22632264
return cmds;
22642265
}
22652266

ui/src/views/extension/AddCustomAction.vue

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,25 @@
3434
api="listExtensions"
3535
:apiParams="extensionsApiParams"
3636
resourceType="extension"
37+
@change-option="updateResourceTypeByExtension"
3738
defaultIcon="appstore-add-outlined" />
3839
</a-form-item>
40+
<a-form-item name="resourcetype" ref="resourcetype">
41+
<template #label>
42+
<tooltip-label :title="$t('label.resourcetype')" :tooltip="apiParams.resourcetype.description"/>
43+
</template>
44+
<a-select
45+
v-model:value="form.resourcetype"
46+
:placeholder="apiParams.resourcetype.description"
47+
optionFilterProp="label"
48+
:filterOption="(input, option) => {
49+
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
50+
}" >
51+
<a-select-option v-for="opt in resourceTypeOptions" :key="opt" :label="opt">
52+
{{ opt }}
53+
</a-select-option>
54+
</a-select>
55+
</a-form-item>
3956
<a-form-item name="name" ref="name">
4057
<template #label>
4158
<tooltip-label :title="$t('label.name')" :tooltip="apiParams.name.description"/>
@@ -152,6 +169,7 @@ export default {
152169
data () {
153170
return {
154171
roleTypes: [],
172+
resourceTypeOptions: ['VirtualMachine', 'Network'],
155173
loading: false
156174
}
157175
},
@@ -165,6 +183,7 @@ export default {
165183
this.initForm()
166184
if (this.extension) {
167185
this.form.extensionid = this.extension.id
186+
this.updateResourceTypeByExtension(this.extension)
168187
}
169188
this.roleTypes = this.$fetchCustomActionRoleTypes()
170189
},
@@ -173,13 +192,30 @@ export default {
173192
this.formRef = ref()
174193
this.form = reactive({
175194
enabled: true,
176-
timeout: 5
195+
timeout: 5,
196+
resourcetype: 'VirtualMachine'
177197
})
178198
this.rules = reactive({
179199
extensionid: [{ required: true, message: `${this.$t('message.error.select')}` }],
180-
name: [{ required: true, message: `${this.$t('message.error.name')}` }]
200+
name: [{ required: true, message: `${this.$t('message.error.name')}` }],
201+
resourcetype: [{ required: true, message: `${this.$t('message.error.select')}` }]
181202
})
182203
},
204+
updateResourceTypeByExtension (selectedExtension) {
205+
const type = selectedExtension?.type
206+
if (type === 'NetworkOrchestrator') {
207+
this.resourceTypeOptions = ['Network']
208+
this.form.resourcetype = 'Network'
209+
} else if (type === 'Orchestrator') {
210+
this.resourceTypeOptions = ['VirtualMachine']
211+
this.form.resourcetype = 'VirtualMachine'
212+
} else {
213+
this.resourceTypeOptions = ['VirtualMachine', 'Network']
214+
if (!this.form.resourcetype) {
215+
this.form.resourcetype = 'VirtualMachine'
216+
}
217+
}
218+
},
183219
handleSubmit (e) {
184220
e.preventDefault()
185221
if (this.loading) return
@@ -189,6 +225,7 @@ export default {
189225
const params = {
190226
extensionid: values.extensionid || this.extension.id,
191227
name: values.name,
228+
resourcetype: values.resourcetype,
192229
enabled: values.enabled
193230
}
194231
const keys = ['description', 'allowedroletypes', 'successmessage', 'errormessage', 'timeout']

ui/src/views/extension/ExtensionResourcesTab.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
<template v-if="column.key === 'actions'">
3737
<span style="margin-right: 5px">
3838
<tooltip-button
39-
v-if="'updateRegisteredExtension' in $store.getters.apis"
4039
:tooltip="$t('label.action.update.extension.resource')"
4140
type="default"
4241
icon="edit-outlined"
@@ -70,6 +69,7 @@
7069
<a-modal
7170
v-if="updateModalVisible"
7271
:visible="updateModalVisible"
72+
:width="600"
7373
:title="$t('label.action.update.extension.resource')"
7474
:closable="true"
7575
:footer="null"

ui/src/views/offering/AddVpcOffering.vue

Lines changed: 89 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,8 @@
160160
<CheckBoxSelectPair
161161
:resourceKey="item.name"
162162
:checkBoxLabel="item.description"
163-
:forExternalNetProvider="form.provider === 'NSX' || form.provider === 'Netris'"
164-
:defaultCheckBoxValue="form.provider === 'NSX' || form.provider === 'Netris'"
163+
:forExternalNetProvider="form.provider === 'NSX' || form.provider === 'Netris' || isExternalNetworkProvider"
164+
:defaultCheckBoxValue="form.provider === 'NSX' || form.provider === 'Netris' || isExternalNetworkProvider"
165165
:selectOptions="item.provider"
166166
@handle-checkselectpair-change="handleSupportedServiceChange"/>
167167
</a-list-item>
@@ -336,13 +336,20 @@ export default {
336336
description: 'Netris',
337337
enabled: true
338338
},
339+
externalNetworkProviderObj: {
340+
name: '',
341+
description: 'External Network',
342+
enabled: true
343+
},
344+
externalNetworkSupportedServicesMap: {},
339345
nsxSupportedServicesMap: {},
340346
availableExtensionProviders: []
341347
}
342348
},
343349
computed: {
344350
isExternalNetworkProvider () {
345-
return this.availableExtensionProviders.some(e => e.name === this.provider)
351+
const selectedProvider = this.form?.provider || this.provider
352+
return this.availableExtensionProviders.some(e => e.name === selectedProvider)
346353
}
347354
},
348355
beforeCreate () {
@@ -457,7 +464,18 @@ export default {
457464
},
458465
fetchSupportedServiceData () {
459466
var services = []
460-
if (this.provider === 'NSX') {
467+
if (this.isExternalNetworkProvider) {
468+
const serviceMap = this._buildExternalVpcServiceMap()
469+
Object.keys(serviceMap).forEach(serviceName => {
470+
services.push({
471+
name: serviceName,
472+
enabled: true,
473+
provider: Array.isArray(serviceMap[serviceName])
474+
? serviceMap[serviceName]
475+
: [serviceMap[serviceName]]
476+
})
477+
})
478+
} else if (this.provider === 'NSX') {
461479
services.push({
462480
name: 'Dhcp',
463481
enabled: true,
@@ -648,9 +666,76 @@ export default {
648666
if (this.provider === 'NSX') {
649667
this.form.nsxsupportlb = true
650668
this.handleNsxLbService(true)
669+
} else if (this.isExternalNetworkProvider) {
670+
this._buildExternalVpcServiceMap()
651671
}
652672
this.fetchSupportedServiceData()
653673
},
674+
_getExtensionServices (extDef) {
675+
if (!extDef || !extDef.details) {
676+
return []
677+
}
678+
679+
const capsJson = extDef.details['network.capabilities']
680+
if (capsJson) {
681+
try {
682+
const caps = JSON.parse(capsJson)
683+
if (caps && Array.isArray(caps.services)) {
684+
return caps.services
685+
}
686+
} catch (e) {
687+
// Ignore malformed capabilities and fallback to network.services.
688+
}
689+
}
690+
691+
const servicesCsv = extDef.details['network.services']
692+
if (servicesCsv && typeof servicesCsv === 'string') {
693+
return servicesCsv.split(',').map(x => x.trim()).filter(x => x.length > 0)
694+
}
695+
return []
696+
},
697+
_buildExternalVpcServiceMap () {
698+
const selectedProvider = this.form?.provider || this.provider
699+
const extProviderObj = {
700+
name: selectedProvider,
701+
description: selectedProvider,
702+
enabled: true
703+
}
704+
const extWithFallbackProviders = [
705+
{ name: selectedProvider },
706+
{ name: 'VpcVirtualRouter' },
707+
{ name: 'ConfigDrive' }
708+
]
709+
const serviceMap = {
710+
Dhcp: extWithFallbackProviders,
711+
Dns: extWithFallbackProviders,
712+
UserData: extWithFallbackProviders
713+
}
714+
715+
const extDef = this.availableExtensionProviders.find(e => e.name === selectedProvider)
716+
const services = this._getExtensionServices(extDef)
717+
const allowedVpcServices = new Set([
718+
'Gateway', 'Lb', 'StaticNat', 'SourceNat', 'NetworkACL', 'PortForwarding', 'Vpn'
719+
])
720+
721+
services.forEach(service => {
722+
if (allowedVpcServices.has(service)) {
723+
serviceMap[service] = [{ name: selectedProvider }]
724+
}
725+
})
726+
727+
// Fallback for older extensions that only declare partial details.
728+
if (Object.keys(serviceMap).length <= 3) {
729+
serviceMap.SourceNat = [{ name: selectedProvider }]
730+
serviceMap.StaticNat = [{ name: selectedProvider }]
731+
serviceMap.PortForwarding = [{ name: selectedProvider }]
732+
serviceMap.NetworkACL = [{ name: selectedProvider }]
733+
}
734+
735+
this.externalNetworkProviderObj = extProviderObj
736+
this.externalNetworkSupportedServicesMap = serviceMap
737+
return serviceMap
738+
},
654739
handleNsxLbService (supportLb) {
655740
console.log(supportLb)
656741
if (!supportLb) {

0 commit comments

Comments
 (0)