Merge branch '4.16' into main

This commit is contained in:
Suresh Kumar Anaparti 2022-01-11 12:46:33 +05:30
commit e1b56be67c
No known key found for this signature in database
GPG Key ID: D7CEAE3A9E71D0AA
23 changed files with 395 additions and 118 deletions

View File

@ -47,8 +47,8 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
RevertSnapshotting("There is a snapshot created on this volume, the volume is being reverting from snapshot"),
Resizing("The volume is being resized"),
Expunging("The volume is being expunging"),
Expunged("The volume has been expunged"),
Destroy("The volume is destroyed, and can't be recovered."),
Expunged("The volume has been expunged, and can no longer be recovered"),
Destroy("The volume is destroyed, and can be recovered."),
Destroying("The volume is destroying, and can't be recovered."),
UploadOp("The volume upload operation is in progress or in short the volume is on secondary storage"),
Copying("Volume is copying from image store to primary, in case it's an uploaded volume"),

View File

@ -18,6 +18,8 @@ package org.apache.cloudstack.api.command.admin.vm;
import org.apache.log4j.Logger;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.ACL;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
@ -43,6 +45,7 @@ public class RecoverVMCmd extends BaseCmd {
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@ACL(accessType = AccessType.OperateEntry)
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, description = "The ID of the virtual machine")
private Long id;

View File

@ -43,6 +43,7 @@ import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import javax.persistence.EntityExistsException;
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
import org.apache.cloudstack.annotation.AnnotationService;
@ -191,7 +192,6 @@ import com.cloud.offering.NetworkOffering;
import com.cloud.offering.ServiceOffering;
import com.cloud.offerings.NetworkOfferingVO;
import com.cloud.offerings.dao.NetworkOfferingDao;
import com.cloud.offerings.dao.NetworkOfferingDetailsDao;
import com.cloud.org.Cluster;
import com.cloud.resource.ResourceManager;
import com.cloud.resource.ResourceState;
@ -354,8 +354,6 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
@Inject
private StorageManager storageMgr;
@Inject
private NetworkOfferingDetailsDao networkOfferingDetailsDao;
@Inject
private NetworkDetailsDao networkDetailsDao;
@Inject
private SecurityGroupManager _securityGroupManager;
@ -3820,8 +3818,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext();
if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) {
VmWorkJobVO placeHolder = createPlaceHolderWork(vm.getId());
VmWorkJobVO placeHolder = createPlaceHolderWork(vm.getId(), network.getUuid());
try {
return orchestrateAddVmToNetwork(vm, network, requested);
} finally {
@ -3840,7 +3837,19 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
return (NicProfile) jobResult;
}
throw new RuntimeException("Unexpected job execution result");
throw new RuntimeException("null job execution result");
}
}
/**
* duplicated in {@see UserVmManagerImpl} for a {@see UserVmVO}
*/
private void checkIfNetworkExistsForVM(VirtualMachine virtualMachine, Network network) {
List<NicVO> allNics = _nicsDao.listByVmId(virtualMachine.getId());
for (NicVO nic : allNics) {
if (nic.getNetworkId() == network.getId()) {
throw new CloudRuntimeException("A NIC already exists for VM:" + virtualMachine.getInstanceName() + " in network: " + network.getUuid());
}
}
}
@ -3848,6 +3857,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
InsufficientCapacityException {
final CallContext cctx = CallContext.current();
checkIfNetworkExistsForVM(vm, network);
s_logger.debug("Adding vm " + vm + " to network " + network + "; requested nic profile " + requested);
final VMInstanceVO vmVO = _vmDao.findById(vm.getId());
final ReservationContext context = new ReservationContextImpl(null, null, cctx.getCallingUser(), cctx.getCallingAccount());
@ -5141,37 +5151,64 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
final List<VmWorkJobVO> pendingWorkJobs = _workJobDao.listPendingWorkJobs(
VirtualMachine.Type.Instance, vm.getId(),
VmWorkAddVmToNetwork.class.getName());
VmWorkAddVmToNetwork.class.getName(), network.getUuid());
VmWorkJobVO workJob = null;
if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) {
assert pendingWorkJobs.size() == 1;
if (pendingWorkJobs.size() > 1) {
throw new CloudRuntimeException(String.format("The number of jobs to add network %s to vm %s are %d", network.getUuid(), vm.getInstanceName(), pendingWorkJobs.size()));
}
workJob = pendingWorkJobs.get(0);
} else {
if (s_logger.isTraceEnabled()) {
s_logger.trace(String.format("no jobs to add network %s for vm %s yet", network, vm));
}
workJob = new VmWorkJobVO(context.getContextId());
workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER);
workJob.setCmd(VmWorkAddVmToNetwork.class.getName());
workJob.setAccountId(account.getId());
workJob.setUserId(user.getId());
workJob.setVmType(VirtualMachine.Type.Instance);
workJob.setVmInstanceId(vm.getId());
workJob.setRelated(AsyncJobExecutionContext.getOriginJobId());
// save work context info (there are some duplications)
final VmWorkAddVmToNetwork workInfo = new VmWorkAddVmToNetwork(user.getId(), account.getId(), vm.getId(),
VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, network.getId(), requested);
workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo));
_jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId());
workJob = createVmWorkJobToAddNetwork(vm, network, requested, context, user, account);
}
AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(workJob.getId());
return new VmJobVirtualMachineOutcome(workJob, vm.getId());
}
private VmWorkJobVO createVmWorkJobToAddNetwork(
VirtualMachine vm,
Network network,
NicProfile requested,
CallContext context,
User user,
Account account) {
VmWorkJobVO workJob;
workJob = new VmWorkJobVO(context.getContextId());
workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER);
workJob.setCmd(VmWorkAddVmToNetwork.class.getName());
workJob.setAccountId(account.getId());
workJob.setUserId(user.getId());
workJob.setVmType(VirtualMachine.Type.Instance);
workJob.setVmInstanceId(vm.getId());
workJob.setRelated(AsyncJobExecutionContext.getOriginJobId());
workJob.setSecondaryObjectIdentifier(network.getUuid());
// save work context info as there might be some duplicates
final VmWorkAddVmToNetwork workInfo = new VmWorkAddVmToNetwork(user.getId(), account.getId(), vm.getId(),
VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, network.getId(), requested);
workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo));
try {
_jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId());
} catch (CloudRuntimeException e) {
if (e.getCause() instanceof EntityExistsException) {
String msg = String.format("A job to add a nic for network %s to vm %s already exists", network.getUuid(), vm.getUuid());
s_logger.warn(msg, e);
}
throw e;
}
return workJob;
}
public Outcome<VirtualMachine> removeNicFromVmThroughJobQueue(
final VirtualMachine vm, final Nic nic) {
Long vmId = vm.getId();
@ -5374,6 +5411,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
}
private VmWorkJobVO createPlaceHolderWork(final long instanceId) {
return createPlaceHolderWork(instanceId, null);
}
private VmWorkJobVO createPlaceHolderWork(final long instanceId, String secondaryObjectIdentifier) {
final VmWorkJobVO workJob = new VmWorkJobVO("");
workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_PLACEHOLDER);
@ -5385,6 +5426,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
workJob.setStep(VmWorkJobVO.Step.Starting);
workJob.setVmType(VirtualMachine.Type.Instance);
workJob.setVmInstanceId(instanceId);
if(org.apache.commons.lang3.StringUtils.isNotBlank(secondaryObjectIdentifier)) {
workJob.setSecondaryObjectIdentifier(secondaryObjectIdentifier);
}
workJob.setInitMsid(ManagementServerNode.getManagementServerId());
_workJobDao.persist(workJob);

View File

@ -17,4 +17,6 @@
--;
-- Schema upgrade from 4.16.0.0 to 4.16.1.0
--;
--;
ALTER TABLE `cloud`.`vm_work_job` ADD COLUMN `secondary_object` char(100) COMMENT 'any additional item that must be checked during queueing' AFTER `vm_instance_id`;

View File

@ -32,6 +32,8 @@ public interface VmWorkJobDao extends GenericDao<VmWorkJobVO, Long> {
List<VmWorkJobVO> listPendingWorkJobs(VirtualMachine.Type type, long instanceId, String jobCmd);
List<VmWorkJobVO> listPendingWorkJobs(VirtualMachine.Type type, long instanceId, String jobCmd, String secondaryObjectIdentifier);
void updateStep(long workJobId, Step step);
void expungeCompletedWorkJobs(Date cutDate);

View File

@ -67,6 +67,7 @@ public class VmWorkJobDaoImpl extends GenericDaoBase<VmWorkJobVO, Long> implemen
PendingWorkJobByCommandSearch.and("jobStatus", PendingWorkJobByCommandSearch.entity().getStatus(), Op.EQ);
PendingWorkJobByCommandSearch.and("vmType", PendingWorkJobByCommandSearch.entity().getVmType(), Op.EQ);
PendingWorkJobByCommandSearch.and("vmInstanceId", PendingWorkJobByCommandSearch.entity().getVmInstanceId(), Op.EQ);
PendingWorkJobByCommandSearch.and("secondaryObjectIdentifier", PendingWorkJobByCommandSearch.entity().getSecondaryObjectIdentifier(), Op.EQ);
PendingWorkJobByCommandSearch.and("step", PendingWorkJobByCommandSearch.entity().getStep(), Op.NEQ);
PendingWorkJobByCommandSearch.and("cmd", PendingWorkJobByCommandSearch.entity().getCmd(), Op.EQ);
PendingWorkJobByCommandSearch.done();
@ -119,6 +120,19 @@ public class VmWorkJobDaoImpl extends GenericDaoBase<VmWorkJobVO, Long> implemen
return this.listBy(sc, filter);
}
@Override
public List<VmWorkJobVO> listPendingWorkJobs(VirtualMachine.Type type, long instanceId, String jobCmd, String secondaryObjectIdentifier) {
SearchCriteria<VmWorkJobVO> sc = PendingWorkJobByCommandSearch.create();
sc.setParameters("jobStatus", JobInfo.Status.IN_PROGRESS);
sc.setParameters("vmType", type);
sc.setParameters("vmInstanceId", instanceId);
sc.setParameters("secondaryObjectIdentifier", secondaryObjectIdentifier);
sc.setParameters("cmd", jobCmd);
Filter filter = new Filter(VmWorkJobVO.class, "created", true, null, null);
return this.listBy(sc, filter);
}
@Override
public void updateStep(long workJobId, Step step) {
VmWorkJobVO jobVo = findById(workJobId);

View File

@ -384,7 +384,7 @@ public class AsyncJobVO implements AsyncJob, JobInfo {
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("AsyncJobVO {id:").append(getId());
sb.append("AsyncJobVO: {id:").append(getId());
sb.append(", userId: ").append(getUserId());
sb.append(", accountId: ").append(getAccountId());
sb.append(", instanceType: ").append(getInstanceType());

View File

@ -58,6 +58,9 @@ public class VmWorkJobVO extends AsyncJobVO {
@Column(name = "vm_instance_id")
long vmInstanceId;
@Column(name = "secondary_object")
String secondaryObjectIdentifier;
protected VmWorkJobVO() {
}
@ -89,4 +92,25 @@ public class VmWorkJobVO extends AsyncJobVO {
public void setVmInstanceId(long vmInstanceId) {
this.vmInstanceId = vmInstanceId;
}
public String getSecondaryObjectIdentifier() {
return secondaryObjectIdentifier;
}
public void setSecondaryObjectIdentifier(String secondaryObjectIdentifier) {
this.secondaryObjectIdentifier = secondaryObjectIdentifier;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("VmWorkJobVO : {").
append(", step: ").append(getStep()).
append(", vmType: ").append(getVmType()).
append(", vmInstanceId: ").append(getVmInstanceId()).
append(", secondaryObjectIdentifier: ").append(getSecondaryObjectIdentifier()).
append(super.toString()).
append("}");
return sb.toString();
}
}

View File

@ -37,6 +37,9 @@ public final class LibvirtGetStorageStatsCommandWrapper extends CommandWrapper<G
try {
final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr();
final KVMStoragePool sp = storagePoolMgr.getStoragePool(command.getPooltype(), command.getStorageId(), true);
if (sp == null) {
return new GetStorageStatsAnswer(command, "no storage pool to get statistics from");
}
return new GetStorageStatsAnswer(command, sp.getCapacity(), sp.getUsed());
} catch (final CloudRuntimeException e) {
return new GetStorageStatsAnswer(command, e.toString());

View File

@ -1463,7 +1463,7 @@ public class KVMStorageProcessor implements StorageProcessor {
primaryPool = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid());
disksize = volume.getSize();
PhysicalDiskFormat format;
if (volume.getFormat() == null) {
if (volume.getFormat() == null || StoragePoolType.RBD.equals(primaryStore.getPoolType())) {
format = primaryPool.getDefaultFormat();
} else {
format = PhysicalDiskFormat.valueOf(volume.getFormat().toString().toUpperCase());

View File

@ -1659,8 +1659,7 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
throw new CloudRuntimeException("cannot check permissions on (Network) <null>");
}
// Perform account permission check
if ((network.getGuestType() != GuestType.Shared && network.getGuestType() != GuestType.L2) ||
(network.getGuestType() == GuestType.Shared && network.getAclType() == ACLType.Account)) {
if (network.getGuestType() != GuestType.Shared || network.getAclType() == ACLType.Account) {
AccountVO networkOwner = _accountDao.findById(network.getAccountId());
if (networkOwner == null)
throw new PermissionDeniedException("Unable to use network with id= " + ((NetworkVO)network).getUuid() +
@ -1838,14 +1837,14 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
public boolean isNetworkAvailableInDomain(long networkId, long domainId) {
Long networkDomainId = null;
Network network = getNetwork(networkId);
if (network.getGuestType() != GuestType.Shared && network.getGuestType() != GuestType.L2) {
s_logger.trace("Network id=" + networkId + " is not shared or L2");
if (network.getGuestType() != GuestType.Shared) {
s_logger.trace("Network id=" + networkId + " is not shared");
return false;
}
NetworkDomainVO networkDomainMap = _networkDomainDao.getDomainNetworkMapByNetworkId(networkId);
if (networkDomainMap == null) {
s_logger.trace("Network id=" + networkId + " is shared or L2, but not domain specific");
s_logger.trace("Network id=" + networkId + " is shared, but not domain specific");
return true;
} else {
networkDomainId = networkDomainMap.getDomainId();

View File

@ -24,6 +24,7 @@ import java.util.Set;
import javax.inject.Inject;
import com.cloud.exception.UnsupportedServiceException;
import com.cloud.network.element.UserDataServiceProvider;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.dao.VMTemplateDao;
@ -605,7 +606,7 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
// enable static nat on the backend
s_logger.trace("Enabling static nat for ip address " + ipAddress + " and vm id=" + vmId + " on the backend");
if (applyStaticNatForIp(ipId, false, caller, false)) {
applyUserData(vmId, network, guestNic);
applyUserDataIfNeeded(vmId, network, guestNic);
performedIpAssoc = false; // ignor unassignIPFromVpcNetwork in finally block
return true;
} else {
@ -629,8 +630,14 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
return false;
}
protected void applyUserData(long vmId, Network network, Nic guestNic) throws ResourceUnavailableException {
UserDataServiceProvider element = _networkModel.getUserDataUpdateProvider(network);
protected void applyUserDataIfNeeded(long vmId, Network network, Nic guestNic) throws ResourceUnavailableException {
UserDataServiceProvider element = null;
try {
element = _networkModel.getUserDataUpdateProvider(network);
} catch (UnsupportedServiceException ex) {
s_logger.info(String.format("%s is not supported by network %s, skipping.", Service.UserData.getName(), network));
return;
}
if (element == null) {
s_logger.error("Can't find network element for " + Service.UserData.getName() + " provider needed for UserData update");
} else {
@ -1144,7 +1151,7 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
Nic guestNic = _networkModel.getNicInNetwork(vmId, guestNetwork.getId());
if (applyStaticNatForIp(ipId, false, caller, true)) {
if (ipAddress.getState() == IpAddress.State.Releasing) {
applyUserData(vmId, guestNetwork, guestNic);
applyUserDataIfNeeded(vmId, guestNetwork, guestNic);
}
} else {
success = false;
@ -1289,7 +1296,7 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules
if (disableStaticNat(ipId, caller, ctx.getCallingUserId(), false)) {
Nic guestNic = _networkModel.getNicInNetworkIncludingRemoved(vmId, guestNetwork.getId());
applyUserData(vmId, guestNetwork, guestNic);
applyUserDataIfNeeded(vmId, guestNetwork, guestNic);
return true;
}
return false;

View File

@ -1389,12 +1389,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
Account vmOwner = _accountMgr.getAccount(vmInstance.getAccountId());
_networkModel.checkNetworkPermissions(vmOwner, network);
List<NicVO> allNics = _nicDao.listByVmId(vmInstance.getId());
for (NicVO nic : allNics) {
if (nic.getNetworkId() == network.getId()) {
throw new CloudRuntimeException("A NIC already exists for VM:" + vmInstance.getInstanceName() + " in network: " + network.getUuid());
}
}
checkIfNetExistsForVM(vmInstance, network);
macAddress = validateOrReplaceMacAddress(macAddress, network.getId());
@ -1461,10 +1456,22 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
}
}
CallContext.current().putContextParameter(Nic.class, guestNic.getUuid());
s_logger.debug("Successful addition of " + network + " from " + vmInstance);
s_logger.debug(String.format("Successful addition of %s from %s through %s", network, vmInstance, guestNic));
return _vmDao.findById(vmInstance.getId());
}
/**
* duplicated in {@see VirtualMachineManagerImpl} for a {@see VMInstanceVO}
*/
private void checkIfNetExistsForVM(VirtualMachine virtualMachine, Network network) {
List<NicVO> allNics = _nicDao.listByVmId(virtualMachine.getId());
for (NicVO nic : allNics) {
if (nic.getNetworkId() == network.getId()) {
throw new CloudRuntimeException("A NIC already exists for VM:" + virtualMachine.getInstanceName() + " in network: " + network.getUuid());
}
}
}
/**
* If the given MAC address is invalid it replaces the given MAC with the next available MAC address
*/
@ -4419,6 +4426,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
value = DBEncryptionUtil.encrypt(value);
}
}
} else if (value == null) {
value = "";
}
if (s_logger.isTraceEnabled()) {
s_logger.trace(String.format("setting property '%s' as '%s' with value '%s'", key, detailKey, value));

View File

@ -192,6 +192,8 @@ class TestVMWareStoragePolicies(cloudstackTestCase):
self.apiclient,
self.testdata["l2-network"],
zoneid=self.zone.id,
accountid=self.account.name,
domainid=self.account.domainid,
networkofferingid=self.network_offering.id
)
self.cleanup.append(l2_network)

View File

@ -2956,6 +2956,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",

View File

@ -1133,7 +1133,7 @@ export default {
this.vm.hostname = host.name
}
if (this.serviceOffering.rootdisksize) {
if (this.serviceOffering?.rootdisksize) {
this.vm.disksizetotalgb = this.serviceOffering.rootdisksize
} else if (this.diskSize) {
this.vm.disksizetotalgb = this.diskSize
@ -2097,6 +2097,7 @@ export default {
this.updateComputeOffering(null)
},
updateTemplateConfigurationOfferingDetails (offeringId) {
this.rootDiskSizeFixed = 0
var offering = this.serviceOffering
if (!offering || offering.id !== offeringId) {
offering = _.find(this.options.serviceOfferings, (option) => option.id === offeringId)

View File

@ -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)
})
})
}

View File

@ -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

View File

@ -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']

View File

@ -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 {

View File

@ -253,7 +253,7 @@
<a-form-item :label="$t('label.service.lb.elasticlbcheckbox')" v-if="guestType == 'shared' && lbServiceChecked && lbServiceProvider === 'Netscaler'">
<a-switch v-decorator="['elasticlb', {initialValue: false}]" />
</a-form-item>
<a-form-item :label="$t('label.service.lb.inlinemodedropdown')" v-if="(guestType === 'shared' || guestType === 'isolated') && lbServiceChecked && firewallServiceChecked && lbServiceProvider === 'F5BigIp' && firewallServiceProvider === 'JuniperSRX'">
<a-form-item :label="$t('label.service.lb.inlinemodedropdown')" v-if="['shared', 'isolated'].includes(guestType) && lbServiceChecked && firewallServiceChecked && ['F5BigIp', 'Netscaler'].includes(lbServiceProvider) && ['JuniperSRX'].includes(firewallServiceProvider)">
<a-radio-group
v-decorator="['inlinemode', {
initialValue: 'false'
@ -756,23 +756,14 @@ export default {
this.loading = true
var params = {}
var self = this
var selectedServices = null
var keys = Object.keys(values)
const detailsKey = ['promiscuousmode', 'macaddresschanges', 'forgedtransmits', 'maclearning']
const ignoredKeys = [...detailsKey, 'state', 'status', 'allocationstate', 'forvpc', 'lbType', 'specifyvlan', 'ispublic', 'domainid', 'zoneid', 'egressdefaultpolicy', 'isolation', 'supportspublicaccess']
keys.forEach(function (key, keyIndex) {
if (self.isSupportedServiceObject(values[key])) {
if (selectedServices == null) {
selectedServices = {}
}
selectedServices[key] = values[key]
} else {
if (!ignoredKeys.includes(key) &&
values[key] != null && values[key] !== undefined &&
!(key === 'availability' && values[key] === 'Optional')) {
params[key] = values[key]
}
if (!ignoredKeys.includes(key) &&
values[key] != null && values[key] !== undefined &&
!(key === 'availability' && values[key] === 'Optional')) {
params[key] = values[key]
}
})
@ -810,12 +801,12 @@ export default {
params.conservemode = false
}
}
if (selectedServices != null) {
var supportedServices = Object.keys(selectedServices)
if (this.selectedServiceProviderMap != null) {
var supportedServices = Object.keys(this.selectedServiceProviderMap)
params.supportedservices = supportedServices.join(',')
for (var k in supportedServices) {
params['serviceProviderList[' + k + '].service'] = supportedServices[k]
params['serviceProviderList[' + k + '].provider'] = selectedServices[supportedServices[k]].provider
params['serviceProviderList[' + k + '].provider'] = this.selectedServiceProviderMap[supportedServices[k]]
}
var serviceCapabilityIndex = 0
if (supportedServices.includes('Connectivity')) {
@ -871,7 +862,7 @@ export default {
params['servicecapabilitylist[' + serviceCapabilityIndex + '].capabilityvalue'] = true
serviceCapabilityIndex++
}
if (values.inlinemode === true && ((selectedServices.Lb.provider === 'F5BigIp') || (selectedServices.Lb.provider === 'Netscaler'))) {
if (values.inlinemode === true && ((this.selectedServiceProviderMap.Lb === 'F5BigIp') || (this.selectedServiceProviderMap.Lb === 'Netscaler'))) {
params['servicecapabilitylist[' + serviceCapabilityIndex + '].service'] = 'lb'
params['servicecapabilitylist[' + serviceCapabilityIndex + '].capabilitytype'] = 'InlineMode'
params['servicecapabilitylist[' + serviceCapabilityIndex + '].capabilityvalue'] = values.inlinemode
@ -881,7 +872,7 @@ export default {
params['servicecapabilitylist[' + serviceCapabilityIndex + '].capabilitytype'] = 'SupportedLbIsolation'
params['servicecapabilitylist[' + serviceCapabilityIndex + '].capabilityvalue'] = values.isolation
serviceCapabilityIndex++
if (selectedServices.Lb.provider === 'InternalLbVm') {
if (this.selectedServiceProviderMap.Lb === 'InternalLbVm') {
params['servicecapabilitylist[' + serviceCapabilityIndex + '].service'] = 'lb'
params['servicecapabilitylist[' + serviceCapabilityIndex + '].capabilitytype'] = 'lbSchemes'
params['servicecapabilitylist[' + serviceCapabilityIndex + '].capabilityvalue'] = 'internal'

View File

@ -166,7 +166,8 @@ export default {
supportedServices: [],
supportedServiceLoading: false,
connectivityServiceChecked: false,
sourceNatServiceChecked: false
sourceNatServiceChecked: false,
selectedServiceProviderMap: {}
}
},
beforeCreate () {
@ -191,9 +192,6 @@ export default {
isAdmin () {
return isAdmin()
},
isSupportedServiceObject (obj) {
return (obj !== null && obj !== undefined && Object.keys(obj).length > 0 && obj.constructor === Object && 'provider' in obj)
},
fetchDomainData () {
const params = {}
params.listAll = true
@ -307,6 +305,11 @@ export default {
if (service === 'SourceNat') {
this.sourceNatServiceChecked = checked
}
if (checked && provider != null & provider !== undefined) {
this.selectedServiceProviderMap[service] = provider
} else {
delete this.selectedServiceProviderMap[service]
}
},
handleSubmit (e) {
e.preventDefault()
@ -345,23 +348,12 @@ export default {
if (zoneId) {
params.zoneid = zoneId
}
var selectedServices = null
var keys = Object.keys(values)
var self = this
keys.forEach(function (key, keyIndex) {
if (self.isSupportedServiceObject(values[key])) {
if (selectedServices == null) {
selectedServices = {}
}
selectedServices[key] = values[key]
}
})
if (selectedServices != null) {
var supportedServices = Object.keys(selectedServices)
if (this.selectedServiceProviderMap != null) {
var supportedServices = Object.keys(this.selectedServiceProviderMap)
params.supportedservices = supportedServices.join(',')
for (var k in supportedServices) {
params['serviceProviderList[' + k + '].service'] = supportedServices[k]
params['serviceProviderList[' + k + '].provider'] = selectedServices[supportedServices[k]].provider
params['serviceProviderList[' + k + '].provider'] = this.selectedServiceProviderMap[supportedServices[k]]
}
var serviceCapabilityIndex = 0
if (supportedServices.includes('Connectivity')) {

View File

@ -57,7 +57,11 @@ mocks = {
return option
}),
info: jest.fn((option) => {
return option
return {
message: option.message,
description: 'test-description',
duration: option.duration
}
}),
success: jest.fn((option) => {
return option