Merge release branch 4.18 to main

* 4.18:
  UI: Filter templates by zone and hypervisor type when reinstall a VM (#7739)
  KVM: fix SSVM starting when overprovisioning memory (#7663)
  pom.xml: add property project.systemvm.template.location (#7706)
  cloudutils: fix adding rocky9 host failure due to missing /etc/sysconfig/libvirtd (#7779)
  server: get id from persisted object ReservationVO (#7785)
  search in (too) large result sets (#7766)
  ui: fix 404 error when list volumes of system vms (#7772)
  packaging: install tzdata-java on centos7/centos8 (#7768)
This commit is contained in:
Daan Hoogland 2023-07-31 09:04:44 +02:00
commit d51d8a4a13
14 changed files with 188 additions and 130 deletions

View File

@ -122,7 +122,7 @@
<goal>wget</goal>
</goals>
<configuration>
<url>https://download.cloudstack.org/systemvm/${cs.version}/md5sum.txt</url>
<url>${project.systemvm.template.location}/${cs.version}/md5sum.txt</url>
<outputDirectory>${basedir}/dist/systemvm-templates/</outputDirectory>
<skipCache>true</skipCache>
<overwrite>true</overwrite>
@ -181,7 +181,7 @@
</goals>
<configuration>
<checkSignature>true</checkSignature>
<url>https://download.cloudstack.org/systemvm/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-kvm.qcow2.bz2</url>
<url>${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-kvm.qcow2.bz2</url>
<outputDirectory>${basedir}/dist/systemvm-templates/</outputDirectory>
<md5>${kvm.checksum}</md5>
</configuration>
@ -217,7 +217,7 @@
</goals>
<configuration>
<checkSignature>true</checkSignature>
<url>https://download.cloudstack.org/systemvm/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-vmware.ova</url>
<url>${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-vmware.ova</url>
<outputDirectory>${basedir}/dist/systemvm-templates/</outputDirectory>
<md5>${vmware.checksum}</md5>
</configuration>
@ -253,7 +253,7 @@
</goals>
<configuration>
<checkSignature>true</checkSignature>
<url>https://download.cloudstack.org/systemvm/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-xen.vhd.bz2</url>
<url>${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-xen.vhd.bz2</url>
<outputDirectory>${basedir}/dist/systemvm-templates/</outputDirectory>
<md5>${xen.checksum}</md5>
</configuration>
@ -289,7 +289,7 @@
</goals>
<configuration>
<checkSignature>true</checkSignature>
<url>https://download.cloudstack.org/systemvm/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-ovm.raw.bz2</url>
<url>${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-ovm.raw.bz2</url>
<outputDirectory>${basedir}/dist/systemvm-templates/</outputDirectory>
<md5>${ovm.checksum}</md5>
</configuration>
@ -325,7 +325,7 @@
</goals>
<configuration>
<checkSignature>true</checkSignature>
<url>https://download.cloudstack.org/systemvm/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-hyperv.vhd.zip</url>
<url>${project.systemvm.template.location}/${cs.version}/systemvmtemplate-${cs.version}.${patch.version}-hyperv.vhd.zip</url>
<outputDirectory>${basedir}/dist/systemvm-templates/</outputDirectory>
<md5>${hyperv.checksum}</md5>
</configuration>

View File

@ -61,6 +61,7 @@ intelligent IaaS cloud implementation.
%package management
Summary: CloudStack management server UI
Requires: java-11-openjdk
Requires: tzdata-java
Requires: python
Requires: python3
Requires: bash
@ -107,6 +108,7 @@ The Apache CloudStack files shared between agent and management server
Summary: CloudStack Agent for KVM hypervisors
Requires: openssh-clients
Requires: java-11-openjdk
Requires: tzdata-java
Requires: %{name}-common = %{_ver}
Requires: libvirt
Requires: bridge-utils
@ -142,6 +144,7 @@ The CloudStack baremetal agent
%package usage
Summary: CloudStack Usage calculation server
Requires: java-11-openjdk
Requires: tzdata-java
Group: System Environment/Libraries
%description usage
The CloudStack usage calculation service

View File

@ -53,6 +53,7 @@ intelligent IaaS cloud implementation.
%package management
Summary: CloudStack management server UI
Requires: java-11-openjdk
Requires: tzdata-java
Requires: python3
Requires: bash
Requires: gawk
@ -98,6 +99,7 @@ The Apache CloudStack files shared between agent and management server
Summary: CloudStack Agent for KVM hypervisors
Requires: (openssh-clients or openssh)
Requires: java-11-openjdk
Requires: tzdata-java
Requires: %{name}-common = %{_ver}
Requires: libvirt
Requires: ebtables
@ -134,6 +136,7 @@ The CloudStack baremetal agent
%package usage
Summary: CloudStack Usage calculation server
Requires: java-11-openjdk
Requires: tzdata-java
Group: System Environment/Libraries
%description usage
The CloudStack usage calculation service

View File

@ -2791,10 +2791,15 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
grd.setMemBalloning(!_noMemBalloon);
Long maxRam = ByteScaleUtils.bytesToKibibytes(vmTO.getMaxRam());
long maxRam = ByteScaleUtils.bytesToKibibytes(vmTO.getMaxRam());
long currRam = vmTO.getType() == VirtualMachine.Type.User ? getCurrentMemAccordingToMemBallooning(vmTO, maxRam) : maxRam;
if (s_logger.isTraceEnabled()) {
s_logger.trace(String.format("memory values for VM %s are %d/%d",vmTO.getName(),maxRam, currRam));
}
grd.setMemorySize(maxRam);
grd.setCurrentMem(getCurrentMemAccordingToMemBallooning(vmTO, maxRam));
grd.setCurrentMem(currRam);
int vcpus = vmTO.getCpus();
Integer maxVcpus = vmTO.getVcpuMaxLimit();

View File

@ -282,7 +282,7 @@ public class LibvirtVMDef {
@Override
public String toString() {
StringBuilder response = new StringBuilder();
response.append(String.format("<memory>%s</memory>\n", this.currentMemory));
response.append(String.format("<memory>%s</memory>\n", this.memory));
response.append(String.format("<currentMemory>%s</currentMemory>\n", this.currentMemory));
if (this.memory > this.currentMemory) {
@ -1238,7 +1238,7 @@ public class LibvirtVMDef {
@Override
public String toString() {
StringBuilder memBalloonBuilder = new StringBuilder();
memBalloonBuilder.append("<memballoon model='" + memBalloonModel + "'>\n");
memBalloonBuilder.append("<memballoon model='" + memBalloonModel + "' autodeflate='on'>\n");
if (StringUtils.isNotBlank(memBalloonStatsPeriod)) {
memBalloonBuilder.append("<stats period='" + memBalloonStatsPeriod +"'/>\n");
}

View File

@ -760,7 +760,7 @@ public class LibvirtComputingResourceTest {
private void verifyMemory(VirtualMachineTO to, Document domainDoc, String minRam) {
assertXpath(domainDoc, "/domain/maxMemory/text()", String.valueOf( to.getMaxRam() / 1024 ));
assertXpath(domainDoc, "/domain/memory/text()",minRam);
assertXpath(domainDoc, "/domain/currentMemory/text()",minRam);
assertXpath(domainDoc, "/domain/cpu/numa/cell/@memory", minRam);
assertXpath(domainDoc, "/domain/currentMemory/text()", minRam);
}

View File

@ -266,11 +266,11 @@ public class LibvirtVMDefTest extends TestCase {
assertEquals(bus, disk.getBusType());
assertEquals(DiskDef.DeviceType.DISK, disk.getDeviceType());
String xmlDef = disk.toString();
String resultingXml = disk.toString();
String expectedXml = "<disk device='disk' type='file'>\n<driver name='qemu' type='" + type.toString() + "' cache='" + cacheMode.toString() + "' />\n" +
"<source file='" + filePath + "'/>\n<target dev='" + diskLabel + "' bus='" + bus.toString() + "'/>\n</disk>\n";
assertEquals(xmlDef, expectedXml);
assertEquals(expectedXml, resultingXml);
}
@Test
@ -288,7 +288,7 @@ public class LibvirtVMDefTest extends TestCase {
"<secret type='passphrase' uuid='" + passphraseUuid + "' />\n" +
"</encryption>\n" +
"</disk>\n";
assertEquals(disk.toString(), expectedXML);
assertEquals(expectedXML, disk.toString());
}
@Test
@ -398,7 +398,7 @@ public class LibvirtVMDefTest extends TestCase {
LibvirtVMDef.setGlobalQemuVersion(2006000L);
LibvirtVMDef.setGlobalLibvirtVersion(9008L);
String xmlDef = disk.toString();
String resultingXml = disk.toString();
String expectedXml = "<disk device='disk' type='file'>\n<driver name='qemu' type='" + type.toString() + "' cache='none' />\n" +
"<source file='" + filePath + "'/>\n<target dev='" + diskLabel + "' bus='" + bus.toString() + "'/>\n" +
"<iotune>\n<read_bytes_sec>"+bytesReadRate+"</read_bytes_sec>\n<write_bytes_sec>"+bytesWriteRate+"</write_bytes_sec>\n" +
@ -408,29 +408,29 @@ public class LibvirtVMDefTest extends TestCase {
"<read_bytes_sec_max_length>"+bytesReadRateMaxLength+"</read_bytes_sec_max_length>\n<write_bytes_sec_max_length>"+bytesWriteRateMaxLength+"</write_bytes_sec_max_length>\n" +
"<read_iops_sec_max_length>"+iopsReadRateMaxLength+"</read_iops_sec_max_length>\n<write_iops_sec_max_length>"+iopsWriteRateMaxLength+"</write_iops_sec_max_length>\n</iotune>\n</disk>\n";
assertEquals(xmlDef, expectedXml);
assertEquals(expectedXml, resultingXml);
}
@Test
public void memBalloonDefTestNone() {
String expectedXml = "<memballoon model='none'>\n</memballoon>";
String expectedXml = "<memballoon model='none' autodeflate='on'>\n</memballoon>";
MemBalloonDef memBalloonDef = new MemBalloonDef();
memBalloonDef.defNoneMemBalloon();
String xmlDef = memBalloonDef.toString();
String resultingXml = memBalloonDef.toString();
assertEquals(xmlDef, expectedXml);
assertEquals(expectedXml, resultingXml);
}
@Test
public void memBalloonDefTestVirtio() {
String expectedXml = "<memballoon model='virtio'>\n<stats period='60'/>\n</memballoon>";
String expectedXml = "<memballoon model='virtio' autodeflate='on'>\n<stats period='60'/>\n</memballoon>";
MemBalloonDef memBalloonDef = new MemBalloonDef();
memBalloonDef.defVirtioMemBalloon("60");
String xmlDef = memBalloonDef.toString();
String resultingXml = memBalloonDef.toString();
assertEquals(xmlDef, expectedXml);
assertEquals(expectedXml, resultingXml);
}
@Test
@ -465,11 +465,11 @@ public class LibvirtVMDefTest extends TestCase {
int bytes = 2048;
LibvirtVMDef.RngDef def = new LibvirtVMDef.RngDef(path, backendModel, bytes, period);
assertEquals(def.getPath(), path);
assertEquals(def.getRngBackendModel(), backendModel);
assertEquals(def.getRngModel(), LibvirtVMDef.RngDef.RngModel.VIRTIO);
assertEquals(def.getRngRateBytes(), bytes);
assertEquals(def.getRngRatePeriod(), period);
assertEquals(path, def.getPath());
assertEquals(backendModel, def.getRngBackendModel());
assertEquals(LibvirtVMDef.RngDef.RngModel.VIRTIO, def.getRngModel());
assertEquals(bytes, def.getRngRateBytes());
assertEquals(period, def.getRngRatePeriod());
}
@Test
@ -493,8 +493,8 @@ public class LibvirtVMDefTest extends TestCase {
LibvirtVMDef.WatchDogDef.WatchDogAction action = LibvirtVMDef.WatchDogDef.WatchDogAction.RESET;
LibvirtVMDef.WatchDogDef def = new LibvirtVMDef.WatchDogDef(action, model);
assertEquals(def.getModel(), model);
assertEquals(def.getAction(), action);
assertEquals(model, def.getModel());
assertEquals(action, def.getAction());
}
@Test
@ -505,6 +505,6 @@ public class LibvirtVMDefTest extends TestCase {
"<address type='pci' domain='0x0000' bus='0x00' slot='0x09' function='0x0'/>\n" +
"<driver queues='4'/>\n" +
"</controller>\n";
assertEquals(str, expected);
assertEquals(expected, str);
}
}

View File

@ -49,6 +49,7 @@
<!-- keep in alphabetic order -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<project.systemvm.template.location>https://download.cloudstack.org/systemvm</project.systemvm.template.location>
<project.systemvm.template.version>4.18.0.0</project.systemvm.template.version>
<sonar.organization>apache</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>

View File

@ -15,6 +15,7 @@
# specific language governing permissions and limitations
# under the License.
import re
import os
import tempfile
import shutil
from .utilities import bash
@ -59,39 +60,42 @@ class configFileOps:
return ""
def save(self):
fp = open(self.fileName, "r")
newLines = []
for line in fp.readlines():
matched = False
for entry in self.entries:
if entry.op == "add":
if entry.separator == "=":
matchString = "^\ *" + entry.name + ".*"
elif entry.separator == " ":
matchString = "^\ *" + entry.name + "\ *" + entry.value
else:
if entry.separator == "=":
matchString = "^\ *" + entry.name + "\ *=\ *" + entry.value
if os.path.exists(self.fileName) and os.path.isfile(self.fileName):
fp = open(self.fileName, "r")
for line in fp.readlines():
matched = False
for entry in self.entries:
if entry.op == "add":
if entry.separator == "=":
matchString = "^\ *" + entry.name + ".*"
elif entry.separator == " ":
matchString = "^\ *" + entry.name + "\ *" + entry.value
else:
matchString = "^\ *" + entry.name + "\ *" + entry.value
if entry.separator == "=":
matchString = "^\ *" + entry.name + "\ *=\ *" + entry.value
else:
matchString = "^\ *" + entry.name + "\ *" + entry.value
match = re.match(matchString, line)
if match is not None:
if entry.op == "add" and entry.separator == "=":
newline = "\n" + entry.name + "=" + entry.value + "\n"
entry.setState("set")
newLines.append(newline)
self.backups.append([line, newline])
matched = True
break
elif entry.op == "rm":
entry.setState("set")
self.backups.append([line, None])
matched = True
break
match = re.match(matchString, line)
if match is not None:
if entry.op == "add" and entry.separator == "=":
newline = "\n" + entry.name + "=" + entry.value + "\n"
entry.setState("set")
newLines.append(newline)
self.backups.append([line, newline])
matched = True
break
elif entry.op == "rm":
entry.setState("set")
self.backups.append([line, None])
matched = True
break
if not matched:
newLines.append(line)
if not matched:
newLines.append(line)
fp.close()
for entry in self.entries:
if entry.getState() != "set":
@ -101,8 +105,6 @@ class configFileOps:
self.backups.append([None, newline])
entry.setState("set")
fp.close()
open(self.fileName, "w").writelines(newLines)
def replace_line(self, startswith,stanza,always_add=False):

View File

@ -213,7 +213,7 @@ public class KVMGuru extends HypervisorGuruBase implements HypervisorGuru {
Integer maxHostCpuCore = max.second();
long minMemory = virtualMachineTo.getMinRam();
Long maxMemory = minMemory;
Long maxMemory = virtualMachineTo.getMaxRam();
int minCpuCores = virtualMachineTo.getCpus();
Integer maxCpuCores = minCpuCores;

View File

@ -76,7 +76,7 @@ public class CheckedReservation implements AutoCloseable, ResourceReservation {
resourceLimitService.checkResourceLimit(account,resourceType,amount);
ReservationVO reservationVO = new ReservationVO(account.getAccountId(), account.getDomainId(), resourceType, amount);
this.reservation = reservationDao.persist(reservationVO);
CallContext.current().putContextParameter(getContextParameterKey(), reservationVO.getId());
CallContext.current().putContextParameter(getContextParameterKey(), reservation.getId());
} catch (NullPointerException npe) {
throw new CloudRuntimeException("not enough means to check limits", npe);
} finally {

View File

@ -65,7 +65,8 @@
:filterOption="(input, option) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}"
:loading="field.loading">
:loading="field.loading"
@input="onchange($event, field.name)">
<a-select-option
v-for="(opt, idx) in field.opts"
:key="idx"
@ -236,6 +237,9 @@ export default {
}
},
methods: {
onchange: async function (event, fieldname) {
this.fetchDynamicFieldData(fieldname, event.target.value)
},
onVisibleForm () {
this.visibleFilter = !this.visibleFilter
if (!this.visibleFilter) return
@ -254,7 +258,7 @@ export default {
}
return this.$t('label.' + fieldName)
},
async initFormFieldData () {
initFields () {
const arrayField = []
this.fields = []
this.searchFilters.forEach(item => {
@ -291,14 +295,9 @@ export default {
})
arrayField.push(item)
})
const promises = []
let zoneIndex = -1
let domainIndex = -1
let podIndex = -1
let clusterIndex = -1
let groupIndex = -1
return arrayField
},
fetchStaticFieldData (arrayField) {
if (arrayField.includes('type')) {
if (this.$route.path === '/guestnetwork' || this.$route.path.includes('/guestnetwork/')) {
const typeIndex = this.fields.findIndex(item => item.name === 'type')
@ -322,36 +321,6 @@ export default {
this.fields[levelIndex].loading = false
}
if (arrayField.includes('zoneid')) {
zoneIndex = this.fields.findIndex(item => item.name === 'zoneid')
this.fields[zoneIndex].loading = true
promises.push(await this.fetchZones())
}
if (arrayField.includes('domainid')) {
domainIndex = this.fields.findIndex(item => item.name === 'domainid')
this.fields[domainIndex].loading = true
promises.push(await this.fetchDomains())
}
if (arrayField.includes('podid')) {
podIndex = this.fields.findIndex(item => item.name === 'podid')
this.fields[podIndex].loading = true
promises.push(await this.fetchPods())
}
if (arrayField.includes('clusterid')) {
clusterIndex = this.fields.findIndex(item => item.name === 'clusterid')
this.fields[clusterIndex].loading = true
promises.push(await this.fetchClusters())
}
if (arrayField.includes('groupid')) {
groupIndex = this.fields.findIndex(item => item.name === 'groupid')
this.fields[groupIndex].loading = true
promises.push(await this.fetchInstanceGroups())
}
if (arrayField.includes('entitytype')) {
const entityTypeIndex = this.fields.findIndex(item => item.name === 'entitytype')
this.fields[entityTypeIndex].loading = true
@ -374,6 +343,44 @@ export default {
]
this.fields[resourceTypeIndex].loading = false
}
},
async fetchDynamicFieldData (arrayField, searchKeyword) {
const promises = []
let zoneIndex = -1
let domainIndex = -1
let podIndex = -1
let clusterIndex = -1
let groupIndex = -1
if (arrayField.includes('zoneid')) {
zoneIndex = this.fields.findIndex(item => item.name === 'zoneid')
this.fields[zoneIndex].loading = true
promises.push(await this.fetchZones(searchKeyword))
}
if (arrayField.includes('domainid')) {
domainIndex = this.fields.findIndex(item => item.name === 'domainid')
this.fields[domainIndex].loading = true
promises.push(await this.fetchDomains(searchKeyword))
}
if (arrayField.includes('podid')) {
podIndex = this.fields.findIndex(item => item.name === 'podid')
this.fields[podIndex].loading = true
promises.push(await this.fetchPods(searchKeyword))
}
if (arrayField.includes('clusterid')) {
clusterIndex = this.fields.findIndex(item => item.name === 'clusterid')
this.fields[clusterIndex].loading = true
promises.push(await this.fetchClusters(searchKeyword))
}
if (arrayField.includes('groupid')) {
groupIndex = this.fields.findIndex(item => item.name === 'groupid')
this.fields[groupIndex].loading = true
promises.push(await this.fetchInstanceGroups(searchKeyword))
}
Promise.all(promises).then(response => {
if (zoneIndex > -1) {
@ -425,6 +432,13 @@ export default {
this.fillFormFieldValues()
})
},
initFormFieldData () {
const arrayField = this.initFields()
this.fetchStaticFieldData(arrayField)
this.fetchDynamicFieldData(arrayField)
},
sortArray (data, key = 'name') {
return data.sort(function (a, b) {
if (a[key] < b[key]) { return -1 }
@ -447,9 +461,9 @@ export default {
this.inputKey = this.fieldValues['tags[0].key'] || null
this.inputValue = this.fieldValues['tags[0].value'] || null
},
fetchZones () {
fetchZones (searchKeyword) {
return new Promise((resolve, reject) => {
api('listZones', { showicon: true }).then(json => {
api('listZones', { showicon: true, keyword: searchKeyword }).then(json => {
const zones = json.listzonesresponse.zone
resolve({
type: 'zoneid',
@ -460,9 +474,9 @@ export default {
})
})
},
fetchDomains () {
fetchDomains (searchKeyword) {
return new Promise((resolve, reject) => {
api('listDomains', { listAll: true, showicon: true }).then(json => {
api('listDomains', { listAll: true, showicon: true, keyword: searchKeyword }).then(json => {
const domain = json.listdomainsresponse.domain
resolve({
type: 'domainid',
@ -473,9 +487,9 @@ export default {
})
})
},
fetchPods () {
fetchPods (searchKeyword) {
return new Promise((resolve, reject) => {
api('listPods').then(json => {
api('listPods', { keyword: searchKeyword }).then(json => {
const pods = json.listpodsresponse.pod
resolve({
type: 'podid',
@ -486,9 +500,9 @@ export default {
})
})
},
fetchClusters () {
fetchClusters (searchKeyword) {
return new Promise((resolve, reject) => {
api('listClusters').then(json => {
api('listClusters', { keyword: searchKeyword }).then(json => {
const clusters = json.listclustersresponse.cluster
resolve({
type: 'clusterid',
@ -499,9 +513,9 @@ export default {
})
})
},
fetchInstanceGroups () {
fetchInstanceGroups (searchKeyword) {
return new Promise((resolve, reject) => {
api('listInstanceGroups', { listAll: true }).then(json => {
api('listInstanceGroups', { listAll: true, keyword: searchKeyword }).then(json => {
const instancegroups = json.listinstancegroupsresponse.instancegroup
resolve({
type: 'groupid',

View File

@ -164,6 +164,14 @@ export default {
message: 'message.reinstall.vm',
dataView: true,
args: ['virtualmachineid', 'templateid'],
filters: (record) => {
var filters = {}
var filterParams = {}
filterParams.hypervisortype = record.hypervisor
filterParams.zoneid = record.zoneid
filters.templateid = filterParams
return filters
},
show: (record) => { return ['Running', 'Stopped'].includes(record.state) },
mapping: {
virtualmachineid: {

View File

@ -794,6 +794,10 @@ export default {
this.dataView = false
}
if (this.dataView && ['Admin'].includes(this.$store.getters.userInfo.roletype) && this.routeName === 'volume') {
params.listsystemvms = true
}
if ('listview' in this.$refs && this.$refs.listview) {
this.$refs.listview.resetSelection()
}
@ -1047,7 +1051,6 @@ export default {
this.setModalWidthByScreen()
},
execAction (action, isGroupAction) {
const self = this
this.formRef = ref()
this.form = reactive({})
this.rules = reactive({})
@ -1085,6 +1088,7 @@ export default {
return 0
})
this.currentAction.paramFields = []
this.currentAction.paramFilters = []
if ('message' in action) {
var message = action.message
if (typeof action.message === 'function') {
@ -1092,6 +1096,29 @@ export default {
}
action.message = message
}
this.getArgs(action, isGroupAction, paramFields)
this.getFilters(action, isGroupAction, paramFields)
this.getFirstIndexFocus()
this.showAction = true
const listIconForFillValues = ['copy-outlined', 'CopyOutlined', 'edit-outlined', 'EditOutlined', 'share-alt-outlined', 'ShareAltOutlined']
for (const param of this.currentAction.paramFields) {
if (param.type === 'list' && ['tags', 'hosttags', 'storagetags', 'files'].includes(param.name)) {
param.type = 'string'
}
this.setRules(param)
if (param.type === 'uuid' || param.type === 'list' || param.name === 'account' || (this.currentAction.mapping && param.name in this.currentAction.mapping)) {
this.listUuidOpts(param, this.currentAction.paramFilters[param.name])
}
}
this.actionLoading = false
if (action.dataView && listIconForFillValues.includes(action.icon)) {
this.fillEditFormFieldValues()
}
},
getArgs (action, isGroupAction, paramFields) {
const self = this
if ('args' in action) {
var args = action.args
if (typeof action.args === 'function') {
@ -1121,22 +1148,14 @@ export default {
})
}
}
this.getFirstIndexFocus()
this.showAction = true
const listIconForFillValues = ['copy-outlined', 'CopyOutlined', 'edit-outlined', 'EditOutlined', 'share-alt-outlined', 'ShareAltOutlined']
for (const param of this.currentAction.paramFields) {
if (param.type === 'list' && ['tags', 'hosttags', 'storagetags', 'files'].includes(param.name)) {
param.type = 'string'
},
getFilters (action, isGroupAction, paramFields) {
if ('filters' in action) {
var filters = action.filters
if (typeof action.filters === 'function') {
filters = action.filters(action.resource, this.$store.getters, isGroupAction)
}
this.setRules(param)
if (param.type === 'uuid' || param.type === 'list' || param.name === 'account' || (this.currentAction.mapping && param.name in this.currentAction.mapping)) {
this.listUuidOpts(param)
}
}
this.actionLoading = false
if (action.dataView && listIconForFillValues.includes(action.icon)) {
this.fillEditFormFieldValues()
this.currentAction.paramFilters = filters
}
},
getFirstIndexFocus () {
@ -1149,13 +1168,16 @@ export default {
}
}
},
listUuidOpts (param) {
listUuidOpts (param, filters) {
if (this.currentAction.mapping && param.name in this.currentAction.mapping && !this.currentAction.mapping[param.name].api) {
return
}
var paramName = param.name
var extractedParamName = paramName.replace('ids', '').replace('id', '').toLowerCase()
var params = { listall: true }
for (const filter in filters) {
params[filter] = filters[filter]
}
const possibleName = 'list' + extractedParamName + 's'
var showIcon = false
if (this.$showIcon(extractedParamName)) {