From d4d91f355dbc6b656f28dc0f20c0e687028bb568 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Mon, 12 Nov 2018 11:32:36 +0530 Subject: [PATCH] vmware: updateVmwareDc API for updating vmware datacenter details (#2919) This adds a new API updateVmwareDc that allows admins to update the VMware datacenter details of a zone. It also recursively updates the cluster_details for any username/password updates as well as updates the url detail in cluster_details table and guid detail in the host_details table with any newly provided vcenter domain/ip. The update API assumes that there is only one vCenter per zone. And, since the username/password for each VMware host could be different than what gets configured for vcenter at zone level, it does not update the username/password in host_details. Previously, one has to manually update the db with any new vcenter details for the zone. Signed-off-by: Rohit Yadav --- .../main/java/com/cloud/host/dao/HostDao.java | 2 + .../java/com/cloud/host/dao/HostDaoImpl.java | 11 + .../vmware/VmwareDatacenterService.java | 9 +- .../vmware/manager/VmwareManagerImpl.java | 201 ++++++++++++------ .../command/admin/zone/UpdateVmwareDcCmd.java | 131 ++++++++++++ .../vmware/VmwareDatacenterApiUnitTest.java | 6 + .../vmware/manager/VmwareManagerImplTest.java | 118 ++++++++++ ui/css/cloudstack3.css | 8 + ui/l10n/en.js | 1 + ui/scripts/system.js | 61 +++++- 10 files changed, 482 insertions(+), 66 deletions(-) create mode 100644 plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/UpdateVmwareDcCmd.java create mode 100644 plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImplTest.java diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java b/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java index 2de3fcd17ad..1fca86ca319 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java @@ -85,6 +85,8 @@ public interface HostDao extends GenericDao, StateDao listAllHosts(long zoneId); + List listAllHostsByZoneAndHypervisorType(long zoneId, HypervisorType hypervisorType); + List listAllHostsByType(Host.Type type); HostVO findByPublicIp(String publicIp); diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java b/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java index 6eec7baa5ad..8c8c082ed8f 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java @@ -229,6 +229,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao DcSearch = createSearchBuilder(); DcSearch.and("dc", DcSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); + DcSearch.and("hypervisorType", DcSearch.entity().getHypervisorType(), Op.EQ); DcSearch.and("type", DcSearch.entity().getType(), Op.EQ); DcSearch.and("status", DcSearch.entity().getStatus(), Op.EQ); DcSearch.and("resourceState", DcSearch.entity().getResourceState(), Op.EQ); @@ -1116,6 +1117,16 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao return customSearch(sc, null); } + @Override + public List listAllHostsByZoneAndHypervisorType(long zoneId, HypervisorType hypervisorType) { + SearchCriteria sc = DcSearch.create(); + sc.setParameters("dc", zoneId); + if (hypervisorType != null) { + sc.setParameters("hypervisorType", hypervisorType.toString()); + } + return listBy(sc); + } + @Override public List listClustersByHostTag(String hostTagOnOffering) { TransactionLegacy txn = TransactionLegacy.currentTxn(); diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/VmwareDatacenterService.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/VmwareDatacenterService.java index d74c12347fe..53792539ee8 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/VmwareDatacenterService.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/VmwareDatacenterService.java @@ -22,6 +22,7 @@ import java.util.List; import org.apache.cloudstack.api.command.admin.zone.AddVmwareDcCmd; import org.apache.cloudstack.api.command.admin.zone.ListVmwareDcsCmd; import org.apache.cloudstack.api.command.admin.zone.RemoveVmwareDcCmd; +import org.apache.cloudstack.api.command.admin.zone.UpdateVmwareDcCmd; import com.cloud.exception.DiscoveryException; import com.cloud.exception.ResourceInUseException; @@ -30,9 +31,11 @@ import com.cloud.utils.exception.CloudRuntimeException; public interface VmwareDatacenterService extends PluggableService { - public VmwareDatacenterVO addVmwareDatacenter(AddVmwareDcCmd cmd) throws IllegalArgumentException, DiscoveryException, ResourceInUseException; + VmwareDatacenterVO addVmwareDatacenter(AddVmwareDcCmd cmd) throws IllegalArgumentException, DiscoveryException, ResourceInUseException; - public boolean removeVmwareDatacenter(RemoveVmwareDcCmd cmd) throws IllegalArgumentException, ResourceInUseException; + VmwareDatacenter updateVmwareDatacenter(UpdateVmwareDcCmd updateVmwareDcCmd); - public List listVmwareDatacenters(ListVmwareDcsCmd cmd) throws IllegalArgumentException, CloudRuntimeException; + boolean removeVmwareDatacenter(RemoveVmwareDcCmd cmd) throws IllegalArgumentException, ResourceInUseException; + + List listVmwareDatacenters(ListVmwareDcsCmd cmd) throws IllegalArgumentException, CloudRuntimeException; } diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java index f586f393821..643d3cebb7c 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java @@ -16,6 +16,42 @@ // under the License. package com.cloud.hypervisor.vmware.manager; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.rmi.RemoteException; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import org.apache.cloudstack.api.command.admin.zone.AddVmwareDcCmd; +import org.apache.cloudstack.api.command.admin.zone.ListVmwareDcsCmd; +import org.apache.cloudstack.api.command.admin.zone.RemoveVmwareDcCmd; +import org.apache.cloudstack.api.command.admin.zone.UpdateVmwareDcCmd; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.jobs.impl.AsyncJobManagerImpl; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.utils.identity.ManagementServerNode; +import org.apache.log4j.Logger; + import com.cloud.agent.AgentManager; import com.cloud.agent.Listener; import com.cloud.agent.api.AgentControlAnswer; @@ -36,12 +72,17 @@ import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.ClusterVSMMapDao; import com.cloud.dc.dao.DataCenterDao; +import com.cloud.event.ActionEvent; +import com.cloud.event.EventTypes; import com.cloud.exception.DiscoveredWithErrorException; import com.cloud.exception.DiscoveryException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceInUseException; import com.cloud.host.Host; import com.cloud.host.Status; +import com.cloud.host.dao.HostDao; +import com.cloud.host.dao.HostDetailsDao; +import com.cloud.hypervisor.Hypervisor; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao; import com.cloud.hypervisor.vmware.LegacyZoneVO; @@ -49,6 +90,7 @@ import com.cloud.hypervisor.vmware.VmwareCleanupMaid; import com.cloud.hypervisor.vmware.VmwareDatacenter; import com.cloud.hypervisor.vmware.VmwareDatacenterService; import com.cloud.hypervisor.vmware.VmwareDatacenterVO; +import com.cloud.hypervisor.vmware.VmwareDatacenterZoneMap; import com.cloud.hypervisor.vmware.VmwareDatacenterZoneMapVO; import com.cloud.hypervisor.vmware.dao.LegacyZoneDao; import com.cloud.hypervisor.vmware.dao.VmwareDatacenterDao; @@ -71,6 +113,7 @@ import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.TrafficType; import com.cloud.network.VmwareTrafficLabel; import com.cloud.network.dao.CiscoNexusVSMDeviceDao; +import com.cloud.org.Cluster; import com.cloud.org.Cluster.ClusterType; import com.cloud.secstorage.CommandExecLogDao; import com.cloud.server.ConfigurationServer; @@ -88,6 +131,7 @@ import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; import com.cloud.utils.db.GlobalLock; import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; import com.cloud.utils.db.TransactionCallbackNoReturn; import com.cloud.utils.db.TransactionStatus; import com.cloud.utils.exception.CloudRuntimeException; @@ -96,41 +140,9 @@ import com.cloud.utils.ssh.SshHelper; import com.cloud.vm.DomainRouterVO; import com.cloud.vm.dao.UserVmCloneSettingDao; import com.cloud.vm.dao.VMInstanceDao; +import com.google.common.base.Strings; import com.vmware.vim25.AboutInfo; import com.vmware.vim25.ManagedObjectReference; -import org.apache.cloudstack.api.command.admin.zone.AddVmwareDcCmd; -import org.apache.cloudstack.api.command.admin.zone.ListVmwareDcsCmd; -import org.apache.cloudstack.api.command.admin.zone.RemoveVmwareDcCmd; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; -import org.apache.cloudstack.framework.config.ConfigKey; -import org.apache.cloudstack.framework.config.Configurable; -import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.framework.jobs.impl.AsyncJobManagerImpl; -import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; -import org.apache.cloudstack.utils.identity.ManagementServerNode; -import org.apache.log4j.Logger; - -import javax.inject.Inject; -import javax.naming.ConfigurationException; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.rmi.RemoteException; -import java.time.Duration; -import java.time.Instant; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.UUID; -import java.util.concurrent.Executors; -import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; public class VmwareManagerImpl extends ManagerBase implements VmwareManager, VmwareStorageMount, Listener, VmwareDatacenterService, Configurable { private static final Logger s_logger = Logger.getLogger(VmwareManagerImpl.class); @@ -148,9 +160,13 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw @Inject private NetworkModel _netMgr; @Inject - private ClusterDao _clusterDao; + private ClusterDao clusterDao; @Inject - private ClusterDetailsDao _clusterDetailsDao; + private ClusterDetailsDao clusterDetailsDao; + @Inject + private HostDao hostDao; + @Inject + private HostDetailsDao hostDetailsDao; @Inject private CommandExecLogDao _cmdExecLogDao; @Inject @@ -166,17 +182,17 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw @Inject private HypervisorCapabilitiesDao _hvCapabilitiesDao; @Inject - private DataCenterDao _dcDao; + private DataCenterDao datacenterDao; @Inject - private VmwareDatacenterDao _vmwareDcDao; + private VmwareDatacenterDao vmwareDcDao; @Inject - private VmwareDatacenterZoneMapDao _vmwareDcZoneMapDao; + private VmwareDatacenterZoneMapDao vmwareDatacenterZoneMapDao; @Inject - private LegacyZoneDao _legacyZoneDao; + private LegacyZoneDao legacyZoneDao; @Inject - private ManagementServerHostPeerDao _mshostPeerDao; + private ManagementServerHostPeerDao msHostPeerDao; @Inject - private ClusterManager _clusterMgr; + private ClusterManager clusterManager; @Inject private ImageStoreDetailsUtil imageStoreDetailsUtil; @Inject @@ -557,13 +573,13 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw long msid = Long.parseLong(tokens[1]); long runid = Long.parseLong(tokens[2]); - if (_mshostPeerDao.countStateSeenInPeers(msid, runid, ManagementServerHost.State.Down) > 0) { + if (msHostPeerDao.countStateSeenInPeers(msid, runid, ManagementServerHost.State.Down) > 0) { if (s_logger.isInfoEnabled()) s_logger.info("Worker VM's owner management server node has been detected down from peer nodes, recycle it"); return true; } - if (runid != _clusterMgr.getManagementRunId(msid)) { + if (runid != clusterManager.getManagementRunId(msid)) { if (s_logger.isInfoEnabled()) s_logger.info("Worker VM's owner management server has changed runid, recycle it"); return true; @@ -819,16 +835,16 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw @DB private void updateClusterNativeHAState(Host host, StartupCommand cmd) { - ClusterVO cluster = _clusterDao.findById(host.getClusterId()); + ClusterVO cluster = clusterDao.findById(host.getClusterId()); if (cluster.getClusterType() == ClusterType.ExternalManaged) { if (cmd instanceof StartupRoutingCommand) { StartupRoutingCommand hostStartupCmd = (StartupRoutingCommand)cmd; Map details = hostStartupCmd.getHostDetails(); if (details.get("NativeHA") != null && details.get("NativeHA").equalsIgnoreCase("true")) { - _clusterDetailsDao.persist(host.getClusterId(), "NativeHA", "true"); + clusterDetailsDao.persist(host.getClusterId(), "NativeHA", "true"); } else { - _clusterDetailsDao.persist(host.getClusterId(), "NativeHA", "false"); + clusterDetailsDao.persist(host.getClusterId(), "NativeHA", "false"); } } } @@ -1000,6 +1016,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw public List> getCommands() { List> cmdList = new ArrayList>(); cmdList.add(AddVmwareDcCmd.class); + cmdList.add(UpdateVmwareDcCmd.class); cmdList.add(RemoveVmwareDcCmd.class); cmdList.add(ListVmwareDcsCmd.class); return cmdList; @@ -1040,14 +1057,14 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw // Zone validation validateZone(zoneId); - VmwareDatacenterZoneMapVO vmwareDcZoneMap = _vmwareDcZoneMapDao.findByZoneId(zoneId); + VmwareDatacenterZoneMapVO vmwareDcZoneMap = vmwareDatacenterZoneMapDao.findByZoneId(zoneId); // Check if zone is associated with VMware DC if (vmwareDcZoneMap != null) { // Check if the associated VMware DC matches the one specified in API params // This check would yield success as the association exists between same entities (zone and VMware DC) // This scenario would result in if the API addVmwareDc is called more than once with same parameters. Long associatedVmwareDcId = vmwareDcZoneMap.getVmwareDcId(); - VmwareDatacenterVO associatedVmwareDc = _vmwareDcDao.findById(associatedVmwareDcId); + VmwareDatacenterVO associatedVmwareDc = vmwareDcDao.findById(associatedVmwareDcId); if (associatedVmwareDc.getVcenterHost().equalsIgnoreCase(vCenterHost) && associatedVmwareDc.getVmwareDatacenterName().equalsIgnoreCase(vmwareDcName)) { s_logger.info("Ignoring API call addVmwareDc, because VMware DC " + vCenterHost + "/" + vmwareDcName + " is already associated with specified zone with id " + zoneId); @@ -1063,7 +1080,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw // Check if DC is already part of zone // In that case vmware_data_center table should have the DC - vmwareDc = _vmwareDcDao.getVmwareDatacenterByGuid(vmwareDcName + "@" + vCenterHost); + vmwareDc = vmwareDcDao.getVmwareDatacenterByGuid(vmwareDcName + "@" + vCenterHost); if (vmwareDc != null) { throw new ResourceInUseException("This DC is already part of other CloudStack zone(s). Cannot add this DC to more zones."); } @@ -1102,12 +1119,12 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw // Add DC to database into vmware_data_center table vmwareDc = new VmwareDatacenterVO(guid, vmwareDcName, vCenterHost, userName, password); - vmwareDc = _vmwareDcDao.persist(vmwareDc); + vmwareDc = vmwareDcDao.persist(vmwareDc); // Map zone with vmware datacenter vmwareDcZoneMap = new VmwareDatacenterZoneMapVO(zoneId, vmwareDc.getId()); - vmwareDcZoneMap = _vmwareDcZoneMapDao.persist(vmwareDcZoneMap); + vmwareDcZoneMap = vmwareDatacenterZoneMapDao.persist(vmwareDcZoneMap); // Set custom field for this DC if (addDcCustomFieldDef) { @@ -1132,6 +1149,70 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw return vmwareDc; } + @Override + @ActionEvent(eventType = EventTypes.EVENT_ZONE_EDIT, eventDescription = "updating VMware datacenter") + public VmwareDatacenter updateVmwareDatacenter(UpdateVmwareDcCmd cmd) { + final Long zoneId = cmd.getZoneId(); + final String userName = cmd.getUsername(); + final String password = cmd.getPassword(); + final String vCenterHost = cmd.getVcenter(); + final String vmwareDcName = cmd.getName(); + final Boolean isRecursive = cmd.isRecursive(); + + final VmwareDatacenterZoneMap vdcMap = vmwareDatacenterZoneMapDao.findByZoneId(zoneId); + final VmwareDatacenterVO vmwareDc = vmwareDcDao.findById(vdcMap.getVmwareDcId()); + if (vmwareDc == null) { + throw new CloudRuntimeException("VMWare datacenter does not exist by provided ID"); + } + final String oldVCenterHost = vmwareDc.getVcenterHost(); + + if (!Strings.isNullOrEmpty(userName)) { + vmwareDc.setUser(userName); + } + if (!Strings.isNullOrEmpty(password)) { + vmwareDc.setPassword(password); + } + if (!Strings.isNullOrEmpty(vCenterHost)) { + vmwareDc.setVcenterHost(vCenterHost); + } + if (!Strings.isNullOrEmpty(vmwareDcName)) { + vmwareDc.setVmwareDatacenterName(vmwareDcName); + } + vmwareDc.setGuid(String.format("%s@%s", vmwareDc.getVmwareDatacenterName(), vmwareDc.getVcenterHost())); + + return Transaction.execute(new TransactionCallback() { + @Override + public VmwareDatacenter doInTransaction(TransactionStatus status) { + if (vmwareDcDao.update(vmwareDc.getId(), vmwareDc)) { + if (isRecursive) { + for (final Cluster cluster : clusterDao.listByDcHyType(zoneId, Hypervisor.HypervisorType.VMware.toString())) { + final Map clusterDetails = clusterDetailsDao.findDetails(cluster.getId()); + clusterDetails.put("username", vmwareDc.getUser()); + clusterDetails.put("password", vmwareDc.getPassword()); + final String clusterUrl = clusterDetails.get("url"); + if (!oldVCenterHost.equals(vmwareDc.getVcenterHost()) && !Strings.isNullOrEmpty(clusterUrl)) { + clusterDetails.put("url", clusterUrl.replace(oldVCenterHost, vmwareDc.getVcenterHost())); + } + clusterDetailsDao.persist(cluster.getId(), clusterDetails); + } + for (final Host host : hostDao.listAllHostsByZoneAndHypervisorType(zoneId, HypervisorType.VMware)) { + final Map hostDetails = hostDetailsDao.findDetails(host.getId()); + hostDetails.put("username", vmwareDc.getUser()); + hostDetails.put("password", vmwareDc.getPassword()); + final String hostGuid = hostDetails.get("guid"); + if (!Strings.isNullOrEmpty(hostGuid)) { + hostDetails.put("guid", hostGuid.replace(oldVCenterHost, vmwareDc.getVcenterHost())); + } + hostDetailsDao.persist(host.getId(), hostDetails); + } + } + return vmwareDc; + } + return null; + } + }); + } + @Override public boolean removeVmwareDatacenter(RemoveVmwareDcCmd cmd) throws ResourceInUseException { Long zoneId = cmd.getZoneId(); @@ -1148,14 +1229,14 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw String userName; String password; DatacenterMO dcMo = null; - final VmwareDatacenterZoneMapVO vmwareDcZoneMap = _vmwareDcZoneMapDao.findByZoneId(zoneId); + final VmwareDatacenterZoneMapVO vmwareDcZoneMap = vmwareDatacenterZoneMapDao.findByZoneId(zoneId); // Check if zone is associated with VMware DC if (vmwareDcZoneMap == null) { throw new CloudRuntimeException("Zone " + zoneId + " is not associated with any VMware datacenter."); } final long vmwareDcId = vmwareDcZoneMap.getVmwareDcId(); - vmwareDatacenter = _vmwareDcDao.findById(vmwareDcId); + vmwareDatacenter = vmwareDcDao.findById(vmwareDcId); vmwareDcName = vmwareDatacenter.getVmwareDatacenterName(); vCenterHost = vmwareDatacenter.getVcenterHost(); userName = vmwareDatacenter.getUser(); @@ -1164,9 +1245,9 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw @Override public void doInTransactionWithoutResult(TransactionStatus status) { // Remove the VMware datacenter entry in table vmware_data_center - _vmwareDcDao.remove(vmwareDcId); + vmwareDcDao.remove(vmwareDcId); // Remove the map entry in table vmware_data_center_zone_map - _vmwareDcZoneMapDao.remove(vmwareDcZoneMap.getId()); + vmwareDatacenterZoneMapDao.remove(vmwareDcZoneMap.getId()); } }); @@ -1217,7 +1298,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw private void validateZoneWithResources(Long zoneId, String errStr) throws ResourceInUseException { // Check if zone has resources? - For now look for clusters - List clusters = _clusterDao.listByZoneId(zoneId); + List clusters = clusterDao.listByZoneId(zoneId); if (clusters != null && clusters.size() > 0) { // Look for VMware hypervisor. for (ClusterVO cluster : clusters) { @@ -1231,7 +1312,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw @Override public boolean isLegacyZone(long dcId) { boolean isLegacyZone = false; - LegacyZoneVO legacyZoneVo = _legacyZoneDao.findByZoneId(dcId); + LegacyZoneVO legacyZoneVo = legacyZoneDao.findByZoneId(dcId); if (legacyZoneVo != null) { isLegacyZone = true; } @@ -1250,13 +1331,13 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw doesZoneExist(zoneId); // Check if zone is associated with VMware DC - vmwareDcZoneMap = _vmwareDcZoneMapDao.findByZoneId(zoneId); + vmwareDcZoneMap = vmwareDatacenterZoneMapDao.findByZoneId(zoneId); if (vmwareDcZoneMap == null) { return null; } // Retrieve details of VMware DC associated with zone. vmwareDcId = vmwareDcZoneMap.getVmwareDcId(); - vmwareDatacenter = _vmwareDcDao.findById(vmwareDcId); + vmwareDatacenter = vmwareDcDao.findById(vmwareDcId); vmwareDcList.add(vmwareDatacenter); // Currently a zone can have only 1 VMware DC associated with. @@ -1266,7 +1347,7 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw private void doesZoneExist(Long zoneId) throws InvalidParameterValueException { // Check if zone with specified id exists - DataCenterVO zone = _dcDao.findById(zoneId); + DataCenterVO zone = datacenterDao.findById(zoneId); if (zone == null) { throw new InvalidParameterValueException("Can't find zone by the id specified."); } diff --git a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/UpdateVmwareDcCmd.java b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/UpdateVmwareDcCmd.java new file mode 100644 index 00000000000..e428947a43c --- /dev/null +++ b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/api/command/admin/zone/UpdateVmwareDcCmd.java @@ -0,0 +1,131 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.api.command.admin.zone; + +import javax.inject.Inject; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.VmwareDatacenterResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.log4j.Logger; + +import com.cloud.hypervisor.vmware.VmwareDatacenter; +import com.cloud.hypervisor.vmware.VmwareDatacenterService; +import com.cloud.user.Account; + +@APICommand(name = UpdateVmwareDcCmd.APINAME, description = "Updates a VMware datacenter details for a zone", + responseObject = VmwareDatacenterResponse.class, responseHasSensitiveInfo = false, + since = "4.12.0", authorized = {RoleType.Admin}) +public class UpdateVmwareDcCmd extends BaseCmd { + public static final Logger LOG = Logger.getLogger(UpdateVmwareDcCmd.class); + + static final String APINAME = "updateVmwareDc"; + + @Inject + public VmwareDatacenterService vmwareDatacenterService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, + entityType = ZoneResponse.class, required = true, description = "The zone ID") + private Long zoneId; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, + description = "VMware datacenter name.") + private String name; + + @Parameter(name = ApiConstants.VCENTER, type = CommandType.STRING, + description = "The name/IP of vCenter. Make sure it is IP address or full qualified domain name for host running vCenter server.") + private String vCenter; + + @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, + description = "The username required to connect to resource.") + private String username; + + @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, + description = "The password for specified username.") + private String password; + + @Parameter(name = ApiConstants.IS_RECURSIVE, type = CommandType.BOOLEAN, + description = "Specify if cluster level username/password/url and host level guid need to be updated as well. By default this is true.") + private Boolean recursive = true; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public long getZoneId() { + return zoneId; + } + + public String getName() { + return name; + } + + public String getVcenter() { + return vCenter; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public Boolean isRecursive() { + return recursive; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() { + final VmwareDatacenter vmwareDatacenter = vmwareDatacenterService.updateVmwareDatacenter(this); + if (vmwareDatacenter == null) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update VMware datacenter"); + } + final VmwareDatacenterResponse response = new VmwareDatacenterResponse(); + response.setId(vmwareDatacenter.getUuid()); + response.setName(vmwareDatacenter.getVmwareDatacenterName()); + response.setResponseName(getCommandName()); + response.setObjectName("vmwaredc"); + setResponseObject(response); + } + + @Override + public String getCommandName() { + return APINAME.toLowerCase() + BaseCmd.RESPONSE_SUFFIX; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } +} \ No newline at end of file diff --git a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/VmwareDatacenterApiUnitTest.java b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/VmwareDatacenterApiUnitTest.java index e835a0b52af..eb041396459 100644 --- a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/VmwareDatacenterApiUnitTest.java +++ b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/VmwareDatacenterApiUnitTest.java @@ -36,6 +36,7 @@ import com.cloud.exception.DiscoveryException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceInUseException; import com.cloud.host.dao.HostDao; +import com.cloud.host.dao.HostDetailsDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao; import com.cloud.hypervisor.vmware.dao.LegacyZoneDao; @@ -356,6 +357,11 @@ public class VmwareDatacenterApiUnitTest { return Mockito.mock(HostDao.class); } + @Bean + public HostDetailsDao hostDetailsDao() { + return Mockito.mock(HostDetailsDao.class); + } + @Bean public NetworkModel networkModel() { return Mockito.mock(NetworkModel.class); diff --git a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImplTest.java b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImplTest.java new file mode 100644 index 00000000000..499ed246028 --- /dev/null +++ b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImplTest.java @@ -0,0 +1,118 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package com.cloud.hypervisor.vmware.manager; + +import java.util.Collections; +import java.util.Map; + +import org.apache.cloudstack.api.command.admin.zone.UpdateVmwareDcCmd; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.runners.MockitoJUnitRunner; + +import com.cloud.dc.ClusterDetailsDao; +import com.cloud.dc.ClusterVO; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.host.dao.HostDetailsDao; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.hypervisor.vmware.VmwareDatacenter; +import com.cloud.hypervisor.vmware.VmwareDatacenterVO; +import com.cloud.hypervisor.vmware.VmwareDatacenterZoneMapVO; +import com.cloud.hypervisor.vmware.dao.VmwareDatacenterDao; +import com.cloud.hypervisor.vmware.dao.VmwareDatacenterZoneMapDao; + +@RunWith(MockitoJUnitRunner.class) +public class VmwareManagerImplTest { + + @Spy + @InjectMocks + private VmwareManagerImpl vmwareManager; + + @Mock + private UpdateVmwareDcCmd updateVmwareDcCmd; + @Mock + private VmwareDatacenterDao vmwareDcDao; + @Mock + private VmwareDatacenterZoneMapDao vmwareDatacenterZoneMapDao; + @Mock + private ClusterDao clusterDao; + @Mock + private ClusterDetailsDao clusterDetailsDao; + @Mock + private HostDao hostDao; + @Mock + private HostDetailsDao hostDetailsDao; + @Mock + private Map clusterDetails; + @Mock + private Map hostDetails; + + @Before + public void beforeTest() { + VmwareDatacenterZoneMapVO vmwareDatacenterZoneMap = new VmwareDatacenterZoneMapVO(); + vmwareDatacenterZoneMap.setZoneId(1); + vmwareDatacenterZoneMap.setVmwareDcId(1); + VmwareDatacenterVO vmwareDatacenterVO = new VmwareDatacenterVO(1, "some-guid", "some-name", "10.1.1.1", "username", "password"); + + Mockito.doReturn(vmwareDatacenterZoneMap).when(vmwareDatacenterZoneMapDao).findByZoneId(Mockito.anyLong()); + Mockito.doReturn(vmwareDatacenterVO).when(vmwareDcDao).findById(Mockito.anyLong()); + Mockito.doReturn(1L).when(updateVmwareDcCmd).getZoneId(); + } + + @Test + public void updateVmwareDatacenterNoUpdate() { + VmwareDatacenter vmwareDatacenter = vmwareManager.updateVmwareDatacenter(updateVmwareDcCmd); + Assert.assertNull(vmwareDatacenter); + } + + @Test + public void updateVmwareDatacenterNormalUpdate() { + Mockito.doReturn("some-new-username").when(updateVmwareDcCmd).getUsername(); + Mockito.doReturn("some-new-password").when(updateVmwareDcCmd).getPassword(); + Mockito.doReturn("some-new-vcenter-address").when(updateVmwareDcCmd).getVcenter(); + Mockito.doReturn(true).when(updateVmwareDcCmd).isRecursive(); + Mockito.doReturn(true).when(vmwareDcDao).update(Mockito.anyLong(), Mockito.any(VmwareDatacenterVO.class)); + Mockito.doReturn(Collections.singletonList(new ClusterVO(1, 1, "some-cluster"))).when(clusterDao).listByDcHyType(Mockito.anyLong(), Mockito.anyString()); + Mockito.doReturn(clusterDetails).when(clusterDetailsDao).findDetails(Mockito.anyLong()); + + final HostVO host = new HostVO("someGuid"); + host.setDataCenterId(1); + host.setHypervisorType(Hypervisor.HypervisorType.VMware); + Mockito.doReturn(Collections.singletonList(host)).when(hostDao).listAllHostsByZoneAndHypervisorType(Mockito.anyLong(), Mockito.any()); + Mockito.doReturn(hostDetails).when(hostDetailsDao).findDetails(Mockito.anyLong()); + Mockito.doReturn("some-old-guid").when(hostDetails).get("guid"); + Mockito.doReturn(hostDetails).when(hostDetailsDao).findDetails(Mockito.anyLong()); + + final VmwareDatacenter vmwareDatacenter = vmwareManager.updateVmwareDatacenter(updateVmwareDcCmd); + + Assert.assertEquals(vmwareDatacenter.getUser(), updateVmwareDcCmd.getUsername()); + Assert.assertEquals(vmwareDatacenter.getPassword(), updateVmwareDcCmd.getPassword()); + Mockito.verify(clusterDetails, Mockito.times(2)).put(Mockito.anyString(), Mockito.anyString()); + Mockito.verify(clusterDetailsDao, Mockito.times(1)).persist(Mockito.anyLong(), Mockito.anyMapOf(String.class, String.class)); + Mockito.verify(hostDetails, Mockito.times(3)).put(Mockito.anyString(), Mockito.anyString()); + Mockito.verify(hostDetailsDao, Mockito.times(1)).persist(Mockito.anyLong(), Mockito.anyMapOf(String.class, String.class)); + } +} \ No newline at end of file diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 9eea2e590d4..59e6235b7bb 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -12566,6 +12566,14 @@ div.ui-dialog div.autoscaler div.field-group div.form-container form div.form-it background-position: -169px -583px; } +.updateVmwareDc .icon { + background-position: -265px -148px; +} + +.updateVmwareDc:hover .icon { + background-position: -265px -728px; +} + .stop .icon, .removeVmwareDc .icon, .release .icon { diff --git a/ui/l10n/en.js b/ui/l10n/en.js index 59fa35779ec..e0fb6e7544a 100644 --- a/ui/l10n/en.js +++ b/ui/l10n/en.js @@ -1710,6 +1710,7 @@ var dictionary = { "label.update.project.resources":"Update project resources", "label.update.ssl":" SSL Certificate", "label.update.ssl.cert":" SSL Certificate", +"label.update.vmware.datacenter":"Update VMware datacenter", "label.updating":"Updating", "label.upgrade.required":"Upgrade is required", "label.upgrade.router.newer.template":"Upgrade Router to Use Newer Template", diff --git a/ui/scripts/system.js b/ui/scripts/system.js index e6ba33f0826..c1c693f695a 100755 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -8020,6 +8020,58 @@ } }, + updateVmwareDc: { + label: 'label.update.vmware.datacenter', + messages: { + confirm: function (args) { + return 'label.update.vmware.datacenter'; + }, + notification: function (args) { + return 'label.update.vmware.datacenter'; + } + }, + createForm: { + title: 'label.update.vmware.datacenter', + fields: { + name: { + label: 'label.vmware.datacenter.name' + }, + vcenter: { + label: 'label.vmware.datacenter.vcenter' + }, + username: { + label: 'label.username' + }, + password: { + label: 'label.password', + isPassword: true + } + } + }, + action: function (args) { + var data = args.data; + data.zoneid = args.context.physicalResources[0].id; + $.ajax({ + url: createURL('updateVmwareDc'), + data: data, + success: function (json) { + args.response.success({ + data: args.context.physicalResources[0] + }); + }, + error: function (XMLHttpResponse) { + var errorMsg = parseXMLHttpResponse(XMLHttpResponse); + args.response.error(errorMsg); + } + }); + }, + notification: { + poll: function (args) { + args.complete(); + } + } + }, + removeVmwareDc: { label: 'label.remove.vmware.datacenter', messages: { @@ -22192,9 +22244,12 @@ var jsonObj = args.context.item; var allowedActions =[ 'enableSwift']; - if (jsonObj.vmwaredcId == null) - allowedActions.push('addVmwareDc'); else - allowedActions.push('removeVmwareDc'); + if (jsonObj.vmwaredcId == null) { + allowedActions.push('addVmwareDc'); + } else { + allowedActions.push('updateVmwareDc'); + allowedActions.push('removeVmwareDc'); + } if (jsonObj.domainid != null) allowedActions.push("releaseDedicatedZone"); else