Merge remote-tracking branch 'origin/4.16'

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
Rohit Yadav 2021-12-30 16:52:30 +05:30
commit c84198d76d
102 changed files with 737 additions and 286 deletions

2
debian/control vendored
View File

@ -22,7 +22,7 @@ Description: CloudStack server library
Package: cloudstack-agent Package: cloudstack-agent
Architecture: all Architecture: all
Depends: ${python:Depends}, ${python3:Depends}, openjdk-11-jre-headless | java11-runtime-headless | java11-runtime | openjdk-11-jre-headless | zulu-11, cloudstack-common (= ${source:Version}), lsb-base (>= 9), openssh-client, qemu-kvm (>= 2.5), libvirt-bin (>= 1.3) | libvirt-daemon-system (>= 3.0), iproute2, ebtables, vlan, ipset, python3-libvirt, ethtool, iptables, lsb-release, aria2, ufw, apparmor Depends: ${python:Depends}, ${python3:Depends}, openjdk-11-jre-headless | java11-runtime-headless | java11-runtime | openjdk-11-jre-headless | zulu-11, cloudstack-common (= ${source:Version}), lsb-base (>= 9), openssh-client, qemu-kvm (>= 2.5) | qemu-system-x86 (>= 5.2), libvirt-bin (>= 1.3) | libvirt-daemon-system (>= 3.0), iproute2, ebtables, vlan, ipset, python3-libvirt, ethtool, iptables, lsb-release, aria2, ufw, apparmor
Recommends: init-system-helpers Recommends: init-system-helpers
Conflicts: cloud-agent, cloud-agent-libs, cloud-agent-deps, cloud-agent-scripts Conflicts: cloud-agent, cloud-agent-libs, cloud-agent-deps, cloud-agent-scripts
Description: CloudStack agent Description: CloudStack agent

View File

@ -3714,10 +3714,6 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return null; return null;
} }
if (_guestCpuArch != null && _guestCpuArch.equals("aarch64")) {
return DiskDef.DiskBus.SCSI;
}
String rootDiskController = details.get(VmDetailConstants.ROOT_DISK_CONTROLLER); String rootDiskController = details.get(VmDetailConstants.ROOT_DISK_CONTROLLER);
if (StringUtils.isNotBlank(rootDiskController)) { if (StringUtils.isNotBlank(rootDiskController)) {
s_logger.debug("Passed custom disk controller for ROOT disk " + rootDiskController); s_logger.debug("Passed custom disk controller for ROOT disk " + rootDiskController);
@ -3751,10 +3747,6 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
} }
private DiskDef.DiskBus getGuestDiskModel(final String platformEmulator, boolean isUefiEnabled) { private DiskDef.DiskBus getGuestDiskModel(final String platformEmulator, boolean isUefiEnabled) {
if (_guestCpuArch != null && _guestCpuArch.equals("aarch64")) {
return DiskDef.DiskBus.SCSI;
}
if (platformEmulator == null) { if (platformEmulator == null) {
return DiskDef.DiskBus.IDE; return DiskDef.DiskBus.IDE;
} else if (platformEmulator.startsWith("Other PV Virtio-SCSI")) { } else if (platformEmulator.startsWith("Other PV Virtio-SCSI")) {
@ -3765,6 +3757,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return DiskDef.DiskBus.VIRTIO; return DiskDef.DiskBus.VIRTIO;
} else if (isUefiEnabled && StringUtils.startsWithAny(platformEmulator, "Windows", "Other")) { } else if (isUefiEnabled && StringUtils.startsWithAny(platformEmulator, "Windows", "Other")) {
return DiskDef.DiskBus.SATA; return DiskDef.DiskBus.SATA;
} else if (_guestCpuArch != null && _guestCpuArch.equals("aarch64")) {
return DiskDef.DiskBus.SCSI;
} else { } else {
return DiskDef.DiskBus.IDE; return DiskDef.DiskBus.IDE;
} }

View File

@ -420,7 +420,7 @@ public class LibvirtComputingResourceTest {
public void testCreateDevicesWithSCSIDisk() { public void testCreateDevicesWithSCSIDisk() {
VirtualMachineTO to = createDefaultVM(false); VirtualMachineTO to = createDefaultVM(false);
to.setDetails(new HashMap<>()); to.setDetails(new HashMap<>());
libvirtComputingResourceSpy._guestCpuArch = "aarch64"; to.setPlatformEmulator("Other PV Virtio-SCSI");
GuestDef guest = new GuestDef(); GuestDef guest = new GuestDef();
guest.setGuestType(GuestType.KVM); guest.setGuestType(GuestType.KVM);

View File

@ -629,7 +629,7 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
private Answer copyTemplateToVolume(DataObject srcData, DataObject destData, Host destHost) { private Answer copyTemplateToVolume(DataObject srcData, DataObject destData, Host destHost) {
// Copy PowerFlex/ScaleIO template to volume // Copy PowerFlex/ScaleIO template to volume
LOGGER.debug("Initiating copy from PowerFlex template volume on host " + destHost != null ? destHost.getId() : ""); LOGGER.debug(String.format("Initiating copy from PowerFlex template volume on host %s", destHost != null ? destHost.getId() : "<not specified>"));
int primaryStorageDownloadWait = StorageManager.PRIMARY_STORAGE_DOWNLOAD_WAIT.value(); int primaryStorageDownloadWait = StorageManager.PRIMARY_STORAGE_DOWNLOAD_WAIT.value();
CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), primaryStorageDownloadWait, VirtualMachineManager.ExecuteInSequence.value()); CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), primaryStorageDownloadWait, VirtualMachineManager.ExecuteInSequence.value());
@ -648,7 +648,7 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
private Answer copyVolume(DataObject srcData, DataObject destData, Host destHost) { private Answer copyVolume(DataObject srcData, DataObject destData, Host destHost) {
// Copy PowerFlex/ScaleIO volume // Copy PowerFlex/ScaleIO volume
LOGGER.debug("Initiating copy from PowerFlex volume on host " + destHost != null ? destHost.getId() : ""); LOGGER.debug(String.format("Initiating copy from PowerFlex template volume on host %s", destHost != null ? destHost.getId() : "<not specified>"));
String value = configDao.getValue(Config.CopyVolumeWait.key()); String value = configDao.getValue(Config.CopyVolumeWait.key());
int copyVolumeWait = NumbersUtil.parseInt(value, Integer.parseInt(Config.CopyVolumeWait.getDefaultValue())); int copyVolumeWait = NumbersUtil.parseInt(value, Integer.parseInt(Config.CopyVolumeWait.getDefaultValue()));

View File

@ -3152,6 +3152,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
SearchCriteria<ServiceOfferingJoinVO> cpuSearchCriteria = _srvOfferingJoinDao.createSearchCriteria(); SearchCriteria<ServiceOfferingJoinVO> cpuSearchCriteria = _srvOfferingJoinDao.createSearchCriteria();
cpuSearchCriteria.addOr("minCpu", Op.NULL); cpuSearchCriteria.addOr("minCpu", Op.NULL);
cpuSearchCriteria.addOr("constraints", Op.SC, cpuConstraintSearchCriteria); cpuSearchCriteria.addOr("constraints", Op.SC, cpuConstraintSearchCriteria);
cpuSearchCriteria.addOr("minCpu", Op.GTEQ, cpuNumber);
sc.addAnd("cpuConstraints", SearchCriteria.Op.SC, cpuSearchCriteria); sc.addAnd("cpuConstraints", SearchCriteria.Op.SC, cpuSearchCriteria);
} }
@ -3164,6 +3165,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
SearchCriteria<ServiceOfferingJoinVO> memSearchCriteria = _srvOfferingJoinDao.createSearchCriteria(); SearchCriteria<ServiceOfferingJoinVO> memSearchCriteria = _srvOfferingJoinDao.createSearchCriteria();
memSearchCriteria.addOr("minMemory", Op.NULL); memSearchCriteria.addOr("minMemory", Op.NULL);
memSearchCriteria.addOr("memconstraints", Op.SC, memoryConstraintSearchCriteria); memSearchCriteria.addOr("memconstraints", Op.SC, memoryConstraintSearchCriteria);
memSearchCriteria.addOr("minMemory", Op.GTEQ, memory);
sc.addAnd("memoryConstraints", SearchCriteria.Op.SC, memSearchCriteria); sc.addAnd("memoryConstraints", SearchCriteria.Op.SC, memSearchCriteria);
} }
@ -3171,7 +3173,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
if (cpuSpeed != null) { if (cpuSpeed != null) {
SearchCriteria<ServiceOfferingJoinVO> cpuSpeedSearchCriteria = _srvOfferingJoinDao.createSearchCriteria(); SearchCriteria<ServiceOfferingJoinVO> cpuSpeedSearchCriteria = _srvOfferingJoinDao.createSearchCriteria();
cpuSpeedSearchCriteria.addOr("speed", Op.NULL); cpuSpeedSearchCriteria.addOr("speed", Op.NULL);
cpuSpeedSearchCriteria.addOr("speed", Op.EQ, cpuSpeed); cpuSpeedSearchCriteria.addOr("speed", Op.GTEQ, cpuSpeed);
sc.addAnd("cpuspeedconstraints", SearchCriteria.Op.SC, cpuSpeedSearchCriteria); sc.addAnd("cpuspeedconstraints", SearchCriteria.Op.SC, cpuSpeedSearchCriteria);
} }

View File

@ -259,9 +259,17 @@ public abstract class LibvirtServerDiscoverer extends DiscovererBase implements
sshConnection = new Connection(agentIp, 22); sshConnection = new Connection(agentIp, 22);
sshConnection.connect(null, 60000, 60000); sshConnection.connect(null, 60000, 60000);
final String privateKey = _configDao.getValue("ssh.privatekey");
if (!SSHCmdHelper.acquireAuthorizedConnectionWithPublicKey(sshConnection, username, privateKey)) {
s_logger.error("Failed to authenticate with ssh key");
if (org.apache.commons.lang3.StringUtils.isEmpty(password)) {
throw new DiscoveredWithErrorException("Authentication error with ssh private key");
}
if (!sshConnection.authenticateWithPassword(username, password)) { if (!sshConnection.authenticateWithPassword(username, password)) {
s_logger.debug("Failed to authenticate"); s_logger.error("Failed to authenticate with password");
throw new DiscoveredWithErrorException("Authentication error"); throw new DiscoveredWithErrorException("Authentication error with host password");
}
} }
if (!SSHCmdHelper.sshExecuteCmd(sshConnection, "ls /dev/kvm")) { if (!SSHCmdHelper.sshExecuteCmd(sshConnection, "ls /dev/kvm")) {

View File

@ -165,7 +165,8 @@ import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.user.Account; import com.cloud.user.Account;
import com.cloud.user.AccountManager; import com.cloud.user.AccountManager;
import com.cloud.utils.Pair; import com.cloud.utils.Ternary;
import com.cloud.utils.StringUtils;
import com.cloud.utils.UriUtils; import com.cloud.utils.UriUtils;
import com.cloud.utils.component.Manager; import com.cloud.utils.component.Manager;
import com.cloud.utils.component.ManagerBase; import com.cloud.utils.component.ManagerBase;
@ -695,10 +696,17 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
throw new InvalidParameterValueException("Can't specify cluster without specifying the pod"); throw new InvalidParameterValueException("Can't specify cluster without specifying the pod");
} }
List<String> skipList = Arrays.asList(HypervisorType.VMware.name().toLowerCase(Locale.ROOT), Type.SecondaryStorage.name().toLowerCase(Locale.ROOT)); List<String> skipList = Arrays.asList(HypervisorType.VMware.name().toLowerCase(Locale.ROOT), Type.SecondaryStorage.name().toLowerCase(Locale.ROOT));
if (!skipList.contains(hypervisorType.toLowerCase(Locale.ROOT)) && if (!skipList.contains(hypervisorType.toLowerCase(Locale.ROOT))) {
(StringUtils.isAnyEmpty(username, password))) { if (HypervisorType.KVM.toString().equalsIgnoreCase(hypervisorType)) {
if (org.apache.commons.lang3.StringUtils.isBlank(username)) {
throw new InvalidParameterValueException("Username need to be provided.");
}
} else {
if (org.apache.commons.lang3.StringUtils.isBlank(username) || org.apache.commons.lang3.StringUtils.isBlank(password)) {
throw new InvalidParameterValueException("Username and Password need to be provided."); throw new InvalidParameterValueException("Username and Password need to be provided.");
} }
}
}
if (clusterId != null) { if (clusterId != null) {
if (_clusterDao.findById(clusterId) == null) { if (_clusterDao.findById(clusterId) == null) {
@ -2731,8 +2739,8 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
} }
final boolean sshToAgent = Boolean.parseBoolean(_configDao.getValue(KvmSshToAgentEnabled.key())); final boolean sshToAgent = Boolean.parseBoolean(_configDao.getValue(KvmSshToAgentEnabled.key()));
if (sshToAgent) { if (sshToAgent) {
Pair<String, String> credentials = getHostCredentials(host); Ternary<String, String, String> credentials = getHostCredentials(host);
connectAndRestartAgentOnHost(host, credentials.first(), credentials.second()); connectAndRestartAgentOnHost(host, credentials.first(), credentials.second(), credentials.third());
} else { } else {
throw new CloudRuntimeException("SSH access is disabled, cannot cancel maintenance mode as " + throw new CloudRuntimeException("SSH access is disabled, cannot cancel maintenance mode as " +
"host agent is not connected"); "host agent is not connected");
@ -2743,22 +2751,23 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
* Get host credentials * Get host credentials
* @throws CloudRuntimeException if username or password are not found * @throws CloudRuntimeException if username or password are not found
*/ */
protected Pair<String, String> getHostCredentials(HostVO host) { protected Ternary<String, String, String> getHostCredentials(HostVO host) {
_hostDao.loadDetails(host); _hostDao.loadDetails(host);
final String password = host.getDetail("password"); final String password = host.getDetail("password");
final String username = host.getDetail("username"); final String username = host.getDetail("username");
if (password == null || username == null) { final String privateKey = _configDao.getValue("ssh.privatekey");
throw new CloudRuntimeException("SSH to agent is enabled, but username/password credentials are not found"); if ((password == null && privateKey == null) || username == null) {
throw new CloudRuntimeException("SSH to agent is enabled, but username and password or private key are not found");
} }
return new Pair<>(username, password); return new Ternary<>(username, password, privateKey);
} }
/** /**
* True if agent is restarted via SSH. Assumes kvm.ssh.to.agent = true and host status is not Up * True if agent is restarted via SSH. Assumes kvm.ssh.to.agent = true and host status is not Up
*/ */
protected void connectAndRestartAgentOnHost(HostVO host, String username, String password) { protected void connectAndRestartAgentOnHost(HostVO host, String username, String password, String privateKey) {
final com.trilead.ssh2.Connection connection = SSHCmdHelper.acquireAuthorizedConnection( final com.trilead.ssh2.Connection connection = SSHCmdHelper.acquireAuthorizedConnection(
host.getPrivateIpAddress(), 22, username, password); host.getPrivateIpAddress(), 22, username, password, privateKey);
if (connection == null) { if (connection == null) {
throw new CloudRuntimeException(String.format("SSH to agent is enabled, but failed to connect to %s via IP address [%s].", host, host.getPrivateIpAddress())); throw new CloudRuntimeException(String.format("SSH to agent is enabled, but failed to connect to %s via IP address [%s].", host, host.getPrivateIpAddress()));
} }

View File

@ -2136,6 +2136,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
if (hostIds.isEmpty()) { if (hostIds.isEmpty()) {
return null; return null;
} }
Collections.shuffle(hostIds);
for (Long hostId : hostIds) { for (Long hostId : hostIds) {
Host host = _hostDao.findById(hostId); Host host = _hostDao.findById(hostId);

View File

@ -67,7 +67,7 @@ import com.cloud.host.Status;
import com.cloud.host.dao.HostDao; import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.Hypervisor;
import com.cloud.storage.StorageManager; import com.cloud.storage.StorageManager;
import com.cloud.utils.Pair; import com.cloud.utils.Ternary;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.fsm.NoTransitionException; import com.cloud.utils.fsm.NoTransitionException;
import com.cloud.utils.ssh.SSHCmdHelper; import com.cloud.utils.ssh.SSHCmdHelper;
@ -125,6 +125,7 @@ public class ResourceManagerImplTest {
private static long hostId = 1L; private static long hostId = 1L;
private static final String hostUsername = "user"; private static final String hostUsername = "user";
private static final String hostPassword = "password"; private static final String hostPassword = "password";
private static final String hostPrivateKey = "privatekey";
private static final String hostPrivateIp = "192.168.1.10"; private static final String hostPrivateIp = "192.168.1.10";
private static long vm1Id = 1L; private static long vm1Id = 1L;
@ -148,6 +149,7 @@ public class ResourceManagerImplTest {
when(hostDao.findById(hostId)).thenReturn(host); when(hostDao.findById(hostId)).thenReturn(host);
when(host.getDetail("username")).thenReturn(hostUsername); when(host.getDetail("username")).thenReturn(hostUsername);
when(host.getDetail("password")).thenReturn(hostPassword); when(host.getDetail("password")).thenReturn(hostPassword);
when(configurationDao.getValue("ssh.privatekey")).thenReturn(hostPrivateKey);
when(host.getStatus()).thenReturn(Status.Up); when(host.getStatus()).thenReturn(Status.Up);
when(host.getPrivateIpAddress()).thenReturn(hostPrivateIp); when(host.getPrivateIpAddress()).thenReturn(hostPrivateIp);
when(vm1.getId()).thenReturn(vm1Id); when(vm1.getId()).thenReturn(vm1Id);
@ -171,7 +173,7 @@ public class ResourceManagerImplTest {
PowerMockito.mockStatic(SSHCmdHelper.class); PowerMockito.mockStatic(SSHCmdHelper.class);
BDDMockito.given(SSHCmdHelper.acquireAuthorizedConnection(eq(hostPrivateIp), eq(22), BDDMockito.given(SSHCmdHelper.acquireAuthorizedConnection(eq(hostPrivateIp), eq(22),
eq(hostUsername), eq(hostPassword))).willReturn(sshConnection); eq(hostUsername), eq(hostPassword), eq(hostPrivateKey))).willReturn(sshConnection);
BDDMockito.given(SSHCmdHelper.sshExecuteCmdOneShot(eq(sshConnection), BDDMockito.given(SSHCmdHelper.sshExecuteCmdOneShot(eq(sshConnection),
eq("service cloudstack-agent restart"))). eq("service cloudstack-agent restart"))).
willReturn(new SSHCmdHelper.SSHCmdResult(0,"","")); willReturn(new SSHCmdHelper.SSHCmdResult(0,"",""));
@ -292,34 +294,36 @@ public class ResourceManagerImplTest {
@Test(expected = CloudRuntimeException.class) @Test(expected = CloudRuntimeException.class)
public void testGetHostCredentialsMissingParameter() { public void testGetHostCredentialsMissingParameter() {
when(host.getDetail("password")).thenReturn(null); when(host.getDetail("password")).thenReturn(null);
when(configurationDao.getValue("ssh.privatekey")).thenReturn(null);
resourceManager.getHostCredentials(host); resourceManager.getHostCredentials(host);
} }
@Test @Test
public void testGetHostCredentials() { public void testGetHostCredentials() {
Pair<String, String> credentials = resourceManager.getHostCredentials(host); Ternary<String, String, String> credentials = resourceManager.getHostCredentials(host);
Assert.assertNotNull(credentials); Assert.assertNotNull(credentials);
Assert.assertEquals(hostUsername, credentials.first()); Assert.assertEquals(hostUsername, credentials.first());
Assert.assertEquals(hostPassword, credentials.second()); Assert.assertEquals(hostPassword, credentials.second());
Assert.assertEquals(hostPrivateKey, credentials.third());
} }
@Test(expected = CloudRuntimeException.class) @Test(expected = CloudRuntimeException.class)
public void testConnectAndRestartAgentOnHostCannotConnect() { public void testConnectAndRestartAgentOnHostCannotConnect() {
BDDMockito.given(SSHCmdHelper.acquireAuthorizedConnection(eq(hostPrivateIp), eq(22), BDDMockito.given(SSHCmdHelper.acquireAuthorizedConnection(eq(hostPrivateIp), eq(22),
eq(hostUsername), eq(hostPassword))).willReturn(null); eq(hostUsername), eq(hostPassword), eq(hostPrivateKey))).willReturn(null);
resourceManager.connectAndRestartAgentOnHost(host, hostUsername, hostPassword); resourceManager.connectAndRestartAgentOnHost(host, hostUsername, hostPassword, hostPrivateKey);
} }
@Test(expected = CloudRuntimeException.class) @Test(expected = CloudRuntimeException.class)
public void testConnectAndRestartAgentOnHostCannotRestart() throws Exception { public void testConnectAndRestartAgentOnHostCannotRestart() throws Exception {
BDDMockito.given(SSHCmdHelper.sshExecuteCmdOneShot(eq(sshConnection), BDDMockito.given(SSHCmdHelper.sshExecuteCmdOneShot(eq(sshConnection),
eq("service cloudstack-agent restart"))).willThrow(new SshException("exception")); eq("service cloudstack-agent restart"))).willThrow(new SshException("exception"));
resourceManager.connectAndRestartAgentOnHost(host, hostUsername, hostPassword); resourceManager.connectAndRestartAgentOnHost(host, hostUsername, hostPassword, hostPrivateKey);
} }
@Test @Test
public void testConnectAndRestartAgentOnHost() { public void testConnectAndRestartAgentOnHost() {
resourceManager.connectAndRestartAgentOnHost(host, hostUsername, hostPassword); resourceManager.connectAndRestartAgentOnHost(host, hostUsername, hostPassword, hostPrivateKey);
} }
@Test @Test
@ -327,7 +331,7 @@ public class ResourceManagerImplTest {
when(host.getStatus()).thenReturn(Status.Disconnected); when(host.getStatus()).thenReturn(Status.Disconnected);
resourceManager.handleAgentIfNotConnected(host, false); resourceManager.handleAgentIfNotConnected(host, false);
verify(resourceManager).getHostCredentials(eq(host)); verify(resourceManager).getHostCredentials(eq(host));
verify(resourceManager).connectAndRestartAgentOnHost(eq(host), eq(hostUsername), eq(hostPassword)); verify(resourceManager).connectAndRestartAgentOnHost(eq(host), eq(hostUsername), eq(hostPassword), eq(hostPrivateKey));
} }
@Test @Test
@ -335,7 +339,7 @@ public class ResourceManagerImplTest {
when(host.getStatus()).thenReturn(Status.Up); when(host.getStatus()).thenReturn(Status.Up);
resourceManager.handleAgentIfNotConnected(host, false); resourceManager.handleAgentIfNotConnected(host, false);
verify(resourceManager, never()).getHostCredentials(eq(host)); verify(resourceManager, never()).getHostCredentials(eq(host));
verify(resourceManager, never()).connectAndRestartAgentOnHost(eq(host), eq(hostUsername), eq(hostPassword)); verify(resourceManager, never()).connectAndRestartAgentOnHost(eq(host), eq(hostUsername), eq(hostPassword), eq(hostPrivateKey));
} }
@Test(expected = CloudRuntimeException.class) @Test(expected = CloudRuntimeException.class)
@ -351,14 +355,14 @@ public class ResourceManagerImplTest {
when(configurationDao.getValue(ResourceManager.KvmSshToAgentEnabled.key())).thenReturn("false"); when(configurationDao.getValue(ResourceManager.KvmSshToAgentEnabled.key())).thenReturn("false");
resourceManager.handleAgentIfNotConnected(host, false); resourceManager.handleAgentIfNotConnected(host, false);
verify(resourceManager, never()).getHostCredentials(eq(host)); verify(resourceManager, never()).getHostCredentials(eq(host));
verify(resourceManager, never()).connectAndRestartAgentOnHost(eq(host), eq(hostUsername), eq(hostPassword)); verify(resourceManager, never()).connectAndRestartAgentOnHost(eq(host), eq(hostUsername), eq(hostPassword), eq(hostPrivateKey));
} }
@Test @Test
public void testHandleAgentVMsMigrating() { public void testHandleAgentVMsMigrating() {
resourceManager.handleAgentIfNotConnected(host, true); resourceManager.handleAgentIfNotConnected(host, true);
verify(resourceManager, never()).getHostCredentials(eq(host)); verify(resourceManager, never()).getHostCredentials(eq(host));
verify(resourceManager, never()).connectAndRestartAgentOnHost(eq(host), eq(hostUsername), eq(hostPassword)); verify(resourceManager, never()).connectAndRestartAgentOnHost(eq(host), eq(hostUsername), eq(hostPassword), eq(hostPrivateKey));
} }
private void setupNoPendingMigrationRetries() { private void setupNoPendingMigrationRetries() {

View File

@ -635,7 +635,7 @@ class ConfigDriveUtils:
""" """
ssh.execute("umount -d %s" % mount_path) ssh.execute("umount -d %s" % mount_path)
# Give the VM time to unlock the iso device # Give the VM time to unlock the iso device
time.sleep(2) time.sleep(0.5)
# Verify umount # Verify umount
result = ssh.execute("ls %s" % mount_path) result = ssh.execute("ls %s" % mount_path)
self.assertTrue(len(result) == 0, self.assertTrue(len(result) == 0,
@ -1089,10 +1089,8 @@ class ConfigDriveUtils:
vm.details = vm_new_ssh.details vm.details = vm_new_ssh.details
# reset SSH key also resets the password. # reset SSH key also removes the password (see https://github.com/apache/cloudstack/pull/4819)
self._decrypt_password(vm) vm.password_test = ConfigDriveUtils.PasswordTest(expect_pw=False)
vm.password_test = ConfigDriveUtils.PasswordTest(vm=vm)
vm.key_pair = self.keypair vm.key_pair = self.keypair
if public_ip: if public_ip:
@ -1119,7 +1117,7 @@ class ConfigDriveUtils:
cipher = PKCS1_v1_5.new(key) cipher = PKCS1_v1_5.new(key)
new_password = cipher.decrypt(b64decode(password_), None) new_password = cipher.decrypt(b64decode(password_), None)
if new_password: if new_password:
vm.password = new_password vm.password = new_password.decode()
else: else:
self.fail("Failed to decrypt new password") self.fail("Failed to decrypt new password")
except ImportError: except ImportError:
@ -1511,7 +1509,6 @@ class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils):
tries = 1 if negative_test else 3 tries = 1 if negative_test else 3
private_key_file_location = keypair.private_key_file if keypair else None private_key_file_location = keypair.private_key_file if keypair else None
@retry(tries=tries)
def retry_ssh(): def retry_ssh():
ssh_client = vm.get_ssh_client( ssh_client = vm.get_ssh_client(
ipaddress=public_ip.ipaddress.ipaddress, ipaddress=public_ip.ipaddress.ipaddress,
@ -1732,8 +1729,7 @@ class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils):
self.api_client.restartVPC(cmd) self.api_client.restartVPC(cmd)
self.debug("Restarted VPC with ID - %s" % vpc.id) self.debug("Restarted VPC with ID - %s" % vpc.id)
# was tags=["advanced", "isonw"] @attr(tags=["advanced", "isonw"], required_hardware="true")
@attr(tags=["TODO"], required_hardware="true")
def test_configdrive_isolated_network(self): def test_configdrive_isolated_network(self):
"""Test Configdrive as provider for isolated Networks """Test Configdrive as provider for isolated Networks
to provide userdata and password reset functionality to provide userdata and password reset functionality
@ -2500,6 +2496,7 @@ class TestConfigDrive(cloudstackTestCase, ConfigDriveUtils):
# ===================================================================== # =====================================================================
self.debug("+++ Scenario: " self.debug("+++ Scenario: "
"validate updated userdata after migrate") "validate updated userdata after migrate")
time.sleep(30)
host = self.migrate_VM(vm) host = self.migrate_VM(vm)
vm.hostname = host.name vm.hostname = host.name
self.then_config_drive_is_as_expected(vm, public_ip_1, metadata=True) self.then_config_drive_is_as_expected(vm, public_ip_1, metadata=True)

View File

@ -10,6 +10,7 @@
"docBase": "http://docs.cloudstack.apache.org/en/latest", "docBase": "http://docs.cloudstack.apache.org/en/latest",
"appTitle": "CloudStack", "appTitle": "CloudStack",
"footer": "Licensed under the <a href='http://www.apache.org/licenses/' target='_blank'>Apache License</a>, Version 2.0.", "footer": "Licensed under the <a href='http://www.apache.org/licenses/' target='_blank'>Apache License</a>, Version 2.0.",
"loginFooter": "",
"logo": "assets/logo.svg", "logo": "assets/logo.svg",
"banner": "assets/banner.svg", "banner": "assets/banner.svg",
"error": { "error": {

View File

@ -468,6 +468,8 @@
"label.associatednetworkid": "Associated Network ID", "label.associatednetworkid": "Associated Network ID",
"label.associatednetworkname": "Network Name", "label.associatednetworkname": "Network Name",
"label.asyncbackup": "Async Backup", "label.asyncbackup": "Async Backup",
"label.authentication.method": "Authentication Method",
"label.authentication.sshkey": "System SSH Key",
"label.author.email": "Author e-mail", "label.author.email": "Author e-mail",
"label.author.name": "Author name", "label.author.name": "Author name",
"label.auto.assign": "Automatically assign", "label.auto.assign": "Automatically assign",
@ -577,6 +579,7 @@
"label.cks.cluster.size": "Cluster size (Worker nodes)", "label.cks.cluster.size": "Cluster size (Worker nodes)",
"label.cleanup": "Clean up", "label.cleanup": "Clean up",
"label.clear": "Clear", "label.clear": "Clear",
"label.clear.notification": "Clear notification",
"label.clear.list": "Clear list", "label.clear.list": "Clear list",
"label.close": "Close", "label.close": "Close",
"label.cloud.console": "Cloud Management Console", "label.cloud.console": "Cloud Management Console",
@ -2595,6 +2598,7 @@
"message.add.firewall.rule.processing": "Adding new Firewall rule...", "message.add.firewall.rule.processing": "Adding new Firewall rule...",
"message.add.guest.network": "Please confirm that you would like to add a guest network", "message.add.guest.network": "Please confirm that you would like to add a guest network",
"message.add.host": "Please specify the following parameters to add a new host", "message.add.host": "Please specify the following parameters to add a new host",
"message.add.host.sshkey": "WARNING: In order to add a host with SSH key, you must ensure your hypervisor host has been configured correctly.",
"message.add.ip.range": "Add an IP range to public network in zone", "message.add.ip.range": "Add an IP range to public network in zone",
"message.add.ip.range.direct.network": "Add an IP range to direct network <b><span id=\"directnetwork_name\"></span></b> in zone <b><span id=\"zone_name\"></span></b>", "message.add.ip.range.direct.network": "Add an IP range to direct network <b><span id=\"directnetwork_name\"></span></b> in zone <b><span id=\"zone_name\"></span></b>",
"message.add.ip.range.to.pod": "<p>Add an IP range to pod: <b><span id=\"pod_name_label\"></span></b></p>", "message.add.ip.range.to.pod": "<p>Add an IP range to pod: <b><span id=\"pod_name_label\"></span></b></p>",

View File

@ -20,7 +20,6 @@
<a-row :gutter="6"> <a-row :gutter="6">
<a-col :md="24" :lg="layout === 'horizontal' ? 12 : 24"> <a-col :md="24" :lg="layout === 'horizontal' ? 12 : 24">
<a-checkbox <a-checkbox
v-decorator="[checkBoxDecorator, {}]"
:checked="checked" :checked="checked"
@change="handleCheckChange"> @change="handleCheckChange">
{{ checkBoxLabel }} {{ checkBoxLabel }}
@ -31,8 +30,7 @@
v-if="reversed != checked" v-if="reversed != checked"
:label="selectLabel"> :label="selectLabel">
<a-select <a-select
v-decorator="[selectDecorator, { initialValue: selectedOption ? selectedOption : getSelectInitialValue()}]" v-model="selectedOption"
:defaultValue="selectDecorator ? undefined : selectedOption ? selectedOption : getSelectInitialValue()"
showSearch showSearch
optionFilterProp="children" optionFilterProp="children"
:filterOption="(input, option) => { :filterOption="(input, option) => {
@ -69,10 +67,6 @@ export default {
type: String, type: String,
required: true required: true
}, },
checkBoxDecorator: {
type: String,
default: ''
},
defaultCheckBoxValue: { defaultCheckBoxValue: {
type: Boolean, type: Boolean,
default: false default: false
@ -85,10 +79,6 @@ export default {
type: String, type: String,
default: '' default: ''
}, },
selectDecorator: {
type: String,
default: ''
},
reversed: { reversed: {
type: Boolean, type: Boolean,
default: false default: false
@ -97,12 +87,21 @@ export default {
data () { data () {
return { return {
checked: false, checked: false,
selectedOption: null selectedOption: null,
selectOptionsTimer: null
} }
}, },
created () { created () {
this.checked = this.defaultCheckBoxValue this.checked = this.defaultCheckBoxValue
}, },
watch: {
selectOptions () {
clearTimeout(this.selectOptionsTimer)
this.selectOptionsTimer = setTimeout(() => {
this.handleSelectOptionsUpdated()
}, 50)
}
},
computed: { computed: {
selectSource () { selectSource () {
return this.selectOptions.map(item => { return this.selectOptions.map(item => {
@ -118,18 +117,23 @@ export default {
arrayHasItems (array) { arrayHasItems (array) {
return array !== null && array !== undefined && Array.isArray(array) && array.length > 0 return array !== null && array !== undefined && Array.isArray(array) && array.length > 0
}, },
getSelectInitialValue () {
const initialValue = this.selectSource?.filter(x => x.enabled !== false)?.[0]?.id || ''
this.handleSelectChange(initialValue)
return initialValue
},
handleCheckChange (e) { handleCheckChange (e) {
this.checked = e.target.checked this.checked = e.target.checked
if (this.checked && !this.selectedOption) {
this.selectedOption = this.selectSource?.filter(x => x.enabled !== false)?.[0]?.id || null
}
this.$emit('handle-checkselectpair-change', this.resourceKey, this.checked, this.selectedOption) this.$emit('handle-checkselectpair-change', this.resourceKey, this.checked, this.selectedOption)
}, },
handleSelectChange (val) { handleSelectChange (val) {
this.selectedOption = val this.selectedOption = val
this.$emit('handle-checkselectpair-change', this.resourceKey, this.checked, this.selectedOption) this.$emit('handle-checkselectpair-change', this.resourceKey, this.checked, this.selectedOption)
},
handleSelectOptionsUpdated () {
if (!this.checked) return
var enabledOptions = this.selectSource?.filter(x => x.enabled !== false) || []
if (this.selectedOption && !enabledOptions.includes(this.selectedOption)) {
this.handleSelectChange(enabledOptions[0]?.id || null)
}
} }
} }
} }

View File

@ -84,7 +84,8 @@ export default {
}, },
data () { data () {
return { return {
image: '' image: '',
countNotify: 0
} }
}, },
created () { created () {
@ -92,6 +93,12 @@ export default {
eventBus.$on('refresh-header', () => { eventBus.$on('refresh-header', () => {
this.getIcon() this.getIcon()
}) })
this.$store.watch(
(state, getters) => getters.countNotify,
(newValue, oldValue) => {
this.countNotify = newValue
}
)
}, },
watch: { watch: {
image () { image () {
@ -137,6 +144,10 @@ export default {
description: err.message description: err.message
}) })
}) })
},
clearAllNotify () {
this.$store.commit('SET_COUNT_NOTIFY', 0)
this.$notification.destroy()
} }
} }
} }

View File

@ -91,6 +91,13 @@
/> />
</a-affix> </a-affix>
<a-button
v-if="showClear"
type="default"
size="small"
class="button-clear-notification"
@click="onClearNotification">{{ $t('label.clear.notification') }}</a-button>
<!-- layout content --> <!-- layout content -->
<a-layout-content class="layout-content" :class="{'is-header-fixed': fixedHeader}"> <a-layout-content class="layout-content" :class="{'is-header-fixed': fixedHeader}">
<slot></slot> <slot></slot>
@ -128,7 +135,8 @@ export default {
return { return {
collapsed: false, collapsed: false,
menus: [], menus: [],
showSetting: false showSetting: false,
showClear: false
} }
}, },
computed: { computed: {
@ -161,6 +169,12 @@ export default {
} else { } else {
document.body.classList.remove('dark-mode') document.body.classList.remove('dark-mode')
} }
},
'$store.getters.countNotify' (countNotify) {
this.showClear = false
if (countNotify && countNotify > 0) {
this.showClear = true
}
} }
}, },
provide: function () { provide: function () {
@ -212,6 +226,10 @@ export default {
}, },
toggleSetting (showSetting) { toggleSetting (showSetting) {
this.showSetting = showSetting this.showSetting = showSetting
},
onClearNotification () {
this.$notification.destroy()
this.$store.commit('SET_COUNT_NOTIFY', 0)
} }
} }
} }

View File

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

View File

@ -651,7 +651,7 @@
</div> </div>
</div> </div>
<div class="account-center-tags" v-if="!isStatic && resourceType && 'listTags' in $store.getters.apis"> <div class="account-center-tags" v-if="!isStatic && resourceType && tagsSupportingResourceTypes.includes(this.resourceType) && 'listTags' in $store.getters.apis">
<a-divider/> <a-divider/>
<a-spin :spinning="loadingTags"> <a-spin :spinning="loadingTags">
<div class="title">{{ $t('label.tags') }}</div> <div class="title">{{ $t('label.tags') }}</div>
@ -772,11 +772,13 @@ export default {
this.showKeys = false this.showKeys = false
this.setData() this.setData()
if (this.tagsSupportingResourceTypes.includes(this.resourceType)) {
if ('tags' in this.resource) { if ('tags' in this.resource) {
this.tags = this.resource.tags this.tags = this.resource.tags
} else if (this.resourceType) { } else if (this.resourceType) {
this.getTags() this.getTags()
} }
}
if ('apikey' in this.resource) { if ('apikey' in this.resource) {
this.getUserKeys() this.getUserKeys()
} }
@ -794,6 +796,12 @@ export default {
await this.getIcons() await this.getIcons()
}, },
computed: { computed: {
tagsSupportingResourceTypes () {
return ['UserVm', 'Template', 'ISO', 'Volume', 'Snapshot', 'Backup', 'Network',
'LoadBalancer', 'PortForwardingRule', 'FirewallRule', 'SecurityGroup', 'SecurityGroupRule',
'PublicIpAddress', 'Project', 'Account', 'Vpc', 'NetworkACL', 'StaticRoute', 'VMSnapshot',
'RemoteAccessVpn', 'User', 'SnapshotPolicy', 'VpcOffering']
},
name () { name () {
return this.resource.displayname || this.resource.displaytext || this.resource.name || this.resource.username || return this.resource.displayname || this.resource.displaytext || this.resource.name || this.resource.username ||
this.resource.ipaddress || this.resource.virtualmachinename || this.resource.templatetype this.resource.ipaddress || this.resource.virtualmachinename || this.resource.templatetype

View File

@ -514,7 +514,8 @@ export default {
json.updateconfigurationresponse.configuration && json.updateconfigurationresponse.configuration &&
!json.updateconfigurationresponse.configuration.isdynamic && !json.updateconfigurationresponse.configuration.isdynamic &&
['Admin'].includes(this.$store.getters.userInfo.roletype)) { ['Admin'].includes(this.$store.getters.userInfo.roletype)) {
this.$notification.warning({ this.$showNotification({
type: 'warning',
message: this.$t('label.status'), message: this.$t('label.status'),
description: this.$t('message.restart.mgmt.server') description: this.$t('message.restart.mgmt.server')
}) })

View File

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

View File

@ -164,7 +164,8 @@ export default {
}).catch(error => { }).catch(error => {
console.error(error) console.error(error)
this.$message.error(this.$t('message.error.save.setting')) this.$message.error(this.$t('message.error.save.setting'))
this.$notification.error({ this.$showNotification({
type: 'error',
message: this.$t('label.error'), message: this.$t('label.error'),
description: this.$t('message.error.try.save.setting') description: this.$t('message.error.try.save.setting')
}) })

View File

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

View File

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

View File

@ -17,6 +17,12 @@
<template> <template>
<div id="userLayout" :class="['user-layout', device]"> <div id="userLayout" :class="['user-layout', device]">
<a-button
v-if="showClear"
type="default"
size="small"
class="button-clear-notification"
@click="onClearNotification">{{ $t('label.clear.notification') }}</a-button>
<div class="user-layout-container"> <div class="user-layout-container">
<div class="user-layout-header"> <div class="user-layout-header">
<img <img
@ -31,6 +37,9 @@
</div> </div>
<route-view></route-view> <route-view></route-view>
</div> </div>
<div class="user-layout-footer" v-if="$config.loginFooter">
<label v-html="$config.loginFooter"></label>
</div>
</div> </div>
</template> </template>
@ -45,7 +54,9 @@ export default {
components: { RouteView }, components: { RouteView },
mixins: [mixinDevice], mixins: [mixinDevice],
data () { data () {
return {} return {
showClear: false
}
}, },
watch: { watch: {
'$store.getters.darkMode' (darkMode) { '$store.getters.darkMode' (darkMode) {
@ -54,6 +65,12 @@ export default {
} else { } else {
document.body.classList.remove('dark-mode') document.body.classList.remove('dark-mode')
} }
},
'$store.getters.countNotify' (countNotify) {
this.showClear = false
if (countNotify && countNotify > 0) {
this.showClear = true
}
} }
}, },
mounted () { mounted () {
@ -66,6 +83,12 @@ export default {
beforeDestroy () { beforeDestroy () {
document.body.classList.remove('userLayout') document.body.classList.remove('userLayout')
document.body.classList.remove('dark-mode') document.body.classList.remove('dark-mode')
},
methods: {
onClearNotification () {
this.$notification.destroy()
this.$store.commit('SET_COUNT_NOTIFY', 0)
}
} }
} }
</script> </script>
@ -97,5 +120,25 @@ export default {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
} }
&-footer {
display: flex;
flex-direction: column;
position: absolute;
bottom: 20px;
text-align: center;
width: 100%;
@media (max-height: 600px) {
position: relative;
margin-top: 50px;
}
label {
width: 368px;
font-weight: 500;
margin: 0 auto;
}
}
} }
</style> </style>

View File

@ -78,10 +78,19 @@ router.beforeEach((to, from, next) => {
}) })
}) })
.catch(() => { .catch(() => {
let countNotify = store.getters.countNotify
countNotify++
store.commit('SET_COUNT_NOTIFY', countNotify)
notification.error({ notification.error({
top: '65px',
message: 'Error', message: 'Error',
description: i18n.t('message.error.discovering.feature'), description: i18n.t('message.error.discovering.feature'),
duration: 0 duration: 0,
onClose: () => {
let countNotify = store.getters.countNotify
countNotify > 0 ? countNotify-- : countNotify = 0
store.commit('SET_COUNT_NOTIFY', countNotify)
}
}) })
store.dispatch('Logout').then(() => { store.dispatch('Logout').then(() => {
next({ path: '/user/login', query: { redirect: to.fullPath } }) next({ path: '/user/login', query: { redirect: to.fullPath } })

View File

@ -40,7 +40,8 @@ const getters = {
domainStore: state => state.user.domainStore, domainStore: state => state.user.domainStore,
darkMode: state => state.user.darkMode, darkMode: state => state.user.darkMode,
themeSetting: state => state.user.themeSetting, themeSetting: state => state.user.themeSetting,
defaultListViewPageSize: state => state.user.defaultListViewPageSize defaultListViewPageSize: state => state.user.defaultListViewPageSize,
countNotify: state => state.user.countNotify
} }
export default getters export default getters

View File

@ -55,7 +55,8 @@ const user = {
domainStore: {}, domainStore: {},
darkMode: false, darkMode: false,
themeSetting: {}, themeSetting: {},
defaultListViewPageSize: 20 defaultListViewPageSize: 20,
countNotify: 0
}, },
mutations: { mutations: {
@ -121,6 +122,9 @@ const user = {
}, },
SET_DEFAULT_LISTVIEW_PAGE_SIZE: (state, defaultListViewPageSize) => { SET_DEFAULT_LISTVIEW_PAGE_SIZE: (state, defaultListViewPageSize) => {
state.defaultListViewPageSize = defaultListViewPageSize state.defaultListViewPageSize = defaultListViewPageSize
},
SET_COUNT_NOTIFY (state, number) {
state.countNotify = number
} }
}, },

View File

@ -901,4 +901,8 @@
.ant-empty-normal { .ant-empty-normal {
color: @dark-text-color-3; color: @dark-text-color-3;
} }
.button-clear-notification {
background-color: @dark-secondary-bgColor;
}
} }

View File

@ -344,3 +344,10 @@ a {
border-color: @primary-color; border-color: @primary-color;
} }
} }
.button-clear-notification {
position: fixed;
right: 287px;
top: 35px;
z-index: 1001;
}

View File

@ -132,11 +132,20 @@ export const pollJobPlugin = {
desc = `(${name}) ${desc}` desc = `(${name}) ${desc}`
} }
if (!bulkAction) { if (!bulkAction) {
let countNotify = store.getters.countNotify
countNotify++
store.commit('SET_COUNT_NOTIFY', countNotify)
notification.error({ notification.error({
top: '65px',
message: errMessage, message: errMessage,
description: desc, description: desc,
key: jobId, key: jobId,
duration: 0 duration: 0,
onClose: () => {
let countNotify = store.getters.countNotify
countNotify > 0 ? countNotify-- : countNotify = 0
store.commit('SET_COUNT_NOTIFY', countNotify)
}
}) })
} }
store.dispatch('AddHeaderNotice', { store.dispatch('AddHeaderNotice', {
@ -168,10 +177,19 @@ export const pollJobPlugin = {
} }
}).catch(e => { }).catch(e => {
console.error(`${catchMessage} - ${e}`) console.error(`${catchMessage} - ${e}`)
let countNotify = store.getters.countNotify
countNotify++
store.commit('SET_COUNT_NOTIFY', countNotify)
notification.error({ notification.error({
top: '65px',
message: i18n.t('label.error'), message: i18n.t('label.error'),
description: catchMessage, description: catchMessage,
duration: 0 duration: 0,
onClose: () => {
let countNotify = store.getters.countNotify
countNotify > 0 ? countNotify-- : countNotify = 0
store.commit('SET_COUNT_NOTIFY', countNotify)
}
}) })
catchMethod && catchMethod() catchMethod && catchMethod()
}) })
@ -203,12 +221,50 @@ export const notifierPlugin = {
} }
} }
} }
let countNotify = store.getters.countNotify
countNotify++
store.commit('SET_COUNT_NOTIFY', countNotify)
notification.error({ notification.error({
top: '65px',
message: msg, message: msg,
description: desc, description: desc,
duration: 0 duration: 0,
onClose: () => {
let countNotify = store.getters.countNotify
countNotify > 0 ? countNotify-- : countNotify = 0
store.commit('SET_COUNT_NOTIFY', countNotify)
}
}) })
} }
Vue.prototype.$showNotification = function (config) {
let countNotify = store.getters.countNotify
countNotify++
store.commit('SET_COUNT_NOTIFY', countNotify)
const 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
}
}
} }
} }

View File

@ -30,11 +30,23 @@ const service = axios.create({
const err = (error) => { const err = (error) => {
const response = error.response const response = error.response
let countNotify = store.getters.countNotify
if (response) { if (response) {
console.log(response) console.log(response)
if (response.status === 403) { if (response.status === 403) {
const data = response.data const data = response.data
notification.error({ message: i18n.t('label.forbidden'), description: data.message }) countNotify++
store.commit('SET_COUNT_NOTIFY', countNotify)
notification.error({
top: '65px',
message: i18n.t('label.forbidden'),
description: data.message,
onClose: () => {
let countNotify = store.getters.countNotify
countNotify > 0 ? countNotify-- : countNotify = 0
store.commit('SET_COUNT_NOTIFY', countNotify)
}
})
} }
if (response.status === 401) { if (response.status === 401) {
if (response.config && response.config.params && ['listIdps', 'cloudianIsEnabled'].includes(response.config.params.command)) { if (response.config && response.config.params && ['listIdps', 'cloudianIsEnabled'].includes(response.config.params.command)) {
@ -43,20 +55,36 @@ const err = (error) => {
for (const key in response.data) { for (const key in response.data) {
if (key.includes('response')) { if (key.includes('response')) {
if (response.data[key].errortext.includes('not available for user')) { if (response.data[key].errortext.includes('not available for user')) {
countNotify++
store.commit('SET_COUNT_NOTIFY', countNotify)
notification.error({ notification.error({
top: '65px',
message: 'Error', message: 'Error',
description: response.data[key].errortext + ' ' + i18n.t('error.unable.to.proceed'), description: response.data[key].errortext + ' ' + i18n.t('error.unable.to.proceed'),
duration: 0 duration: 0,
onClose: () => {
let countNotify = store.getters.countNotify
countNotify > 0 ? countNotify-- : countNotify = 0
store.commit('SET_COUNT_NOTIFY', countNotify)
}
}) })
return return
} }
} }
} }
countNotify++
store.commit('SET_COUNT_NOTIFY', countNotify)
notification.error({ notification.error({
top: '65px',
message: i18n.t('label.unauthorized'), message: i18n.t('label.unauthorized'),
description: i18n.t('message.authorization.failed'), description: i18n.t('message.authorization.failed'),
key: 'http-401', key: 'http-401',
duration: 0 duration: 0,
onClose: () => {
let countNotify = store.getters.countNotify
countNotify > 0 ? countNotify-- : countNotify = 0
store.commit('SET_COUNT_NOTIFY', countNotify)
}
}) })
store.dispatch('Logout').then(() => { store.dispatch('Logout').then(() => {
if (router.history.current.path !== '/user/login') { if (router.history.current.path !== '/user/login') {
@ -65,15 +93,34 @@ const err = (error) => {
}) })
} }
if (response.status === 404) { if (response.status === 404) {
notification.error({ message: i18n.t('label.not.found'), description: i18n.t('message.resource.not.found') }) countNotify++
store.commit('SET_COUNT_NOTIFY', countNotify)
notification.error({
top: '65px',
message: i18n.t('label.not.found'),
description: i18n.t('message.resource.not.found'),
onClose: () => {
let countNotify = store.getters.countNotify
countNotify > 0 ? countNotify-- : countNotify = 0
store.commit('SET_COUNT_NOTIFY', countNotify)
}
})
router.push({ path: '/exception/404' }) router.push({ path: '/exception/404' })
} }
} }
if (error.isAxiosError && !error.response) { if (error.isAxiosError && !error.response) {
countNotify++
store.commit('SET_COUNT_NOTIFY', countNotify)
notification.warn({ notification.warn({
top: '65px',
message: error.message || i18n.t('message.network.error'), message: error.message || i18n.t('message.network.error'),
description: i18n.t('message.network.error.description'), description: i18n.t('message.network.error.description'),
key: 'network-error' key: 'network-error',
onClose: () => {
let countNotify = store.getters.countNotify
countNotify > 0 ? countNotify-- : countNotify = 0
store.commit('SET_COUNT_NOTIFY', countNotify)
}
}) })
} }
return Promise.reject(error) return Promise.reject(error)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -157,11 +157,11 @@ export default {
(item.iscustomized === true && maxCpuNumber < this.minimumCpunumber))) { (item.iscustomized === true && maxCpuNumber < this.minimumCpunumber))) {
disabled = true disabled = true
} }
if (disabled === false && this.minimumCpuspeed > 0 && maxCpuSpeed && maxCpuSpeed !== this.minimumCpuspeed) { if (disabled === false && this.minimumCpuspeed > 0 && maxCpuSpeed && maxCpuSpeed < this.minimumCpuspeed) {
disabled = true disabled = true
} }
if (disabled === false && maxMemory && this.minimumMemory > 0 && if (disabled === false && maxMemory && this.minimumMemory > 0 &&
((item.iscustomized === false && maxMemory !== this.minimumMemory) || ((item.iscustomized === false && maxMemory < this.minimumMemory) ||
(item.iscustomized === true && maxMemory < this.minimumMemory))) { (item.iscustomized === true && maxMemory < this.minimumMemory))) {
disabled = true disabled = true
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -94,7 +94,30 @@
<a-input :placeholder="placeholder.username" v-model="username"></a-input> <a-input :placeholder="placeholder.username" v-model="username"></a-input>
</div> </div>
<div class="form__item required-field" v-if="selectedClusterHyperVisorType !== 'VMware'"> <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> <div class="form__label"><span class="required">* </span>{{ $t('label.password') }}</div>
<span class="required required-label">{{ $t('label.required') }}</span> <span class="required required-label">{{ $t('label.required') }}</span>
<a-input :placeholder="placeholder.password" type="password" v-model="password"></a-input> <a-input :placeholder="placeholder.password" type="password" v-model="password"></a-input>
@ -190,6 +213,7 @@ export default {
agentusername: null, agentusername: null,
agentpassword: null, agentpassword: null,
agentport: null, agentport: null,
authMethod: 'password',
selectedCluster: null, selectedCluster: null,
selectedClusterHyperVisorType: null, selectedClusterHyperVisorType: null,
showDedicated: false, showDedicated: false,
@ -280,6 +304,9 @@ export default {
this.dedicatedAccount = null this.dedicatedAccount = null
this.showDedicated = !this.showDedicated this.showDedicated = !this.showDedicated
}, },
handleAuthMethodChange (val) {
this.authMethod = val
},
handleSubmitForm () { handleSubmitForm () {
if (this.loading) return if (this.loading) return
const requiredFields = document.querySelectorAll('.required-field') const requiredFields = document.querySelectorAll('.required-field')
@ -306,6 +333,10 @@ export default {
this.url = this.hostname this.url = this.hostname
} }
if (this.authMethod !== 'password') {
this.password = ''
}
const args = { const args = {
zoneid: this.zoneId, zoneid: this.zoneId,
podid: this.podId, podid: this.podId,
@ -331,7 +362,8 @@ export default {
this.parentFetchData() this.parentFetchData()
this.$parent.$parent.close() this.$parent.$parent.close()
}).catch(error => { }).catch(error => {
this.$notification.error({ this.$showNotification({
type: 'error',
message: `${this.$t('label.error')} ${error.response.status}`, message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.addhostresponse.errortext, description: error.response.data.addhostresponse.errortext,
duration: 0 duration: 0
@ -366,7 +398,8 @@ export default {
} }
}) })
}).catch(error => { }).catch(error => {
this.$notification.error({ this.$showNotification({
type: 'error',
message: `${this.$t('label.error')} ${error.response.status}`, message: `${this.$t('label.error')} ${error.response.status}`,
description: error.response.data.errorresponse.errortext, description: error.response.data.errorresponse.errortext,
duration: 0 duration: 0

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -475,6 +475,7 @@ export default {
Object.keys(fields).forEach(key => { Object.keys(fields).forEach(key => {
this.form.getFieldDecorator([key], { initialValue: fields[key] }) this.form.getFieldDecorator([key], { initialValue: fields[key] })
this.form.setFieldsValue({ [key]: fields[key] })
}) })
}, },
deleteTraffic (key, traffic, $event) { deleteTraffic (key, traffic, $event) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -207,7 +207,7 @@
<a-modal <a-modal
:title="$t('label.configure.sticky.policy')" :title="$t('label.configure.sticky.policy')"
v-model="stickinessModalVisible" :visible="stickinessModalVisible"
:footer="null" :footer="null"
:afterClose="closeModal" :afterClose="closeModal"
:maskClosable="false" :maskClosable="false"
@ -288,7 +288,7 @@
<div :span="24" class="action-button"> <div :span="24" class="action-button">
<a-button @click="stickinessModalVisible = false">{{ $t('label.cancel') }}</a-button> <a-button @click="stickinessModalVisible = false">{{ $t('label.cancel') }}</a-button>
<a-button type="primary" @submit="handleSubmitStickinessForm">{{ $t('label.ok') }}</a-button> <a-button type="primary" ref="submit" @click="handleSubmitStickinessForm">{{ $t('label.ok') }}</a-button>
</div> </div>
</a-form> </a-form>
</a-modal> </a-modal>
@ -890,6 +890,8 @@ export default {
}) })
}).catch(error => { }).catch(error => {
this.$notifyError(error) this.$notifyError(error)
}).finally(() => {
this.closeModal()
}) })
}, },
handleDeleteStickinessPolicy () { handleDeleteStickinessPolicy () {
@ -962,6 +964,7 @@ export default {
}) })
}, },
handleStickinessMethodSelectChange (e) { handleStickinessMethodSelectChange (e) {
this.stickinessPolicyForm.resetFields()
this.stickinessPolicyMethod = e this.stickinessPolicyMethod = e
}, },
handleDeleteInstanceFromRule (instance, rule, ip) { handleDeleteInstanceFromRule (instance, rule, ip) {

View File

@ -331,7 +331,7 @@ export default {
inject: ['parentFetchData', 'parentToggleLoading'], inject: ['parentFetchData', 'parentToggleLoading'],
data () { data () {
return { return {
checked: true, checked: false,
selectedRowKeys: [], selectedRowKeys: [],
showGroupActionModal: false, showGroupActionModal: false,
selectedItems: [], selectedItems: [],
@ -639,6 +639,7 @@ export default {
this.addVmModalNicLoading = false this.addVmModalNicLoading = false
this.showConfirmationAction = false this.showConfirmationAction = false
this.nics = [] this.nics = []
this.checked = false
this.resetTagInputs() this.resetTagInputs()
}, },
openTagsModal (id) { openTagsModal (id) {

View File

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

View File

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

View File

@ -89,25 +89,11 @@
</a-row> </a-row>
<a-form-item v-if="guestType === 'isolated'"> <a-form-item v-if="guestType === 'isolated'">
<tooltip-label slot="label" :title="$t('label.vpc')" :tooltip="apiParams.forvpc.description"/> <tooltip-label slot="label" :title="$t('label.vpc')" :tooltip="apiParams.forvpc.description"/>
<a-switch v-decorator="['forvpc', {initialValue: forVpc}]" :defaultChecked="forVpc" @change="val => { handleForVpcChange(val) }" /> <a-switch v-decorator="['forvpc']" :checked="forVpc" @change="val => { handleForVpcChange(val) }" />
</a-form-item> </a-form-item>
<a-form-item :label="$t('label.userdatal2')" v-if="guestType === 'l2'"> <a-form-item :label="$t('label.userdatal2')" v-if="guestType === 'l2'">
<a-switch v-decorator="['userdatal2', {initialValue: false}]" /> <a-switch v-decorator="['userdatal2', {initialValue: false}]" />
</a-form-item> </a-form-item>
<a-form-item :label="$t('label.lbtype')" v-if="forVpc && lbServiceChecked">
<a-radio-group
v-decorator="[' ', {
initialValue: 'publicLb'
}]"
buttonStyle="solid">
<a-radio-button value="publicLb">
{{ $t('label.public.lb') }}
</a-radio-button>
<a-radio-button value="internalLb">
{{ $t('label.internal.lb') }}
</a-radio-button>
</a-radio-group>
</a-form-item>
<a-row :gutter="12"> <a-row :gutter="12">
<a-col :md="12" :lg="12"> <a-col :md="12" :lg="12">
<a-form-item> <a-form-item>
@ -202,15 +188,28 @@
v-decorator="['service.'+item.name, {}]" v-decorator="['service.'+item.name, {}]"
:resourceKey="item.name" :resourceKey="item.name"
:checkBoxLabel="item.description" :checkBoxLabel="item.description"
:checkBoxDecorator="'service.' + item.name" :selectOptions="!supportedServiceLoading ? item.provider: []"
:selectOptions="item.provider"
:selectDecorator="item.name + '.provider'"
@handle-checkselectpair-change="handleSupportedServiceChange"/> @handle-checkselectpair-change="handleSupportedServiceChange"/>
</a-list-item> </a-list-item>
</a-list> </a-list>
</div> </div>
</a-form-item> </a-form-item>
<a-form-item v-if="isVirtualRouterForAtLeastOneService"> <a-form-item :label="$t('label.lbtype')" v-if="forVpc && lbServiceChecked">
<a-radio-group
v-decorator="['lbType', {
initialValue: lbType
}]"
buttonStyle="solid"
@change="e => { handleLbTypeChange(e.target.value) }" >
<a-radio-button value="publicLb">
{{ $t('label.public.lb') }}
</a-radio-button>
<a-radio-button value="internalLb">
{{ $t('label.internal.lb') }}
</a-radio-button>
</a-radio-group>
</a-form-item>
<a-form-item v-if="isVirtualRouterForAtLeastOneService || isVpcVirtualRouterForAtLeastOneService">
<tooltip-label slot="label" :title="$t('label.serviceofferingid')" :tooltip="apiParams.serviceofferingid.description"/> <tooltip-label slot="label" :title="$t('label.serviceofferingid')" :tooltip="apiParams.serviceofferingid.description"/>
<a-select <a-select
v-decorator="['serviceofferingid', { v-decorator="['serviceofferingid', {
@ -453,6 +452,7 @@ export default {
selectedDomains: [], selectedDomains: [],
selectedZones: [], selectedZones: [],
forVpc: false, forVpc: false,
lbType: 'publicLb',
macLearningValue: '', macLearningValue: '',
supportedServices: [], supportedServices: [],
supportedServiceLoading: false, supportedServiceLoading: false,
@ -463,6 +463,8 @@ export default {
sourceNatServiceChecked: false, sourceNatServiceChecked: false,
lbServiceChecked: false, lbServiceChecked: false,
lbServiceProvider: '', lbServiceProvider: '',
registeredServicePackages: [],
registeredServicePackageLoading: false,
isElasticIp: false, isElasticIp: false,
staticNatServiceChecked: false, staticNatServiceChecked: false,
staticNatServiceProvider: '', staticNatServiceProvider: '',
@ -531,6 +533,27 @@ export default {
}, },
handleGuestTypeChange (val) { handleGuestTypeChange (val) {
this.guestType = val this.guestType = val
if (val === 'l2') {
this.forVpc = false
this.lbType = 'publicLb'
this.isVirtualRouterForAtLeastOneService = false
this.isVpcVirtualRouterForAtLeastOneService = false
this.serviceOfferings = []
this.serviceOfferingLoading = false
this.sourceNatServiceChecked = false
this.lbServiceChecked = false
this.lbServiceProvider = ''
this.registeredServicePackages = []
this.registeredServicePackageLoading = false
this.isElasticIp = false
this.staticNatServiceChecked = false
this.staticNatServiceProvider = ''
this.connectivityServiceChecked = false
this.firewallServiceChecked = false
this.firewallServiceProvider = ''
this.selectedServiceProviderMap = {}
this.updateSupportedServices()
}
}, },
fetchSupportedServiceData () { fetchSupportedServiceData () {
const params = {} const params = {}
@ -594,18 +617,21 @@ export default {
this.supportedServices[i].provider = providers this.supportedServices[i].provider = providers
this.supportedServices[i].description = serviceDisplayName this.supportedServices[i].description = serviceDisplayName
} }
}).finally(() => {
this.supportedServiceLoading = false
this.updateSupportedServices()
}) })
}, },
fetchServiceOfferingData () { fetchServiceOfferingData () {
const params = {} const params = {}
params.issystem = true params.issystem = true
params.systemvmtype = 'domainrouter' params.systemvmtype = 'domainrouter'
this.supportedServiceLoading = true this.serviceOfferingLoading = true
api('listServiceOfferings', params).then(json => { api('listServiceOfferings', params).then(json => {
const listServiceOfferings = json.listserviceofferingsresponse.serviceoffering const listServiceOfferings = json.listserviceofferingsresponse.serviceoffering
this.serviceOfferings = this.serviceOfferings.concat(listServiceOfferings) this.serviceOfferings = this.serviceOfferings.concat(listServiceOfferings)
}).finally(() => { }).finally(() => {
this.supportedServiceLoading = false this.serviceOfferingLoading = false
}) })
}, },
fetchRegisteredServicePackageData () { fetchRegisteredServicePackageData () {
@ -627,32 +653,41 @@ export default {
this.registeredServicePackageLoading = false this.registeredServicePackageLoading = false
}) })
}, },
handleForVpcChange (forVpc) { updateSupportedServices () {
this.supportedServiceLoading = true
var supportedServices = this.supportedServices
var self = this var self = this
this.forVpc = forVpc supportedServices.forEach(function (svc, index) {
this.supportedServices.forEach(function (svc, index) { if (svc.name !== 'Connectivity') {
if (svc !== 'Connectivity') {
var providers = svc.provider var providers = svc.provider
providers.forEach(function (provider, providerIndex) { providers.forEach(function (provider, providerIndex) {
if (self.forVpc) { // *** vpc *** if (self.forVpc) { // *** vpc ***
if (provider.name === 'InternalLbVm' || provider.name === 'VpcVirtualRouter' || provider.name === 'Netscaler' || provider.name === 'BigSwitchBcf' || provider.name === 'ConfigDrive') { var enabledProviders = ['VpcVirtualRouter', 'Netscaler', 'BigSwitchBcf', 'ConfigDrive']
provider.enabled = true if (self.lbType === 'internalLb') {
} else { enabledProviders.push('InternalLbVm')
provider.enabled = false
} }
provider.enabled = enabledProviders.includes(provider.name)
} else { // *** non-vpc *** } else { // *** non-vpc ***
if (provider.name === 'InternalLbVm' || provider.name === 'VpcVirtualRouter') { provider.enabled = !['InternalLbVm', 'VpcVirtualRouter'].includes(provider.name)
provider.enabled = false
} else {
provider.enabled = true
}
} }
providers[providerIndex] = provider providers[providerIndex] = provider
}) })
svc.provider = providers svc.provider = providers
self.supportedServices[index] = svc supportedServices[index] = svc
} }
}) })
setTimeout(() => {
self.supportedServices = supportedServices
self.supportedServiceLoading = false
}, 50)
},
handleForVpcChange (forVpc) {
this.forVpc = forVpc
this.updateSupportedServices()
},
handleLbTypeChange (lbType) {
this.lbType = lbType
this.updateSupportedServices()
}, },
handleSupportedServiceChange (service, checked, provider) { handleSupportedServiceChange (service, checked, provider) {
if (service === 'SourceNat') { if (service === 'SourceNat') {
@ -696,13 +731,14 @@ export default {
providers.forEach(function (prvdr, idx) { providers.forEach(function (prvdr, idx) {
if (prvdr === 'VirtualRouter') { if (prvdr === 'VirtualRouter') {
self.isVirtualRouterForAtLeastOneService = true self.isVirtualRouterForAtLeastOneService = true
if (self.serviceOfferings.length === 0) {
self.fetchServiceOfferingData()
}
} }
if (prvdr === 'VpcVirtualRouter') { if (prvdr === 'VpcVirtualRouter') {
self.isVpcVirtualRouterForAtLeastOneService = true self.isVpcVirtualRouterForAtLeastOneService = true
} }
if ((self.isVirtualRouterForAtLeastOneService || self.isVpcVirtualRouterForAtLeastOneService) &&
self.serviceOfferings.length === 0) {
self.fetchServiceOfferingData()
}
}) })
}, },
handleSubmit (e) { handleSubmit (e) {
@ -724,7 +760,7 @@ export default {
var selectedServices = null var selectedServices = null
var keys = Object.keys(values) var keys = Object.keys(values)
const detailsKey = ['promiscuousmode', 'macaddresschanges', 'forgedtransmits', 'maclearning'] const detailsKey = ['promiscuousmode', 'macaddresschanges', 'forgedtransmits', 'maclearning']
const ignoredKeys = [...detailsKey, 'state', 'status', 'allocationstate', 'forvpc', 'specifyvlan', 'ispublic', 'domainid', 'zoneid', 'egressdefaultpolicy', 'isolation', 'supportspublicaccess'] const ignoredKeys = [...detailsKey, 'state', 'status', 'allocationstate', 'forvpc', 'lbType', 'specifyvlan', 'ispublic', 'domainid', 'zoneid', 'egressdefaultpolicy', 'isolation', 'supportspublicaccess']
keys.forEach(function (key, keyIndex) { keys.forEach(function (key, keyIndex) {
if (self.isSupportedServiceObject(values[key])) { if (self.isSupportedServiceObject(values[key])) {
if (selectedServices == null) { if (selectedServices == null) {

View File

@ -48,9 +48,7 @@
v-decorator="['service.'+item.name, {}]" v-decorator="['service.'+item.name, {}]"
:resourceKey="item.name" :resourceKey="item.name"
:checkBoxLabel="item.description" :checkBoxLabel="item.description"
:checkBoxDecorator="'service.' + item.name"
:selectOptions="item.provider" :selectOptions="item.provider"
:selectDecorator="item.name + '.provider'"
@handle-checkselectpair-change="handleSupportedServiceChange"/> @handle-checkselectpair-change="handleSupportedServiceChange"/>
</a-list-item> </a-list-item>
</a-list> </a-list>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -52,21 +52,14 @@ mocks = {
$notifyError: jest.fn((error) => { $notifyError: jest.fn((error) => {
return error return error
}), }),
$notification: { $showNotification: jest.fn((option) => {
info: jest.fn((option) => {
return { return {
type: option.type,
message: option.message, message: option.message,
description: 'test-description', description: 'test-description',
duration: option.duration duration: option.duration
} }
}), }),
success: jest.fn((option) => {
return {
message: option.message,
description: option.description
}
})
},
$message: { $message: {
success: jest.fn((obj) => { success: jest.fn((obj) => {
return obj return obj
@ -1577,7 +1570,7 @@ describe('Views > AutogenView.vue', () => {
}) })
describe('pollActionCompletion()', () => { describe('pollActionCompletion()', () => {
it('check $notification when pollActionCompletion() is called with action is empty', (done) => { it('check $showNotification when pollActionCompletion() is called with action is empty', (done) => {
const mockData = { const mockData = {
queryasyncjobresultresponse: { queryasyncjobresultresponse: {
jobstatus: 1, jobstatus: 1,
@ -1595,7 +1588,7 @@ describe('Views > AutogenView.vue', () => {
wrapper.vm.pollActionCompletion(jobId, action) wrapper.vm.pollActionCompletion(jobId, action)
setTimeout(() => { setTimeout(() => {
expect(mocks.$notification.info).not.toHaveBeenCalled() expect(mocks.$showNotification).not.toHaveBeenCalled()
expect(mockAxios).toHaveBeenCalled() expect(mockAxios).toHaveBeenCalled()
expect(mockAxios).toHaveBeenCalledWith({ expect(mockAxios).toHaveBeenCalledWith({
url: '/', url: '/',
@ -1612,7 +1605,7 @@ describe('Views > AutogenView.vue', () => {
}) })
}) })
it('check $notification when pollActionCompletion() is called with action is not empty', (done) => { it('check $showNotification when pollActionCompletion() is called with action is not empty', (done) => {
const mockData = { const mockData = {
queryasyncjobresultresponse: { queryasyncjobresultresponse: {
jobstatus: 1, jobstatus: 1,
@ -1633,8 +1626,9 @@ describe('Views > AutogenView.vue', () => {
wrapper.vm.pollActionCompletion(jobId, action) wrapper.vm.pollActionCompletion(jobId, action)
setTimeout(() => { setTimeout(() => {
expect(mocks.$notification.info).toHaveBeenCalled() expect(mocks.$showNotification).toHaveBeenCalled()
expect(mocks.$notification.info).toHaveLastReturnedWith({ expect(mocks.$showNotification).toHaveLastReturnedWith({
type: 'info',
message: 'test-name-en', message: 'test-name-en',
description: 'test-description', description: 'test-description',
duration: 0 duration: 0
@ -2787,7 +2781,7 @@ describe('Views > AutogenView.vue', () => {
}) })
}) })
it('check $notification when api is called and response have not jobId result', async (done) => { it('check $message when api is called and response have not jobId result', async (done) => {
wrapper = factory({ wrapper = factory({
data: { data: {
showAction: true, showAction: true,

Some files were not shown because too many files have changed in this diff Show More