Merge branch '4.16' into main

This commit is contained in:
Suresh Kumar Anaparti 2022-01-05 10:14:47 +05:30
commit 30ae9eedad
No known key found for this signature in database
GPG Key ID: D7CEAE3A9E71D0AA
88 changed files with 1361 additions and 535 deletions

View File

@ -18,6 +18,7 @@ package com.cloud.configuration;
import java.util.List;
import org.apache.cloudstack.api.command.admin.config.ResetCfgCmd;
import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd;
import org.apache.cloudstack.api.command.admin.network.CreateManagementNetworkIpRangeCmd;
import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd;
@ -76,6 +77,15 @@ public interface ConfigurationService {
*/
Configuration updateConfiguration(UpdateCfgCmd cmd) throws InvalidParameterValueException;
/**
* Resets a configuration entry with default value
*
* @param cmd
* - the command wrapping name parameter
* @return updated configuration object if successful
*/
Pair<Configuration, String> resetConfiguration(ResetCfgCmd cmd) throws InvalidParameterValueException;
/**
* Create a service offering through the API
*

View File

@ -714,6 +714,7 @@ public class ApiConstants {
public static final String VM_SNAPSHOT_MEMORY = "snapshotmemory";
public static final String VM_SNAPSHOT_QUIESCEVM = "quiescevm";
public static final String IMAGE_STORE_UUID = "imagestoreuuid";
public static final String IMAGE_STORE_ID = "imagestoreid";
public static final String GUEST_VM_CIDR = "guestvmcidr";
public static final String NETWORK_CIDR = "networkcidr";
public static final String RESERVED_IP_RANGE = "reservediprange";

View File

@ -0,0 +1,166 @@
// 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.
package org.apache.cloudstack.api.command.admin.config;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiArgValidator;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.ImageStoreResponse;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.log4j.Logger;
import org.apache.cloudstack.api.response.AccountResponse;
import org.apache.cloudstack.api.response.ClusterResponse;
import org.apache.cloudstack.api.response.ConfigurationResponse;
import org.apache.cloudstack.api.response.DomainResponse;
import org.apache.cloudstack.api.response.StoragePoolResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.cloudstack.config.Configuration;
import com.cloud.user.Account;
import com.cloud.utils.Pair;
@APICommand(name = "resetConfiguration", description = "Resets a configuration. The configuration will be set to default value for global setting, and removed from account_details or domain_details for Account/Domain settings", responseObject = ConfigurationResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, since = "4.16.0")
public class ResetCfgCmd extends BaseCmd {
public static final Logger s_logger = Logger.getLogger(ResetCfgCmd.class.getName());
private static final String s_name = "resetconfigurationresponse";
/////////////////////////////////////////////////////
//////////////// API parameters /////////////////////
/////////////////////////////////////////////////////
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "the name of the configuration", validations = {ApiArgValidator.NotNullOrEmpty})
private String cfgName;
@Parameter(name = ApiConstants.ZONE_ID,
type = CommandType.UUID,
entityType = ZoneResponse.class,
description = "the ID of the Zone to reset the parameter value for corresponding zone")
private Long zoneId;
@Parameter(name = ApiConstants.CLUSTER_ID,
type = CommandType.UUID,
entityType = ClusterResponse.class,
description = "the ID of the Cluster to reset the parameter value for corresponding cluster")
private Long clusterId;
@Parameter(name = ApiConstants.STORAGE_ID,
type = CommandType.UUID,
entityType = StoragePoolResponse.class,
description = "the ID of the Storage pool to reset the parameter value for corresponding storage pool")
private Long storagePoolId;
@Parameter(name = ApiConstants.DOMAIN_ID,
type = CommandType.UUID,
entityType = DomainResponse.class,
description = "the ID of the Domain to reset the parameter value for corresponding domain")
private Long domainId;
@Parameter(name = ApiConstants.ACCOUNT_ID,
type = CommandType.UUID,
entityType = AccountResponse.class,
description = "the ID of the Account to reset the parameter value for corresponding account")
private Long accountId;
@Parameter(name = ApiConstants.IMAGE_STORE_ID,
type = CommandType.UUID,
entityType = ImageStoreResponse.class,
description = "the ID of the Image Store to reset the parameter value for corresponding image store")
private Long imageStoreId;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
public String getCfgName() {
return cfgName;
}
public Long getZoneId() {
return zoneId;
}
public Long getClusterId() {
return clusterId;
}
public Long getStoragepoolId() {
return storagePoolId;
}
public Long getDomainId() {
return domainId;
}
public Long getAccountId() {
return accountId;
}
public Long getImageStoreId() {
return imageStoreId;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@Override
public String getCommandName() {
return s_name;
}
@Override
public long getEntityOwnerId() {
return Account.ACCOUNT_ID_SYSTEM;
}
@Override
public void execute() {
Pair<Configuration, String> cfg = _configService.resetConfiguration(this);
if (cfg != null) {
ConfigurationResponse response = _responseGenerator.createConfigurationResponse(cfg.first());
response.setResponseName(getCommandName());
if (getZoneId() != null) {
response.setScope(ConfigKey.Scope.Zone.name());
}
if (getClusterId() != null) {
response.setScope(ConfigKey.Scope.Cluster.name());
}
if (getStoragepoolId() != null) {
response.setScope(ConfigKey.Scope.StoragePool.name());
}
if (getDomainId() != null) {
response.setScope(ConfigKey.Scope.Domain.name());
}
if (getAccountId() != null) {
response.setScope(ConfigKey.Scope.Account.name());
}
if (getImageStoreId() != null) {
response.setScope(ConfigKey.Scope.ImageStore.name());
}
response.setValue(cfg.second());
this.setResponseObject(response);
} else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to reset config");
}
}
}

View File

@ -35,6 +35,11 @@ public class ClusterDetailsDaoImpl extends GenericDaoBase<ClusterDetailsVO, Long
protected final SearchBuilder<ClusterDetailsVO> ClusterSearch;
protected final SearchBuilder<ClusterDetailsVO> DetailSearch;
private final String CpuOverprovisioningFactor = "cpu.overprovisioning.factor";
private final String MemoryOverprovisioningFactor = "mem.overprovisioning.factor";
private final String CpuOverCommitRatio = "cpuOvercommitRatio";
private final String MemoryOverCommitRatio = "memoryOvercommitRatio";
protected ClusterDetailsDaoImpl() {
ClusterSearch = createSearchBuilder();
ClusterSearch.and("clusterId", ClusterSearch.entity().getClusterId(), SearchCriteria.Op.EQ);
@ -50,12 +55,7 @@ public class ClusterDetailsDaoImpl extends GenericDaoBase<ClusterDetailsVO, Long
public ClusterDetailsVO findDetail(long clusterId, String name) {
SearchCriteria<ClusterDetailsVO> sc = DetailSearch.create();
// This is temporary fix to support list/update configuration api for cpu and memory overprovisioning ratios
if (name.equalsIgnoreCase("cpu.overprovisioning.factor")) {
name = "cpuOvercommitRatio";
}
if (name.equalsIgnoreCase("mem.overprovisioning.factor")) {
name = "memoryOvercommitRatio";
}
name = getCpuMemoryOvercommitRatio(name);
sc.setParameters("clusterId", clusterId);
sc.setParameters("name", name);
@ -103,11 +103,13 @@ public class ClusterDetailsDaoImpl extends GenericDaoBase<ClusterDetailsVO, Long
expunge(sc);
for (Map.Entry<String, String> detail : details.entrySet()) {
String name = detail.getKey();
name = getCpuMemoryOvercommitRatio(name);
String value = detail.getValue();
if ("password".equals(detail.getKey())) {
value = DBEncryptionUtil.encrypt(value);
}
ClusterDetailsVO vo = new ClusterDetailsVO(clusterId, detail.getKey(), value);
ClusterDetailsVO vo = new ClusterDetailsVO(clusterId, name, value);
persist(vo);
}
txn.commit();
@ -115,6 +117,7 @@ public class ClusterDetailsDaoImpl extends GenericDaoBase<ClusterDetailsVO, Long
@Override
public void persist(long clusterId, String name, String value) {
name = getCpuMemoryOvercommitRatio(name);
TransactionLegacy txn = TransactionLegacy.currentTxn();
txn.start();
SearchCriteria<ClusterDetailsVO> sc = DetailSearch.create();
@ -147,4 +150,15 @@ public class ClusterDetailsDaoImpl extends GenericDaoBase<ClusterDetailsVO, Long
dcName = tokens[3];
return dcName;
}
private String getCpuMemoryOvercommitRatio(String name) {
if (name.equalsIgnoreCase(CpuOverprovisioningFactor)) {
name = CpuOverCommitRatio;
}
if (name.equalsIgnoreCase(MemoryOverprovisioningFactor)) {
name = MemoryOverCommitRatio;
}
return name;
}
}

View File

@ -189,7 +189,7 @@ public class LDAPConfigCmd extends BaseCmd {
List<LDAPConfigResponse> responses = new ArrayList<LDAPConfigResponse>();
if (result.second() > 0) {
boolean useSSlConfig = _ldapConfiguration.getSSLStatus();
boolean useSSlConfig = _ldapConfiguration.getSSLStatus(null);
String searchBaseConfig = _ldapConfiguration.getBaseDn(null);
String bindDnConfig = _ldapConfiguration.getBindPrincipal(null);
for (LdapConfigurationVO ldapConfigurationVO : result.first()) {

View File

@ -238,7 +238,7 @@ public class LdapConfiguration implements Configurable{
}
public String getProviderUrl(final Long domainId) {
final String protocol = getSSLStatus() == true ? "ldaps://" : "ldap://";
final String protocol = getSSLStatus(domainId) == true ? "ldaps://" : "ldap://";
final Pair<List<LdapConfigurationVO>, Integer> result = _ldapConfigurationDao.searchConfigurations(null, 0, domainId);
final StringBuilder providerUrls = new StringBuilder();
String delim = "";
@ -270,20 +270,20 @@ public class LdapConfiguration implements Configurable{
return ldapSearchGroupPrinciple.valueIn(domainId);
}
public boolean getSSLStatus() {
public boolean getSSLStatus(Long domainId) {
boolean sslStatus = false;
if (getTrustStore() != null && getTrustStorePassword() != null) {
if (getTrustStore(domainId) != null && getTrustStorePassword(domainId) != null) {
sslStatus = true;
}
return sslStatus;
}
public String getTrustStore() {
return ldapTrustStore.value();
public String getTrustStore(Long domainId) {
return ldapTrustStore.valueIn(domainId);
}
public String getTrustStorePassword() {
return ldapTrustStorePassword.value();
public String getTrustStorePassword(Long domainId) {
return ldapTrustStorePassword.valueIn(domainId);
}
public String getUsernameAttribute(final Long domainId) {

View File

@ -66,14 +66,14 @@ public class LdapContextFactory {
return createInitialDirContext(principal, password, false, domainId);
}
private void enableSSL(final Hashtable<String, String> environment) {
final boolean sslStatus = _ldapConfiguration.getSSLStatus();
private void enableSSL(final Hashtable<String, String> environment, Long domainId) {
final boolean sslStatus = _ldapConfiguration.getSSLStatus(domainId);
if (sslStatus) {
s_logger.info("LDAP SSL enabled.");
environment.put(Context.SECURITY_PROTOCOL, "ssl");
System.setProperty("javax.net.ssl.trustStore", _ldapConfiguration.getTrustStore());
System.setProperty("javax.net.ssl.trustStorePassword", _ldapConfiguration.getTrustStorePassword());
System.setProperty("javax.net.ssl.trustStore", _ldapConfiguration.getTrustStore(domainId));
System.setProperty("javax.net.ssl.trustStorePassword", _ldapConfiguration.getTrustStorePassword(domainId));
}
}
@ -92,7 +92,7 @@ public class LdapContextFactory {
environment.put("com.sun.jndi.ldap.read.timeout", _ldapConfiguration.getReadTimeout(domainId).toString());
environment.put("com.sun.jndi.ldap.connect.pool", "true");
enableSSL(environment);
enableSSL(environment, domainId);
setAuthentication(environment, isSystemContext, domainId);
if (principal != null) {

View File

@ -49,9 +49,9 @@ class LdapContextFactorySpec extends spock.lang.Specification {
ldapConfiguration.getFirstnameAttribute() >> "givenname"
ldapConfiguration.getLastnameAttribute() >> "sn"
ldapConfiguration.getBaseDn(_) >> "dc=cloudstack,dc=org"
ldapConfiguration.getSSLStatus() >> true
ldapConfiguration.getTrustStore() >> "/tmp/ldap.ts"
ldapConfiguration.getTrustStorePassword() >> "password"
ldapConfiguration.getSSLStatus(domainId) >> true
ldapConfiguration.getTrustStore(domainId) >> "/tmp/ldap.ts"
ldapConfiguration.getTrustStorePassword(domainId) >> "password"
ldapConfiguration.getReadTimeout(_) >> 1000
ldapConfiguration.getLdapPageSize() >> 1

View File

@ -78,7 +78,7 @@ public class LdapConfigurationTest {
ldapTestConfigTool.overrideConfigValue(ldapConfiguration, "ldapTrustStore", "/tmp/ldap.ts");
ldapTestConfigTool.overrideConfigValue(ldapConfiguration, "ldapTrustStorePassword", "password");
assertTrue("A request is made to get the status of SSL should result in true", ldapConfiguration.getSSLStatus());
assertTrue("A request is made to get the status of SSL should result in true", ldapConfiguration.getSSLStatus(null));
}
@Test public void getSearchGroupPrincipleReturnsSuccessfully() throws Exception {
@ -93,7 +93,7 @@ public class LdapConfigurationTest {
// We have a ConfigDao with a value for truststore password
ldapTestConfigTool.overrideConfigValue(ldapConfiguration, "ldapTrustStorePassword", "password");
String result = ldapConfiguration.getTrustStorePassword();
String result = ldapConfiguration.getTrustStorePassword(null);
assertEquals("The result is password", "password", result);
}

View File

@ -30,10 +30,12 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.Vector;
@ -51,6 +53,7 @@ import org.apache.cloudstack.agent.lb.IndirectAgentLBServiceImpl;
import org.apache.cloudstack.annotation.AnnotationService;
import org.apache.cloudstack.annotation.dao.AnnotationDao;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.command.admin.config.ResetCfgCmd;
import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd;
import org.apache.cloudstack.api.command.admin.network.CreateManagementNetworkIpRangeCmd;
import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd;
@ -105,6 +108,7 @@ import org.apache.cloudstack.region.dao.RegionDao;
import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO;
import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDetailVO;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDetailsDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
@ -629,9 +633,16 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
if (cluster == null) {
throw new InvalidParameterValueException("unable to find cluster by id " + resourceId);
}
ClusterDetailsVO clusterDetailsVO = _clusterDetailsDao.findDetail(resourceId, name);
String newName = name;
if (name.equalsIgnoreCase("cpu.overprovisioning.factor")) {
newName = "cpuOvercommitRatio";
}
if (name.equalsIgnoreCase("mem.overprovisioning.factor")) {
newName = "memoryOvercommitRatio";
}
ClusterDetailsVO clusterDetailsVO = _clusterDetailsDao.findDetail(resourceId, newName);
if (clusterDetailsVO == null) {
clusterDetailsVO = new ClusterDetailsVO(resourceId, name, value);
clusterDetailsVO = new ClusterDetailsVO(resourceId, newName, value);
_clusterDetailsDao.persist(clusterDetailsVO);
} else {
clusterDetailsVO.setValue(value);
@ -878,6 +889,159 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
}
}
private ParamCountPair getParamCount(Map<String, Long> scopeMap) {
Long id = null;
int paramCount = 0;
String scope = ConfigKey.Scope.Global.toString();
for (var entry : scopeMap.entrySet()) {
if (entry.getValue() != null) {
id = entry.getValue();
scope = entry.getKey();
paramCount++;
}
}
return new ParamCountPair(id, paramCount, scope);
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_CONFIGURATION_VALUE_EDIT, eventDescription = "resetting configuration")
public Pair<Configuration, String> resetConfiguration(final ResetCfgCmd cmd) throws InvalidParameterValueException {
final Long userId = CallContext.current().getCallingUserId();
final String name = cmd.getCfgName();
final Long zoneId = cmd.getZoneId();
final Long clusterId = cmd.getClusterId();
final Long storagepoolId = cmd.getStoragepoolId();
final Long accountId = cmd.getAccountId();
final Long domainId = cmd.getDomainId();
final Long imageStoreId = cmd.getImageStoreId();
Optional optionalValue;
final ConfigKey<?> configKey = _configDepot.get(name);
if (configKey == null) {
s_logger.warn("Probably the component manager where configuration variable " + name + " is defined needs to implement Configurable interface");
throw new InvalidParameterValueException("Config parameter with name " + name + " doesn't exist");
}
String defaultValue = configKey.defaultValue();
String category = configKey.category();
String configScope = configKey.scope().toString();
String scope = "";
Map<String, Long> scopeMap = new LinkedHashMap<>();
Long id = null;
int paramCountCheck = 0;
scopeMap.put(ConfigKey.Scope.Zone.toString(), zoneId);
scopeMap.put(ConfigKey.Scope.Cluster.toString(), clusterId);
scopeMap.put(ConfigKey.Scope.Domain.toString(), domainId);
scopeMap.put(ConfigKey.Scope.Account.toString(), accountId);
scopeMap.put(ConfigKey.Scope.StoragePool.toString(), storagepoolId);
scopeMap.put(ConfigKey.Scope.ImageStore.toString(), imageStoreId);
ParamCountPair paramCountPair = getParamCount(scopeMap);
id = paramCountPair.getId();
paramCountCheck = paramCountPair.getParamCount();
scope = paramCountPair.getScope();
if (paramCountCheck > 1) {
throw new InvalidParameterValueException("cannot handle multiple IDs, provide only one ID corresponding to the scope");
}
if (scope != null && !scope.equals(ConfigKey.Scope.Global.toString()) && !configScope.contains(scope)) {
throw new InvalidParameterValueException("Invalid scope id provided for the parameter " + name);
}
String newValue = null;
switch (ConfigKey.Scope.valueOf(scope)) {
case Zone:
final DataCenterVO zone = _zoneDao.findById(id);
if (zone == null) {
throw new InvalidParameterValueException("unable to find zone by id " + id);
}
_dcDetailsDao.removeDetail(id, name);
optionalValue = Optional.ofNullable(configKey.valueIn(id));
newValue = optionalValue.isPresent() ? optionalValue.get().toString() : defaultValue;
break;
case Cluster:
final ClusterVO cluster = _clusterDao.findById(id);
if (cluster == null) {
throw new InvalidParameterValueException("unable to find cluster by id " + id);
}
ClusterDetailsVO clusterDetailsVO = _clusterDetailsDao.findDetail(id, name);
newValue = configKey.value().toString();
if (name.equalsIgnoreCase("cpu.overprovisioning.factor") || name.equalsIgnoreCase("mem.overprovisioning.factor")) {
_clusterDetailsDao.persist(id, name, newValue);
} else if (clusterDetailsVO != null) {
_clusterDetailsDao.remove(clusterDetailsVO.getId());
}
optionalValue = Optional.ofNullable(configKey.valueIn(id));
newValue = optionalValue.isPresent() ? optionalValue.get().toString() : defaultValue;
break;
case StoragePool:
final StoragePoolVO pool = _storagePoolDao.findById(id);
if (pool == null) {
throw new InvalidParameterValueException("unable to find storage pool by id " + id);
}
_storagePoolDetailsDao.removeDetail(id, name);
optionalValue = Optional.ofNullable(configKey.valueIn(id));
newValue = optionalValue.isPresent() ? optionalValue.get().toString() : defaultValue;
break;
case Domain:
final DomainVO domain = _domainDao.findById(id);
if (domain == null) {
throw new InvalidParameterValueException("unable to find domain by id " + id);
}
DomainDetailVO domainDetailVO = _domainDetailsDao.findDetail(id, name);
if (domainDetailVO != null) {
_domainDetailsDao.remove(domainDetailVO.getId());
}
optionalValue = Optional.ofNullable(configKey.valueIn(id));
newValue = optionalValue.isPresent() ? optionalValue.get().toString() : defaultValue;
break;
case Account:
final AccountVO account = _accountDao.findById(id);
if (account == null) {
throw new InvalidParameterValueException("unable to find account by id " + id);
}
AccountDetailVO accountDetailVO = _accountDetailsDao.findDetail(id, name);
if (accountDetailVO != null) {
_accountDetailsDao.remove(accountDetailVO.getId());
}
optionalValue = Optional.ofNullable(configKey.valueIn(id));
newValue = optionalValue.isPresent() ? optionalValue.get().toString() : defaultValue;
break;
case ImageStore:
final ImageStoreVO imageStoreVO = _imageStoreDao.findById(id);
if (imageStoreVO == null) {
throw new InvalidParameterValueException("unable to find the image store by id " + id);
}
ImageStoreDetailVO imageStoreDetailVO = _imageStoreDetailsDao.findDetail(id, name);
if (imageStoreDetailVO != null) {
_imageStoreDetailsDao.remove(imageStoreDetailVO.getId());
}
optionalValue = Optional.ofNullable(configKey.valueIn(id));
newValue = optionalValue.isPresent() ? optionalValue.get().toString() : defaultValue;
break;
default:
if (!_configDao.update(name, category, defaultValue)) {
s_logger.error("Failed to reset configuration option, name: " + name + ", defaultValue:" + defaultValue);
throw new CloudRuntimeException("Failed to reset configuration value. Please contact Cloud Support.");
}
optionalValue = Optional.ofNullable(configKey.value());
newValue = optionalValue.isPresent() ? optionalValue.get().toString() : defaultValue;
}
CallContext.current().setEventDetails(" Name: " + name + " New Value: " + (name.toLowerCase().contains("password") ? "*****" : defaultValue == null ? "" : defaultValue));
return new Pair<Configuration, String>(_configDao.findByName(name), newValue);
}
private String validateConfigurationValue(final String name, String value, final String scope) {
final ConfigurationVO cfg = _configDao.findByName(name);
@ -7055,4 +7219,40 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
ENABLE_ACCOUNT_SETTINGS_FOR_DOMAIN, ENABLE_DOMAIN_SETTINGS_FOR_CHILD_DOMAIN
};
}
static class ParamCountPair {
private Long id;
private int paramCount;
private String scope;
public ParamCountPair(Long id, int paramCount, String scope) {
this.id = id;
this.paramCount = paramCount;
this.scope = scope;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public int getParamCount() {
return paramCount;
}
public void setParamCount(int paramCount) {
this.paramCount = paramCount;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
}
}

View File

@ -74,6 +74,7 @@ import org.apache.cloudstack.api.command.admin.cluster.UpdateClusterCmd;
import org.apache.cloudstack.api.command.admin.config.ListCfgsByCmd;
import org.apache.cloudstack.api.command.admin.config.ListDeploymentPlannersCmd;
import org.apache.cloudstack.api.command.admin.config.ListHypervisorCapabilitiesCmd;
import org.apache.cloudstack.api.command.admin.config.ResetCfgCmd;
import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd;
import org.apache.cloudstack.api.command.admin.config.UpdateHypervisorCapabilitiesCmd;
import org.apache.cloudstack.api.command.admin.direct.download.RevokeTemplateDirectDownloadCertificateCmd;
@ -3009,6 +3010,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
cmdList.add(ListHypervisorCapabilitiesCmd.class);
cmdList.add(UpdateCfgCmd.class);
cmdList.add(UpdateHypervisorCapabilitiesCmd.class);
cmdList.add(ResetCfgCmd.class);
cmdList.add(CreateDomainCmd.class);
cmdList.add(DeleteDomainCmd.class);
cmdList.add(ListDomainChildrenCmd.class);

View File

@ -23,6 +23,7 @@ import java.util.Set;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import org.apache.cloudstack.api.command.admin.config.ResetCfgCmd;
import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd;
import org.apache.cloudstack.api.command.admin.network.CreateManagementNetworkIpRangeCmd;
import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd;
@ -615,4 +616,10 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu
return null;
}
@Override
public Pair<Configuration, String> resetConfiguration(ResetCfgCmd cmd) throws InvalidParameterValueException {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -0,0 +1,367 @@
# 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.
# Import Local Modules
from nose.plugins.attrib import attr
from marvin.cloudstackTestCase import cloudstackTestCase, unittest
from marvin.lib.utils import (validateList,
cleanup_resources)
from marvin.lib.base import (Account,
Configurations,
Domain,
Cluster,
StoragePool)
from marvin.lib.common import (get_domain,
get_zone)
class TestRestConfigurationSettings(cloudstackTestCase):
@classmethod
def setUpClass(self):
self.testClient = super(
TestRestConfigurationSettings,
self).getClsTestClient()
self.apiclient = self.testClient.getApiClient()
self.testdata = self.testClient.getParsedTestDataConfig()
# Get Zone, Domain
self.domain = get_domain(self.apiclient)
self.zone = get_zone(self.apiclient, self.testClient.getZoneForTests())
self._cleanup = []
return
@classmethod
def tearDownClass(cls):
super(TestRestConfigurationSettings, cls).tearDownClass()
return
def setUp(self):
self.apiclient = self.testClient.getApiClient()
self.dbclient = self.testClient.getDbConnection()
self.cleanup = []
return
def tearDown(self):
super(TestRestConfigurationSettings, self).tearDown()
return
@attr(tags=["advanced", "advancedsg"], required_hardware="false")
def test_01_test_settings_for_domain(self):
"""
1. Get the default value for the setting in domain scope
2. Change the default value to new value
3. Make sure updated value is same as new value
4. Reset the config value
5. Make sure that current value is same as default value
:return:
"""
config_name="ldap.basedn"
#1. Get default value
configs = Configurations.list(
self.apiclient,
name=config_name
)
self.assertIsNotNone(configs, "Fail to get domain setting %s " % config_name)
orig_value = str(configs[0].value)
new_value = "testing"
#2. Update to new value
Configurations.update(
self.apiclient,
name=config_name,
value=new_value,
domainid=self.domain.id
)
configs = Configurations.list(
self.apiclient,
name=config_name,
domainid=self.domain.id
)
self.assertIsNotNone(configs, "Fail to get domain setting %s " % config_name)
#3. validate they are same
self.assertEqual(new_value,
str(configs[0].value),
"Failed to set new config value")
#4. Reset the value
Configurations.reset(
self.apiclient,
name=config_name,
domainid=self.domain.id
)
#5. Make sure its same as original value
configs = Configurations.list(
self.apiclient,
name=config_name,
domainid=self.domain.id
)
self.assertIsNotNone(configs, "Fail to get domain setting %s " % config_name)
self.assertEqual(orig_value,
str(configs[0].value),
"Failed to reset the value")
@attr(tags=["advanced", "advancedsg"], required_hardware="false")
def test_02_test_settings_for_account(self):
"""
1. Get the default value for the setting in account scope
2. Change the default value to new value
3. Make sure updated value is same as new value
4. Reset the config value
5. Make sure that current value is same as default value
:return:
"""
accounts = Account.list(
self.apiclient,
domainid=self.domain.id,
listall=True
)
self.assertIsNotNone(accounts[0],
"There should be atleast 1 account in the zone")
config_name = "enable.additional.vm.configuration"
#1. Get the default value
configs = Configurations.list(
self.apiclient,
name=config_name
)
self.assertIsNotNone(configs, "Fail to get account setting %s " % config_name)
orig_value = str(configs[0].value)
new_value = "true"
Configurations.update(
self.apiclient,
name=config_name,
value=new_value,
accountid=accounts[0].id
)
configs = Configurations.list(
self.apiclient,
name=config_name,
accountid=accounts[0].id
)
self.assertIsNotNone(configs, "Fail to get account setting %s " % config_name)
self.assertEqual(new_value,
str(configs[0].value),
"Failed to set new config value")
Configurations.reset(
self.apiclient,
name=config_name,
accountid=accounts[0].id
)
configs = Configurations.list(
self.apiclient,
name=config_name,
accountid=accounts[0].id
)
self.assertIsNotNone(configs, "Fail to get account setting %s " % config_name)
self.assertEqual(orig_value,
str(configs[0].value),
"Failed to reset the value")
@attr(tags=["advanced", "advancedsg"], required_hardware="false")
def test_03_test_settings_for_cluster(self):
"""
1. Get the default value for the setting in cluster scope
2. Change the default value to new value
3. Make sure updated value is same as new value
4. Reset the config value
5. Make sure that current value is same as default value
:return:
"""
cluster = Cluster.list(
self.apiclient
)
self.assertIsNotNone(cluster[0],
"There should be atleast 1 cluster in the zone")
config_name = "cluster.storage.operations.exclude"
configs = Configurations.list(
self.apiclient,
name=config_name,
clusterid=cluster[0].id
)
self.assertIsNotNone(configs, "Fail to get cluster setting %s " % config_name)
orig_value = str(configs[0].value)
new_value = "true"
Configurations.update(
self.apiclient,
name=config_name,
value=new_value,
clusterid=cluster[0].id
)
configs = Configurations.list(
self.apiclient,
name=config_name,
clusterid=cluster[0].id
)
self.assertIsNotNone(configs, "Fail to get cluster setting %s " % config_name)
self.assertEqual(new_value,
str(configs[0].value),
"Failed to set new config value")
Configurations.reset(
self.apiclient,
name=config_name,
clusterid=cluster[0].id
)
configs = Configurations.list(
self.apiclient,
name=config_name,
clusterid=cluster[0].id
)
self.assertIsNotNone(configs, "Fail to get cluster setting %s " % config_name)
self.assertEqual(orig_value,
str(configs[0].value),
"Failed to reset the value")
@attr(tags=["advanced", "advancedsg"], required_hardware="false")
def test_04_test_settings_for_storage(self):
"""
1. Get the default value for the setting in storage scope
2. Change the default value to new value
3. Make sure updated value is same as new value
4. Reset the config value
5. Make sure that current value is same as default value
:return:
"""
storage = StoragePool.list(
self.apiclient
)
self.assertIsNotNone(storage[0],
"There should be atleast 1 primary storage pool in the zone")
config_name = "vmware.create.full.clone"
configs = Configurations.list(
self.apiclient,
name=config_name,
storageid=storage[0].id
)
self.assertIsNotNone(configs, "Fail to get storage pool setting %s " % config_name)
orig_value = str(configs[0].value)
new_value = 'false'
Configurations.update(
self.apiclient,
name=config_name,
value=new_value,
storageid=storage[0].id
)
configs = Configurations.list(
self.apiclient,
name=config_name,
storageid=storage[0].id
)
self.assertIsNotNone(configs, "Fail to get storage pool setting %s " % config_name)
self.assertEqual(new_value,
(configs[0].value),
"Failed to set new config value")
Configurations.reset(
self.apiclient,
name=config_name,
storageid=storage[0].id
)
configs = Configurations.list(
self.apiclient,
name=config_name,
storage=storage[0].id
)
self.assertIsNotNone(configs, "Fail to get storage pool setting %s " % config_name)
self.assertEqual(orig_value,
(configs[0].value),
"Failed to reset the value for storage pool")
@attr(tags=["advanced", "advancedsg"], required_hardware="false")
def test_05_test_settings_for_zone(self):
"""
1. Get the default value for the setting in zone scope
2. Change the default value to new value
3. Make sure updated value is same as new value
4. Reset the config value
5. Make sure that current value is same as default value
:return:
"""
config_name = "enable.dynamic.scale.vm"
configs = Configurations.list(
self.apiclient,
name=config_name,
zoneid=self.zone.id
)
self.assertIsNotNone(configs, "Fail to get zone setting %s " % config_name)
orig_value = str(configs[0].value)
new_value = 'true'
Configurations.update(
self.apiclient,
name=config_name,
value=new_value,
zoneid=self.zone.id
)
configs = Configurations.list(
self.apiclient,
name=config_name,
zoneid=self.zone.id
)
self.assertIsNotNone(configs, "Fail to get ol setting %s " % config_name)
self.assertEqual(new_value,
(configs[0].value),
"Failed to set new config value")
Configurations.reset(
self.apiclient,
name=config_name,
zoneid=self.zone.id
)
configs = Configurations.list(
self.apiclient,
name=config_name,
zoneid=self.zone.id
)
self.assertIsNotNone(configs, "Fail to get zone setting %s " % config_name)
self.assertEqual(orig_value,
(configs[0].value),
"Failed to reset the value for zone")

View File

@ -4273,6 +4273,26 @@ class Configurations:
return (apiclient.listCapabilities(cmd))
@classmethod
def reset(cls, apiclient, name, zoneid=None, clusterid=None, storageid=None, domainid=None, accountid=None):
"""Resets the specified configuration to original value"""
cmd = resetConfiguration.resetConfigurationCmd()
cmd.name = name
if zoneid:
cmd.zoneid = zoneid
if clusterid:
cmd.clusterid = clusterid
if storageid:
cmd.storageid = storageid
if domainid:
cmd.domainid = domainid
if accountid:
cmd.accountid = accountid
apiclient.resetConfiguration(cmd)
class NetScaler:
"""Manage external netscaler device"""

View File

@ -1869,6 +1869,7 @@
"label.reservedsystemnetmask": "Reserved system netmask",
"label.reservedsystemstartip": "Start Reserved system IP",
"label.reset": "Reset",
"label.reset.config.value": "Reset to default value",
"label.reset.ssh.key.pair": "Reset SSH Key Pair",
"label.reset.ssh.key.pair.on.vm": "Reset SSH Key Pair on VM",
"label.reset.to.default": "Reset to default",
@ -2950,6 +2951,7 @@
"message.error.rados.user": "Please enter RADOS User",
"message.error.remove.nic": "There was an error",
"message.error.remove.secondary.ipaddress": "There was an error removing the secondary IP Address",
"message.error.reset.config": "Unable to reset config to default value",
"message.error.required.input": "Please enter input",
"message.error.retrieve.kubeconfig": "Unable to retrieve Kubernetes cluster config",
"message.error.s3nfs.path": "Please enter S3 NFS Path",

View File

@ -219,8 +219,7 @@ export default {
apiName = 'updateTemplate'
}
if (!(apiName in this.$store.getters.apis)) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('error.execute.api.failed') + ' ' + apiName,
description: this.$t('message.user.not.permitted.api')
})

View File

@ -348,6 +348,12 @@
v-if="editableValueKey === record.key"
iconType="check-circle"
iconTwoToneColor="#52c41a" />
<tooltip-button
:tooltip="$t('label.reset.config.value')"
@click="resetConfig(record)"
v-if="editableValueKey !== record.key"
icon="reload"
:disabled="!('updateConfiguration' in $store.getters.apis)" />
</template>
<template slot="tariffActions" slot-scope="text, record">
<tooltip-button
@ -514,8 +520,7 @@ export default {
json.updateconfigurationresponse.configuration &&
!json.updateconfigurationresponse.configuration.isdynamic &&
['Admin'].includes(this.$store.getters.userInfo.roletype)) {
this.$showNotification({
type: 'warning',
this.$notification.warning({
message: this.$t('label.status'),
description: this.$t('message.restart.mgmt.server')
})
@ -527,6 +532,23 @@ export default {
this.$emit('refresh')
})
},
resetConfig (item) {
api('resetConfiguration', {
name: item.name
}).then(() => {
const message = `${this.$t('label.setting')} ${item.name} ${this.$t('label.reset.config.value')}`
this.$message.success(message)
}).catch(error => {
console.error(error)
this.$message.error(this.$t('message.error.reset.config'))
this.$notification.error({
message: this.$t('label.error'),
description: this.$t('message.error.reset.config')
})
}).finally(() => {
this.$emit('refresh')
})
},
editValue (record) {
this.editableValueKey = record.key
this.editableValue = record.value

View File

@ -107,8 +107,7 @@ export default {
this.dataResource = await this.listResourceLimits(params)
this.formLoading = false
} catch (e) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: e
})

View File

@ -64,6 +64,12 @@
v-if="editableValueKey === index"
iconType="check-circle"
iconTwoToneColor="#52c41a" />
<tooltip-button
:tooltip="$t('label.reset.config.value')"
@click="resetConfig(item)"
v-if="editableValueKey !== index"
icon="reload"
:disabled="!('updateConfiguration' in $store.getters.apis)" />
</div>
</a-list-item>
</a-list>
@ -164,8 +170,7 @@ export default {
}).catch(error => {
console.error(error)
this.$message.error(this.$t('message.error.save.setting'))
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('label.error'),
description: this.$t('message.error.try.save.setting')
})
@ -183,6 +188,28 @@ export default {
handleSearch (value) {
this.filter = value
this.fetchData()
},
resetConfig (item) {
this.tabLoading = true
api('resetConfiguration', {
[this.scopeKey]: this.resource.id,
name: item.name
}).then(() => {
const message = `${this.$t('label.setting')} ${item.name} ${this.$t('label.reset.config.value')}`
this.$message.success(message)
}).catch(error => {
console.error(error)
this.$message.error(this.$t('message.error.reset.config'))
this.$notification.error({
message: this.$t('label.error'),
description: this.$t('message.error.reset.config')
})
}).finally(() => {
this.tabLoading = false
this.fetchData(() => {
this.editableValueKey = null
})
})
}
}
}

View File

@ -234,15 +234,13 @@ export default {
}).then(json => {
console.log(this.resource)
if (json?.uploadresourceiconresponse?.success) {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.upload.icon'),
description: `${this.$t('message.success.upload.icon')} ${resourceType}: ` + (this.resource.name || this.resource.username || resourceid)
})
}
}).catch((error) => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('label.upload.icon'),
description: error?.response?.data?.uploadresourceiconresponse?.errortext || '',
duration: 0
@ -266,15 +264,13 @@ export default {
resourceids: resourceid
}).then(json => {
if (json?.deleteresourceiconresponse?.success) {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.delete.icon'),
description: `${this.$t('message.success.delete.icon')} ${resourceType}: ` + (this.resource.name || this.resource.username || resourceid)
})
}
}).catch((error) => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('label.delete.icon'),
description: error?.response?.data?.deleteresourceiconresponse?.errortext || '',
duration: 0

View File

@ -152,8 +152,7 @@ export default {
const vm = result.jobresult.virtualmachine || {}
if (result.jobstatus === 1 && vm.password) {
const name = vm.displayname || vm.name || vm.id
obj.$showNotification({
type: 'success',
obj.$notification.success({
message: `${obj.$t('label.reinstall.vm')}: ` + name,
description: `${obj.$t('label.password.reset.confirm')}: ` + vm.password,
duration: 0
@ -362,8 +361,7 @@ export default {
const vm = result.jobresult.virtualmachine || {}
if (result.jobstatus === 1 && vm.password) {
const name = vm.displayname || vm.name || vm.id
obj.$showNotification({
type: 'success',
obj.$notification.success({
message: `${obj.$t('label.reset.ssh.key.pair.on.vm')}: ` + name,
description: `${obj.$t('label.password.reset.confirm')}: ` + vm.password,
duration: 0

View File

@ -122,7 +122,6 @@ Vue.use(VueCropper)
Vue.prototype.$confirm = Modal.confirm
Vue.prototype.$message = message
Vue.prototype.$notification = notification
Vue.prototype.$info = Modal.info
Vue.prototype.$success = Modal.success
Vue.prototype.$error = Modal.error

View File

@ -237,33 +237,47 @@ export const notifierPlugin = {
})
}
Vue.prototype.$showNotification = function (config) {
let countNotify = store.getters.countNotify
countNotify++
store.commit('SET_COUNT_NOTIFY', countNotify)
const defaultConfig = {
Vue.prototype.$notification = {
defaultConfig: {
top: '65px',
onClose: () => {
let countNotify = store.getters.countNotify
countNotify > 0 ? countNotify-- : countNotify = 0
store.commit('SET_COUNT_NOTIFY', countNotify)
}
}
config = Object.assign({}, defaultConfig, config)
switch (config.type) {
case 'info':
notification.info(config)
break
case 'error':
notification.error(config)
break
case 'success':
notification.success(config)
break
case 'warning':
notification.warning(config)
break
}
},
setCountNotify: () => {
let countNotify = store.getters.countNotify
countNotify++
store.commit('SET_COUNT_NOTIFY', countNotify)
},
info: (config) => {
Vue.prototype.$notification.setCountNotify()
config = Object.assign({}, Vue.prototype.$notification.defaultConfig, config)
notification.info(config)
},
error: (config) => {
Vue.prototype.$notification.setCountNotify()
config = Object.assign({}, Vue.prototype.$notification.defaultConfig, config)
notification.error(config)
},
success: (config) => {
Vue.prototype.$notification.setCountNotify()
config = Object.assign({}, Vue.prototype.$notification.defaultConfig, config)
notification.success(config)
},
warning: (config) => {
Vue.prototype.$notification.setCountNotify()
config = Object.assign({}, Vue.prototype.$notification.defaultConfig, config)
notification.warning(config)
},
warn: (config) => {
Vue.prototype.$notification.setCountNotify()
config = Object.assign({}, Vue.prototype.$notification.defaultConfig, config)
notification.warn(config)
},
close: (key) => notification.close(key),
destroy: () => notification.destroy()
}
}
}

View File

@ -1128,8 +1128,7 @@ export default {
if (action.response) {
const description = action.response(result.jobresult)
if (description) {
this.$showNotification({
type: 'info',
this.$notification.info({
message: this.$t(action.label),
description: (<span domPropsInnerHTML={description}></span>),
duration: 0

View File

@ -294,8 +294,7 @@ export default {
[variableKey]: variableValue,
networkids: this.selectedNetwork
}).then(response => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.loadbalancerinstance')
})
this.$parent.$parent.close()

View File

@ -149,8 +149,7 @@ export default {
id: this.resource.id,
affinitygroupids: this.selectedRowKeys.join(',')
}).then(response => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('message.success.change.affinity.group')
})
this.$parent.$parent.close()

View File

@ -198,8 +198,7 @@ export default {
this.hiddenElement.click()
},
notifyCopied () {
this.$showNotification({
type: 'info',
this.$notification.info({
message: this.$t('message.success.copy.clipboard')
})
},

View File

@ -141,8 +141,7 @@ export default {
const volumeId = result.jobresult.snapshot.volumeid
const snapshotId = result.jobresult.snapshot.id
const message = `${this.$t('label.create.snapshot.for.volume')} ${volumeId} ${this.$t('label.with.snapshotid')} ${snapshotId}`
this.$showNotification({
type: 'success',
this.$notification.success({
message: message,
duration: 0
})

View File

@ -1491,7 +1491,7 @@ export default {
this.form.validateFieldsAndScroll(options, async (err, values) => {
if (err) {
if (err.licensesaccepted) {
this.$showNotification({
this.$notification.error({
type: 'error',
message: this.$t('message.license.agreements.not.accepted'),
description: this.$t('message.step.license.agreements.continue')
@ -1499,8 +1499,7 @@ export default {
return
}
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: this.$t('error.form.message')
})
@ -1508,31 +1507,27 @@ export default {
}
if (!values.templateid && !values.isoid) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: this.$t('message.template.iso')
})
return
} else if (values.isoid && (!values.diskofferingid || values.diskofferingid === '0')) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: this.$t('message.step.3.continue')
})
return
}
if (!values.computeofferingid) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: this.$t('message.step.2.continue')
})
return
}
if (this.error) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: this.$t('error.form.message')
})
@ -1645,8 +1640,7 @@ export default {
}
}
} else {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: this.$t('message.step.4.continue')
})
@ -1708,8 +1702,7 @@ export default {
const vm = result.jobresult.virtualmachine
const name = vm.displayname || vm.name || vm.id
if (vm.password) {
this.$showNotification({
type: 'success',
this.$notification.error({
message: password + ` ${this.$t('label.for')} ` + name,
description: vm.password,
duration: 0

View File

@ -321,8 +321,7 @@ export default {
config.configdata !== '') {
this.clusterConfig = config.configdata
} else {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: this.$t('message.error.retrieve.kubeconfig')
})

View File

@ -306,8 +306,7 @@ export default {
})
this.closeModal()
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: (error.response?.headers?.['x-description']) || error.message,
duration: 0

View File

@ -166,8 +166,7 @@ export default {
this.$pollJob({
jobId,
successMethod: result => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('message.success.change.offering')
})
},

View File

@ -143,8 +143,7 @@ export default {
api('listPods', params).then(json => {
this.pods = json.listpodsresponse.pod || []
if (this.pods.length === 0) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: 'No pods found',
duration: 0
})
@ -163,8 +162,7 @@ export default {
api('listClusters', params).then(json => {
this.clusters = json.listclustersresponse.cluster || []
if (this.clusters.length === 0) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: 'No clusters found',
duration: 0
})
@ -186,8 +184,7 @@ export default {
api('listHosts', params).then(json => {
this.hosts = json.listhostsresponse.host || []
if (this.hosts.length === 0) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: 'No hosts found',
duration: 0
})

View File

@ -157,8 +157,7 @@ export default {
this.actionLoading = true
api('deleteBackupSchedule', params).then(json => {
if (json.deletebackupscheduleresponse.success) {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.scheduled.backups'),
description: this.$t('message.success.delete.backup.schedule')
})

View File

@ -284,8 +284,7 @@ export default {
}
this.actionLoading = true
api('createBackupSchedule', params).then(json => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.scheduled.backups'),
description: this.$t('message.success.config.backup.schedule')
})

View File

@ -266,8 +266,7 @@ export default {
this.domainsList = response.listdomainsresponse.domain || []
this.selectedDomain = this.domainsList[0].id || ''
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.errorresponse.errortext
})
@ -342,8 +341,7 @@ export default {
api('createAccount', {}, 'POST', params).then(response => {
this.$emit('refresh-data')
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.create.account'),
description: `${this.$t('message.success.create.account')} ${params.username}`
})
@ -355,14 +353,12 @@ export default {
entityid: values.samlentity,
userid: users[i].id
}).then(response => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.samlenable'),
description: this.$t('message.success.enable.saml.auth')
})
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message,
duration: 0
@ -372,8 +368,7 @@ export default {
}
this.closeAction()
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message,
duration: 0

View File

@ -410,15 +410,13 @@ export default {
this.authorizeUsersForSamlSSO(users, entityId)
}
} else if (apiName === 'importLdapUsers' && response.ldapuserresponse && values.samlEnable) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: this.$t('message.error.enable.saml')
})
} else {
if (apiName === 'ldapCreateAccount') {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.add.ldap.account'),
description: response.createaccountresponse.account.name
})

View File

@ -238,8 +238,7 @@ export default {
this.domainsList = response.listdomainsresponse.domain || []
this.selectedDomain = this.domainsList[0].id || ''
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.errorresponse.errortext
})
@ -253,8 +252,7 @@ export default {
api('listAccounts', { listAll: true, showicon: true }).then(response => {
this.accountList = response.listaccountsresponse.account || []
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.errorresponse.errortext
})
@ -321,8 +319,7 @@ export default {
api('createUser', {}, 'POST', params).then(response => {
this.$emit('refresh-data')
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.create.user'),
description: `${this.$t('message.success.create.user')} ${params.username}`
})
@ -333,14 +330,12 @@ export default {
entityid: values.samlentity,
userid: user.id
}).then(response => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.samlenable'),
description: this.$t('message.success.enable.saml.auth')
})
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message,
duration: 0
@ -349,8 +344,7 @@ export default {
}
this.closeAction()
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message,
duration: 0

View File

@ -128,8 +128,7 @@ export default {
params.currentpassword = values.currentpassword
}
api('updateUser', {}, 'POST', params).then(json => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.action.change.password'),
description: `${this.$t('message.success.change.password')} ${this.resource.username}`
})

View File

@ -108,16 +108,14 @@ export default {
userid: this.resource.id,
entityid: values.samlEntity
}).then(response => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: values.samlEnable ? this.$t('label.saml.enable') : this.$t('label.saml.disable'),
description: values.samlEnable ? `${this.$t('message.success.enable.saml.auth')} ${this.$t('label.for')} ${this.resource.username}`
: `${this.$t('message.success.disable.saml.auth')} ${this.$t('label.for')} ${this.resource.username}`
})
this.handleClose()
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message,
duration: 0

View File

@ -174,8 +174,7 @@ export default {
const role = json.createroleresponse.role
if (role) {
this.$emit('refresh-data')
this.$showNotification({
type: 'success',
this.$notification.success({
message: 'Create Role',
description: 'Sucessfully created role ' + params.name
})

View File

@ -253,8 +253,7 @@ export default {
if (this.action.response) {
const description = this.action.response(result.jobresult)
if (description) {
this.$showNotification({
type: 'info',
this.$notification.info({
message: this.$t(this.action.label),
description: (<span domPropsInnerHTML={description}></span>),
duration: 0
@ -290,8 +289,7 @@ export default {
}
this.parentCloseAction()
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
})

View File

@ -187,8 +187,7 @@ export default {
return
}
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: error.response.headers['x-description'],
duration: 0

View File

@ -178,15 +178,13 @@ export default {
api('updateUser', params).then(response => {
this.$emit('refresh-data')
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.edit.user'),
description: `${this.$t('message.success.update.user')} ${params.username}`
})
this.closeAction()
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message,
duration: 0

View File

@ -128,8 +128,7 @@ export default {
},
handleChange (info) {
if (info.file.status === 'error') {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('label.error.file.upload'),
description: this.$t('label.error.file.upload')
})
@ -186,8 +185,7 @@ export default {
const role = json.importroleresponse.role
if (role) {
this.$emit('refresh-data')
this.$showNotification({
type: 'success',
this.$notification.success({
message: 'Import Role',
description: 'Sucessfully imported role ' + params.name
})

View File

@ -455,8 +455,7 @@ export default {
catchMessage: this.$t('error.fetching.async.job.result')
})
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
})

View File

@ -265,8 +265,7 @@ export default {
handleUpload () {
const { fileList } = this
if (this.fileList.length > 1) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.upload.iso.failed'),
description: this.$t('message.error.upload.iso.description'),
duration: 0
@ -291,16 +290,14 @@ export default {
},
timeout: 86400000
}).then((json) => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('message.success.upload'),
description: this.$t('message.success.upload.description')
})
this.closeAction()
this.$emit('refresh-data')
}).catch(e => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.upload.failed'),
description: `${this.$t('message.upload.iso.failed.description')} - ${e}`,
duration: 0
@ -345,8 +342,7 @@ export default {
if (this.currentForm === 'Create') {
this.loading = true
api('registerIso', params).then(json => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.action.register.iso'),
description: `${this.$t('message.success.register.iso')} ${params.name}`
})
@ -367,8 +363,7 @@ export default {
this.uploadParams = (json.postuploadisoresponse && json.postuploadisoresponse.getuploadparams) ? json.postuploadisoresponse.getuploadparams : ''
const response = this.handleUpload()
if (response === 'upload successful') {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('message.success.upload'),
description: this.$t('message.success.upload.iso.description')
})

View File

@ -511,16 +511,14 @@ export default {
},
timeout: 86400000
}).then((json) => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('message.success.upload'),
description: this.$t('message.success.upload.template.description')
})
this.$emit('refresh-data')
this.closeAction()
}).catch(e => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.upload.failed'),
description: `${this.$t('message.upload.template.failed.description')} - ${e}`,
duration: 0
@ -889,8 +887,7 @@ export default {
if (this.currentForm === 'Create') {
this.loading = true
api('registerTemplate', params).then(json => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.register.template'),
description: `${this.$t('message.success.register.template')} ${params.name}`
})
@ -904,8 +901,7 @@ export default {
} else {
this.loading = true
if (this.fileList.length > 1) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.error.upload.template'),
description: this.$t('message.error.upload.template.description'),
duration: 0

View File

@ -554,8 +554,7 @@ export default {
catchMessage: this.$t('error.fetching.async.job.result')
})
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
})

View File

@ -289,8 +289,7 @@ export default {
featured: this.resource.featured,
op: this.selectedOperation.toLowerCase()
}).then(response => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: `${this.$t('label.success.updated')} ${resourceType} ${this.$t('label.permissions')}`
})
this.closeModal()

View File

@ -709,8 +709,7 @@ export default {
}
this.loading = true
api('createStoragePool', {}, 'POST', params).then(json => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.add.primary.storage'),
description: this.$t('label.add.primary.storage')
})

View File

@ -293,8 +293,7 @@ export default {
this.loading = true
api('addImageStore', data).then(json => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.add.secondary.storage'),
description: this.$t('label.add.secondary.storage')
})

View File

@ -257,8 +257,7 @@ export default {
}
this.addCluster()
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.listvmwaredcsresponse.errortext,
duration: 0
@ -309,8 +308,7 @@ export default {
this.parentToggleLoading()
this.$parent.$parent.close()
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.addclusterresponse.errortext,
duration: 0
@ -345,8 +343,7 @@ export default {
}
})
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.errorresponse.errortext,
duration: 0

View File

@ -17,162 +17,221 @@
<template>
<a-spin :spinning="loading">
<div class="form" v-ctrl-enter="handleSubmitForm">
<div class="form-layout" v-ctrl-enter="handleSubmitForm">
<div class="form">
<a-form
:form="form"
layout="vertical"
@submit="handleSubmitForm">
<a-form-item>
<tooltip-label slot="label" :title="$t('label.zonenamelabel')" :tooltip="placeholder.zoneid"/>
<a-select
v-decorator="['zoneid', {
initialValue: this.zoneId,
rules: [{ required: true, message: $t('message.error.select') }]
}]"
:placeholder="placeholder.zoneid"
autoFocus
showSearch
optionFilterProp="children"
:filterOption="(input, option) => {
return option.componentOptions.propsData.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}"
@change="fetchPods">
<a-select-option
v-for="zone in zonesList"
:value="zone.id"
: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>
<tooltip-label slot="label" :title="$t('label.podname')" :tooltip="placeholder.podid"/>
<a-select
v-decorator="['podid', {
initialValue: podId,
rules: [{ required: true, message: $t('message.error.select') }]
}]"
:placeholder="placeholder.podid"
showSearch
optionFilterProp="children"
:filterOption="(input, option) => {
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
}"
@change="fetchClusters">
<a-select-option
v-for="pod in podsList"
:value="pod.id"
:key="pod.id">
{{ pod.name }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item>
<tooltip-label slot="label" :title="$t('label.clustername')" :tooltip="placeholder.clusterid"/>
<a-select
v-decorator="['clusterid', {
initialValue: clusterId,
rules: [{ required: true, message: $t('message.error.select') }]
}]"
:placeholder="placeholder.clusterid"
showSearch
optionFilterProp="children"
:filterOption="(input, option) => {
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
}"
@change="handleChangeCluster">
<a-select-option
v-for="cluster in clustersList"
:value="cluster.id"
:key="cluster.id">
{{ cluster.name }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item>
<tooltip-label
slot="label"
:title="selectedClusterHyperVisorType === 'VMware' ? $t('label.esx.host') : $t('label.hostnamelabel')"
:tooltip="placeholder.url"/>
<a-input
v-decorator="['hostname', {
initialValue: hostname,
rules: [{ required: true, message: $t('message.error.required.input') }]
}]"
:placeholder="placeholder.url"></a-input>
</a-form-item>
<a-form-item v-if="selectedClusterHyperVisorType !== 'VMware'">
<tooltip-label slot="label" :title="$t('label.username')" :tooltip="placeholder.username"/>
<a-input
v-decorator="['username', {
initialValue: username,
rules: [{ required: true, message: $t('message.error.required.input') }]
}]"
:placeholder="placeholder.username"></a-input>
</a-form-item>
<a-form-item v-if="selectedClusterHyperVisorType !== 'VMware'">
<tooltip-label slot="label" :title="$t('label.authentication.method')" :tooltip="$t('label.authentication.method')"/>
<a-radio-group
v-decorator="['authmethod', {
initialValue: authMethod
}]"
buttonStyle="solid"
:defaultValue="authMethod"
@change="selected => { handleAuthMethodChange(selected.target.value) }">
<a-radio-button value="password">
{{ $t('label.password') }}
</a-radio-button>
<a-radio-button value="sshkey" v-if="selectedClusterHyperVisorType === 'KVM'">
{{ $t('label.authentication.sshkey') }}
</a-radio-button>
</a-radio-group>
<div v-if="authMethod === 'sshkey'">
<a-alert type="warning">
<span style="display:block;width:300px;word-wrap:break-word;" slot="message" v-html="$t('message.add.host.sshkey')" />
</a-alert>
</div>
</a-form-item>
<a-form-item v-if="selectedClusterHyperVisorType !== 'VMware' && authMethod === 'password'">
<tooltip-label slot="label" :title="$t('label.password')" :tooltip="placeholder.password"/>
<a-input-password
v-decorator="['password', {
initialValue: password,
rules: [{ required: true, message: $t('message.error.required.input') }]
}]"
:placeholder="placeholder.password"></a-input-password>
</a-form-item>
<a-form-item v-if="selectedClusterHyperVisorType === 'Ovm3'">
<tooltip-label slot="label" :title="$t('label.agent.username')" :tooltip="$t('label.agent.username')"/>
<a-input
v-decorator="['agentusername', { initialValue: agentusername }]"
:placeholder="$t('label.agent.username')"></a-input>
</a-form-item>
<a-form-item v-if="selectedClusterHyperVisorType === 'Ovm3'">
<tooltip-label slot="label" :title="$t('label.agent.password')" :tooltip="$t('label.agent.password')"/>
<a-input
v-decorator="['agentpassword', { initialValue: agentpassword }]"
:placeholder="$t('label.agent.password')"></a-input>
</a-form-item>
<a-form-item v-if="selectedClusterHyperVisorType === 'Ovm3'">
<tooltip-label slot="label" :title="$t('label.agentport')" :tooltip="$t('label.agentport')"/>
<a-input
v-decorator="['agentport', { initialValue: agentport }]"
:placeholder="$t('label.agentport')"></a-input>
</a-form-item>
<a-form-item v-if="selectedClusterHyperVisorType === 'BareMetal'">
<tooltip-label slot="label" :title="$t('label.baremetalcpucores')" :tooltip="$t('label.baremetalcpucores')"/>
<a-input
v-decorator="['baremetalcpucores', {
rules: [{ required: true, message: $t('message.error.required.input') }]
}]"
:placeholder="$t('label.baremetalcpucores')"></a-input>
</a-form-item>
<a-form-item v-if="selectedClusterHyperVisorType === 'BareMetal'">
<tooltip-label slot="label" :title="$t('label.baremetalcpu')" :tooltip="$t('label.baremetalcpu')"/>
<a-input
v-decorator="['baremetalcpu', {
rules: [{ required: true, message: $t('message.error.required.input') }]
}]"
:placeholder="$t('label.baremetalcpu')"></a-input>
</a-form-item>
<a-form-item v-if="selectedClusterHyperVisorType === 'BareMetal'">
<tooltip-label slot="label" :title="$t('label.baremetalmemory')" :tooltip="$t('label.baremetalmemory')"/>
<a-input
v-decorator="['baremetalmemory', {
rules: [{ required: true, message: $t('message.error.required.input') }]
}]"
:placeholder="$t('label.baremetalmemory')"></a-input>
</a-form-item>
<a-form-item v-if="selectedClusterHyperVisorType === 'BareMetal'">
<tooltip-label slot="label" :title="$t('label.baremetalmac')" :tooltip="$t('label.baremetalmac')"/>
<a-input
v-decorator="['baremetalmac', {
rules: [{ required: true, message: $t('message.error.required.input') }]
}]"
:placeholder="$t('label.baremetalmac')"></a-input>
</a-form-item>
<a-form-item>
<tooltip-label slot="label" :title="$t('label.hosttags')" :tooltip="placeholder.hosttags"/>
<a-select
mode="tags"
:placeholder="placeholder.hosttags"
showSearch
optionFilterProp="children"
:filterOption="(input, option) => {
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
}"
v-decorator="['hosttags', {
rules: hostTagRules
}]">
<a-select-option v-for="tag in hostTagsList" :key="tag.name">{{ tag.name }}</a-select-option>
</a-select>
</a-form-item>
<a-form-item>
<tooltip-label slot="label" :title="$t('label.isdedicated')"/>
<a-checkbox @change="toggleDedicated"></a-checkbox>
</a-form-item>
<template v-if="showDedicated">
<DedicateDomain
@domainChange="id => dedicatedDomainId = id"
@accountChange="id => dedicatedAccount = id"
:error="domainError" />
</template>
<div class="form__item">
<div class="form__label"><span class="required">* </span>{{ $t('label.zonenamelabel') }}</div>
<a-select
v-model="zoneId"
@change="fetchPods"
autoFocus
showSearch
optionFilterProp="children"
:filterOption="(input, option) => {
return option.componentOptions.propsData.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option
v-for="zone in zonesList"
:value="zone.id"
: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-divider></a-divider>
<div :span="24" class="action-button">
<a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
<a-button @click="handleSubmitForm" ref="submit" type="primary">{{ $t('label.ok') }}</a-button>
</div>
</a-form>
</div>
<div class="form__item">
<div class="form__label"><span class="required">* </span>{{ $t('label.podname') }}</div>
<a-select
v-model="podId"
@change="fetchClusters"
showSearch
optionFilterProp="children"
:filterOption="(input, option) => {
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option
v-for="pod in podsList"
:value="pod.id"
:key="pod.id">
{{ pod.name }}
</a-select-option>
</a-select>
</div>
<div class="form__item">
<div class="form__label"><span class="required">* </span>{{ $t('label.clustername') }}</div>
<a-select
v-model="clusterId"
@change="handleChangeCluster"
showSearch
optionFilterProp="children"
:filterOption="(input, option) => {
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option
v-for="cluster in clustersList"
:value="cluster.id"
:key="cluster.id">
{{ cluster.name }}
</a-select-option>
</a-select>
</div>
<div class="form__item required-field">
<div class="form__label"><span class="required">* </span>{{ selectedClusterHyperVisorType === 'VMware' ? $t('label.esx.host') : $t('label.hostnamelabel') }}</div>
<span class="required required-label">{{ $t('label.required') }}</span>
<a-input v-model="hostname"></a-input>
</div>
<div class="form__item required-field" v-if="selectedClusterHyperVisorType !== 'VMware'">
<div class="form__label"><span class="required">* </span>{{ $t('label.username') }}</div>
<span class="required required-label">{{ $t('label.required') }}</span>
<a-input :placeholder="placeholder.username" v-model="username"></a-input>
</div>
<div class="form__item" v-if="selectedClusterHyperVisorType !== 'VMware'">
<div class="form__label"><span class="required">* </span>{{ $t('label.authentication.method') }}</div>
<a-radio-group
v-decorator="['authmethod', {
initialValue: authMethod
}]"
buttonStyle="solid"
:defaultValue="authMethod"
@change="selected => { handleAuthMethodChange(selected.target.value) }">
<a-radio-button value="password">
{{ $t('label.password') }}
</a-radio-button>
<a-radio-button value="sshkey" v-if="selectedClusterHyperVisorType === 'KVM'">
{{ $t('label.authentication.sshkey') }}
</a-radio-button>
</a-radio-group>
<span v-if="authMethod === 'sshkey'">
<a-alert type="warning">
<span style="display:block;width:300px;word-wrap:break-word;" slot="message" v-html="$t('message.add.host.sshkey')" />
</a-alert>
</span>
</div>
<div class="form__item required-field" v-if="selectedClusterHyperVisorType !== 'VMware' && authMethod === 'password'">
<div class="form__label"><span class="required">* </span>{{ $t('label.password') }}</div>
<span class="required required-label">{{ $t('label.required') }}</span>
<a-input :placeholder="placeholder.password" type="password" v-model="password"></a-input>
</div>
<template v-if="selectedClusterHyperVisorType === 'Ovm3'">
<div class="form__item">
<div class="form__label">{{ $t('label.agent.username') }}</div>
<a-input v-model="agentusername"></a-input>
</div>
<div class="form__item required-field">
<div class="form__label"><span class="required">* </span>{{ $t('label.agent.password') }}</div>
<span class="required required-label">{{ $t('label.required') }}</span>
<a-input type="password" v-model="agentpassword"></a-input>
</div>
<div class="form__item">
<div class="form__label">{{ $t('label.agentport') }}</div>
<a-input v-model="agentport"></a-input>
</div>
</template>
<div class="form__item">
<div class="form__label">{{ $t('label.hosttags') }}</div>
<a-select
mode="tags"
:placeholder="placeholder.hosttags"
v-model="selectedTags"
showSearch
optionFilterProp="children"
:filterOption="(input, option) => {
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
}" >
<a-select-option v-for="tag in hostTagsList" :key="tag.name">{{ tag.name }}</a-select-option>
</a-select>
</div>
<div class="form__item">
<div class="form__label">{{ $t('label.isdedicated') }}</div>
<a-checkbox @change="toggleDedicated" />
</div>
<template v-if="showDedicated">
<DedicateDomain
@domainChange="id => dedicatedDomainId = id"
@accountChange="id => dedicatedAccount = id"
:error="domainError" />
</template>
<a-divider></a-divider>
<div :span="24" class="action-button">
<a-button @click="() => this.$parent.$parent.close()">{{ $t('label.cancel') }}</a-button>
<a-button @click="handleSubmitForm" ref="submit" type="primary">{{ $t('label.ok') }}</a-button>
</div>
</div>
</a-spin>
</template>
@ -181,12 +240,14 @@
import { api } from '@/api'
import DedicateDomain from '../../components/view/DedicateDomain'
import ResourceIcon from '@/components/view/ResourceIcon'
import TooltipLabel from '@/components/widgets/TooltipLabel'
export default {
name: 'HostAdd',
components: {
DedicateDomain,
ResourceIcon
ResourceIcon,
TooltipLabel
},
props: {
resource: {
@ -222,12 +283,30 @@ export default {
domainError: false,
params: [],
placeholder: {
zoneid: null,
podid: null,
clusterid: null,
url: null,
username: null,
password: null,
hosttags: null
hosttags: null,
isdedicated: null
}
}
},
computed: {
hostTagRules () {
let rules = []
if (this.selectedClusterHyperVisorType === 'BareMetal') {
rules = [{ required: true, message: this.$t('message.error.select') }]
}
return rules
}
},
beforeCreate () {
this.form = this.$form.createForm(this)
},
created () {
this.fetchData()
},
@ -274,7 +353,7 @@ export default {
this.clustersList = response.listclustersresponse.cluster || []
this.clusterId = this.clustersList[0].id || null
if (this.clusterId) {
this.handleChangeCluster()
this.handleChangeCluster(this.clusterId)
}
}).catch(error => {
this.$notifyError(error)
@ -287,7 +366,16 @@ export default {
fetchHostTags () {
this.loading = true
api('listHostTags').then(response => {
this.hostTagsList = response.listhosttagsresponse.hosttag || []
const listTagExists = []
const hostTagsList = response.listhosttagsresponse.hosttag || []
hostTagsList.forEach(tag => {
if (listTagExists.includes(tag.name)) {
return true
}
listTagExists.push(tag.name)
this.hostTagsList.push(tag)
})
}).catch(error => {
this.$notifyError(error)
this.hostTagsList = []
@ -295,7 +383,8 @@ export default {
this.loading = false
})
},
handleChangeCluster () {
handleChangeCluster (value) {
this.clusterId = value
this.selectedCluster = this.clustersList.find(i => i.id === this.clusterId)
this.selectedClusterHyperVisorType = this.selectedCluster.hypervisortype
},
@ -309,67 +398,53 @@ export default {
},
handleSubmitForm () {
if (this.loading) return
const requiredFields = document.querySelectorAll('.required-field')
this.form.validateFieldsAndScroll((err, values) => {
if (err) return
requiredFields.forEach(field => {
const input = field.querySelector('.ant-input')
if (!input.value) {
input.parentNode.querySelector('.required-label').classList.add('required-label--error')
if (values.hostname.indexOf('http://') === -1) {
this.url = `http://${values.hostname}`
} else {
input.parentNode.querySelector('.required-label').classList.remove('required-label--error')
this.url = values.hostname
}
})
if (this.$el.querySelectorAll('.required-label--error').length > 0) return
if (this.selectedClusterHyperVisorType === 'VMware') {
this.username = ''
this.password = ''
}
if (this.hostname.indexOf('http://') === -1) {
this.url = `http://${this.hostname}`
} else {
this.url = this.hostname
}
if (this.authMethod !== 'password') {
this.password = ''
}
const args = {
zoneid: this.zoneId,
podid: this.podId,
clusterid: this.clusterId,
hypervisor: this.selectedClusterHyperVisorType,
clustertype: this.selectedCluster.clustertype,
hosttags: this.selectedTags.join(),
username: this.username,
password: this.password,
url: this.url,
agentusername: this.agentusername,
agentpassword: this.agentpassword,
agentport: this.agentport
}
Object.keys(args).forEach((key) => (args[key] == null) && delete args[key])
this.loading = true
api('addHost', {}, 'POST', args).then(response => {
const host = response.addhostresponse.host[0] || {}
if (host.id && this.showDedicated) {
this.dedicateHost(host.id)
const args = {
zoneid: values.zoneid,
podid: values.podid,
clusterid: values.clusterid,
hypervisor: this.selectedClusterHyperVisorType,
clustertype: this.selectedCluster.clustertype,
hosttags: values.hosttags ? values.hosttags.join() : null,
username: values.username,
password: this.authMethod !== 'password' ? '' : values.password,
url: this.url,
agentusername: values.agentusername,
agentpassword: values.agentpassword,
agentport: values.agentport
}
this.parentFetchData()
this.$parent.$parent.close()
}).catch(error => {
this.$showNotification({
type: 'error',
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.addhostresponse.errortext,
duration: 0
if (this.selectedClusterHyperVisorType === 'BareMetal') {
args.cpunumber = values.baremetalcpucores
args.cpuspeed = values.baremetalcpu
args.memory = values.baremetalmemory
args.hostmac = values.baremetalmac
}
Object.keys(args).forEach((key) => (args[key] == null) && delete args[key])
this.loading = true
api('addHost', {}, 'POST', args).then(response => {
const host = response.addhostresponse.host[0] || {}
if (host.id && this.showDedicated) {
this.dedicateHost(host.id)
}
this.parentFetchData()
this.closeAction()
}).catch(error => {
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.addhostresponse.errortext,
duration: 0
})
}).finally(() => {
this.loading = false
})
}).finally(() => {
this.loading = false
})
},
dedicateHost (hostId) {
@ -398,8 +473,7 @@ export default {
}
})
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.errorresponse.errortext,
duration: 0
@ -411,15 +485,22 @@ export default {
this.params.find(i => {
if (i.name === field) this.placeholder[field] = i.description
})
},
closeAction () {
this.$emit('close-action')
}
}
}
</script>
<style scoped lang="scss">
<style lang="scss">
.form {
&__label {
margin-bottom: 5px;
.required {
margin-left: 10px;
}
}
&__item {
margin-bottom: 20px;

View File

@ -241,14 +241,12 @@ export default {
const result = json.queryasyncjobresultresponse
if (result.jobstatus === 1 && this.maxCerts === count) {
this.$message.success(`${this.$t('label.certificate.upload')}: ${result.jobresult.customcertificate.message}`)
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.certificate.upload'),
description: result.jobresult.customcertificate.message || this.$t('message.success.certificate.upload')
})
} else if (result.jobstatus === 2) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('label.certificate.upload.failed'),
description: result.jobresult.errortext || this.$t('label.certificate.upload.failed.description'),
duration: 0

View File

@ -154,14 +154,12 @@ export default {
const success = result.imagestore.success || false
const message = result.imagestore.message || ''
if (success) {
this.$showNotification({
type: 'success',
this.$notification.success({
message: title,
description: message
})
} else {
this.$showNotification({
type: 'error',
this.$notification.error({
message: title,
description: message,
duration: 0

View File

@ -207,8 +207,7 @@ export default {
this.parentFetchData()
this.$parent.$parent.close()
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.createpodresponse.errortext,
duration: 0
@ -243,8 +242,7 @@ export default {
}
})
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.errorresponse.errortext,
duration: 0

View File

@ -254,8 +254,7 @@ export default {
this.items = response.listdedicatedguestvlanrangesresponse.dedicatedguestvlanrange || []
this.totalCount = response.listdedicatedguestvlanrangesresponse.count || 0
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.errorresponse.errortext,
duration: 0

View File

@ -133,8 +133,7 @@ export default {
this.traffictype = this.trafficTypes[0].traffictype || undefined
})
.catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.errorresponse.errortext
})

View File

@ -504,8 +504,7 @@ export default {
handleDeleteIpRange (id) {
this.componentLoading = true
api('deleteVlanIpRange', { id }).then(() => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: 'Removed IP Range'
})
}).catch(error => {
@ -541,13 +540,11 @@ export default {
params.networkid = this.network.id
}
api('createVlanIpRange', params).then(() => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('message.success.add.iprange')
})
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.createvlaniprangeresponse
? error.response.data.createvlaniprangeresponse.errortext : error.response.data.errorresponse.errortext,
@ -575,13 +572,11 @@ export default {
forsystemvms: values.forsystemvms
}
api('updateVlanIpRange', params).then(() => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('message.success.update.iprange')
})
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.updatevlaniprangeresponse
? error.response.data.updatevlaniprangeresponse.errortext : error.response.data.errorresponse.errortext,

View File

@ -1219,8 +1219,7 @@ export default {
this.onCloseAction()
} catch (error) {
this.actionLoading = false
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: error
})
@ -1356,8 +1355,7 @@ export default {
this.onCloseAction()
} catch (message) {
this.actionLoading = false
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: message
})

View File

@ -253,8 +253,7 @@ export default {
this.loading = false
} catch (error) {
this.loading = false
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
})

View File

@ -288,8 +288,7 @@ export default {
this.loading = false
} catch (error) {
this.loading = false
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
})

View File

@ -160,8 +160,7 @@ export default {
this.loading = false
} catch (error) {
this.loading = false
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
})

View File

@ -377,8 +377,7 @@ export default {
this.loading = false
} catch (error) {
this.loading = false
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
})

View File

@ -322,8 +322,7 @@ export default {
this.loading = false
} catch (error) {
this.loading = false
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
})

View File

@ -188,8 +188,7 @@ export default {
this.listData[args.title].loading = false
} catch (error) {
this.listData[args.title].loading = false
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
})

View File

@ -300,8 +300,7 @@ export default {
this.actionLoading = false
} catch (error) {
this.actionLoading = false
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
})
@ -337,8 +336,7 @@ export default {
this.actionLoading = false
} catch (error) {
this.actionLoading = false
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
})

View File

@ -1547,8 +1547,7 @@ export default {
this.$emit('refresh-data')
} catch (e) {
this.loading = false
await this.$showNotification({
type: 'error',
await this.$notification.error({
message: this.$t('message.request.failed'),
description: e
})

View File

@ -571,8 +571,7 @@ export default {
data.aclid = this.resource.id
api('createNetworkACL', {}, 'POST', data).then(() => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.success'),
description: this.$t('message.success.add.rule')
})

View File

@ -442,8 +442,7 @@ export default {
}
}
api('createNetwork', params).then(json => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: 'Network',
description: this.$t('message.success.create.isolated.network')
})

View File

@ -393,8 +393,7 @@ export default {
}
}
api('createNetwork', params).then(json => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: 'Network',
description: this.$t('message.success.create.l2.network')
})

View File

@ -703,8 +703,7 @@ export default {
!this.isValidTextValueForKey(values, 'ip6gateway') && !this.isValidTextValueForKey(values, 'ip6cidr') &&
!this.isValidTextValueForKey(values, 'startipv6') && !this.isValidTextValueForKey(values, 'endipv6'))
) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: this.$t('message.error.add.guest.network')
})
@ -789,8 +788,7 @@ export default {
params.hideipaddressusage = true
}
api('createNetwork', params).then(json => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.network'),
description: this.$t('message.success.add.guest.network')
})

View File

@ -248,15 +248,13 @@ export default {
api('createVlanIpRange', params)
.then(() => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('message.success.add.iprange')
})
this.closeAction()
this.$emit('refresh-data')
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.createvlaniprangeresponse
? error.response.data.createvlaniprangeresponse.errortext : error.response.data.errorresponse.errortext,

View File

@ -223,8 +223,7 @@ export default {
})
this.closeModal()
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.errorresponse.errortext,
duration: 0

View File

@ -359,8 +359,7 @@ export default {
})
this.onCloseModal()
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.errorresponse.errortext,
duration: 0
@ -427,8 +426,7 @@ export default {
})
}).catch(error => {
this.fetchLoading = false
this.$showNotification({
type: 'error',
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.errorresponse.errortext,
duration: 0

View File

@ -664,8 +664,7 @@ export default {
}
api('createNetwork', params).then(() => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('message.success.add.vpc.network')
})
}).catch(error => {

View File

@ -132,8 +132,7 @@ export default {
jobId: response.createremoteaccessvpnresponse.jobid,
successMethod: result => {
const res = result.jobresult.remoteaccessvpn
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.status'),
description:
`${this.$t('message.enabled.vpn')} ${res.publicip}. ${this.$t('message.enabled.vpn.ip.sec')} ${res.presharedkey}`,

View File

@ -297,8 +297,7 @@ export default {
this.loading = true
api('update' + this.offeringType, params).then(json => {
this.$emit('refresh-data')
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.action.update.offering.access'),
description: this.$t('label.action.update.offering.access')
})

View File

@ -125,8 +125,7 @@ export default {
this.$message.success(`${this.$t('message.setting.updated')} ${this.resource.description}`)
this.onClose()
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: (error.response && error.response.headers && error.response.headers['x-description']) || error.message
})

View File

@ -229,8 +229,7 @@ export default {
params[key] = input
}
api('updateProjectRole', params).then(response => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.update.project.role'),
description: this.$t('label.update.project.role')
})
@ -269,8 +268,7 @@ export default {
params[key] = input
}
api('createProjectRole', params).then(response => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.create.project.role'),
description: this.$t('label.create.project.role')
})
@ -289,8 +287,7 @@ export default {
projectid: this.resource.id,
id: role.id
}).then(response => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.delete.project.role'),
description: this.$t('label.delete.project.role')
})

View File

@ -385,8 +385,7 @@ export default {
this.actionLoading = true
api('createSnapshotPolicy', params).then(json => {
this.$emit('refresh')
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.action.recurring.snapshot'),
description: this.$t('message.success.recurring.snapshot')
})

View File

@ -125,8 +125,7 @@ export default {
})
this.closeModal()
}).catch(error => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.errorresponse.errortext,
duration: 0

View File

@ -162,8 +162,7 @@ export default {
this.actionLoading = true
api('deleteSnapshotPolicies', params).then(json => {
if (json.deletesnapshotpoliciesresponse.success) {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('label.delete.snapshot.policy'),
description: this.$t('message.success.delete.snapshot.policy')
})

View File

@ -191,8 +191,7 @@ export default {
this.uploadParams = (json.postuploadvolumeresponse && json.postuploadvolumeresponse.getuploadparams) ? json.postuploadvolumeresponse.getuploadparams : ''
const { fileList } = this
if (this.fileList.length > 1) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.upload.volume.failed'),
description: this.$t('message.upload.file.limit'),
duration: 0
@ -217,15 +216,13 @@ export default {
},
timeout: 86400000
}).then((json) => {
this.$showNotification({
type: 'success',
this.$notification.success({
message: this.$t('message.success.upload'),
description: this.$t('message.success.upload.volume.description')
})
this.closeAction()
}).catch(e => {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.upload.failed'),
description: `${this.$t('message.upload.iso.failed.description')} - ${e}`,
duration: 0

View File

@ -631,8 +631,7 @@ export default {
displayname: values.displayname
}
if (!this.computeOffering || !this.computeOffering.id) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: this.$t('message.step.2.continue')
})
@ -643,8 +642,7 @@ export default {
var details = [this.cpuNumberKey, this.cpuSpeedKey, this.memoryKey]
for (var detail of details) {
if (!(values[detail] || this.computeOffering[detail])) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: this.$t('message.please.enter.valid.value') + ': ' + this.$t('label.' + detail.toLowerCase())
})
@ -659,8 +657,7 @@ export default {
var iopsDetails = [this.minIopsKey, this.maxIopsKey]
for (var iopsDetail of iopsDetails) {
if (!values[iopsDetail] || values[iopsDetail] < 0) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: this.$t('message.please.enter.valid.value') + ': ' + this.$t('label.' + iopsDetail.toLowerCase())
})
@ -669,8 +666,7 @@ export default {
params['details[0].' + iopsDetail] = values[iopsDetail]
}
if (values[this.minIopsKey] > values[this.maxIopsKey]) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: this.$t('error.form.message')
})
@ -688,8 +684,7 @@ export default {
var diskOfferingIndex = 0
for (var diskId in this.dataDisksOfferingsMapping) {
if (!this.dataDisksOfferingsMapping[diskId]) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: this.$t('message.select.disk.offering') + ': ' + diskId
})
@ -703,8 +698,7 @@ export default {
var nicIpIndex = 0
for (var nicId in this.nicsNetworksMapping) {
if (!this.nicsNetworksMapping[nicId].network) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: this.$t('message.select.nic.network') + ': ' + nicId
})
@ -715,8 +709,7 @@ export default {
nicNetworkIndex++
if ('ipAddress' in this.nicsNetworksMapping[nicId]) {
if (!this.nicsNetworksMapping[nicId].ipAddress) {
this.$showNotification({
type: 'error',
this.$notification.error({
message: this.$t('message.request.failed'),
description: this.$t('message.enter.valid.nic.ip') + ': ' + nicId
})

View File

@ -52,14 +52,20 @@ mocks = {
$notifyError: jest.fn((error) => {
return error
}),
$showNotification: jest.fn((option) => {
return {
type: option.type,
message: option.message,
description: 'test-description',
duration: option.duration
}
}),
$notification: {
error: jest.fn((option) => {
return option
}),
info: jest.fn((option) => {
return option
}),
success: jest.fn((option) => {
return option
}),
warning: jest.fn((option) => {
return option
})
},
$message: {
success: jest.fn((obj) => {
return obj
@ -1570,7 +1576,7 @@ describe('Views > AutogenView.vue', () => {
})
describe('pollActionCompletion()', () => {
it('check $showNotification when pollActionCompletion() is called with action is empty', (done) => {
it('check $notification when pollActionCompletion() is called with action is empty', (done) => {
const mockData = {
queryasyncjobresultresponse: {
jobstatus: 1,
@ -1588,7 +1594,7 @@ describe('Views > AutogenView.vue', () => {
wrapper.vm.pollActionCompletion(jobId, action)
setTimeout(() => {
expect(mocks.$showNotification).not.toHaveBeenCalled()
expect(mocks.$notification.info).not.toHaveBeenCalled()
expect(mockAxios).toHaveBeenCalled()
expect(mockAxios).toHaveBeenCalledWith({
url: '/',
@ -1605,7 +1611,7 @@ describe('Views > AutogenView.vue', () => {
})
})
it('check $showNotification when pollActionCompletion() is called with action is not empty', (done) => {
it('check $notification when pollActionCompletion() is called with action is not empty', (done) => {
const mockData = {
queryasyncjobresultresponse: {
jobstatus: 1,
@ -1626,9 +1632,8 @@ describe('Views > AutogenView.vue', () => {
wrapper.vm.pollActionCompletion(jobId, action)
setTimeout(() => {
expect(mocks.$showNotification).toHaveBeenCalled()
expect(mocks.$showNotification).toHaveLastReturnedWith({
type: 'info',
expect(mocks.$notification.info).toHaveBeenCalled()
expect(mocks.$notification.info).toHaveLastReturnedWith({
message: 'test-name-en',
description: 'test-description',
duration: 0

View File

@ -29,7 +29,20 @@ mocks = {
$message: {
error: jest.fn((message) => {})
},
$showNotification: jest.fn((obj) => {}),
$notification: {
error: jest.fn((option) => {
return option
}),
info: jest.fn((option) => {
return option
}),
success: jest.fn((option) => {
return option
}),
warning: jest.fn((option) => {
return option
})
},
$pollJob: jest.fn((obj) => {
switch (obj.jobId) {
case 'test-job-id-case-1':
@ -752,9 +765,8 @@ describe('Views > compute > MigrateWizard.vue', () => {
await wrapper.vm.submitForm()
setTimeout(() => {
expect(mocks.$showNotification).toHaveBeenCalled()
expect(mocks.$showNotification).toHaveBeenCalledWith({
type: 'error',
expect(mocks.$notification.error).toHaveBeenCalled()
expect(mocks.$notification.error).toHaveBeenCalledWith({
message: i18n.t('message.request.failed'),
description: 'Error: throw error message',
duration: 0