diff --git a/api/src/com/cloud/api/commands/UpdateVMCmd.java b/api/src/com/cloud/api/commands/UpdateVMCmd.java index 38d22730050..462fe194a8a 100644 --- a/api/src/com/cloud/api/commands/UpdateVMCmd.java +++ b/api/src/com/cloud/api/commands/UpdateVMCmd.java @@ -25,18 +25,20 @@ import com.cloud.api.Implementation; import com.cloud.api.Parameter; import com.cloud.api.ServerApiException; import com.cloud.api.response.UserVmResponse; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; import com.cloud.user.Account; import com.cloud.user.UserContext; import com.cloud.uservm.UserVm; @Implementation(description="Updates properties of a virtual machine. The VM has to be stopped and restarted for the " + - "new properties to take effect. UpdateVirtualMachine does not first check whether the VM is stopped. " + - "Therefore, stop the VM manually before issuing this call.", responseObject=UserVmResponse.class) -public class UpdateVMCmd extends BaseCmd{ - public static final Logger s_logger = Logger.getLogger(UpdateVMCmd.class.getName()); - private static final String s_name = "updatevirtualmachineresponse"; - + "new properties to take effect. UpdateVirtualMachine does not first check whether the VM is stopped. " + + "Therefore, stop the VM manually before issuing this call.", responseObject=UserVmResponse.class) +public class UpdateVMCmd extends BaseCmd{ + public static final Logger s_logger = Logger.getLogger(UpdateVMCmd.class.getName()); + private static final String s_name = "updatevirtualmachineresponse"; + ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @@ -114,7 +116,8 @@ public class UpdateVMCmd extends BaseCmd{ } @Override - public void execute(){ + public void execute() throws ResourceUnavailableException, + InsufficientCapacityException, ServerApiException { UserContext.current().setEventDetails("Vm Id: "+getId()); UserVm result = _userVmService.updateVirtualMachine(this); if (result != null){ diff --git a/api/src/com/cloud/network/element/UserDataServiceProvider.java b/api/src/com/cloud/network/element/UserDataServiceProvider.java index d848216d37f..321ccc7d456 100644 --- a/api/src/com/cloud/network/element/UserDataServiceProvider.java +++ b/api/src/com/cloud/network/element/UserDataServiceProvider.java @@ -29,4 +29,5 @@ import com.cloud.vm.VirtualMachineProfile; public interface UserDataServiceProvider extends NetworkElement { public boolean addPasswordAndUserdata(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException; boolean savePassword(Network network, NicProfile nic, VirtualMachineProfile vm) throws ResourceUnavailableException; + boolean saveUserData(Network network, NicProfile nic, VirtualMachineProfile vm) throws ResourceUnavailableException; } diff --git a/api/src/com/cloud/vm/UserVmService.java b/api/src/com/cloud/vm/UserVmService.java index 02682b0c6f3..98d02dbebdd 100755 --- a/api/src/com/cloud/vm/UserVmService.java +++ b/api/src/com/cloud/vm/UserVmService.java @@ -113,7 +113,7 @@ public interface UserVmService { UserVm rebootVirtualMachine(RebootVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException; - UserVm updateVirtualMachine(UpdateVMCmd cmd); + UserVm updateVirtualMachine(UpdateVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException; UserVm recoverVirtualMachine(RecoverVMCmd cmd) throws ResourceAllocationException; diff --git a/server/src/com/cloud/network/NetworkManager.java b/server/src/com/cloud/network/NetworkManager.java index 4124b19d84b..057f473e732 100755 --- a/server/src/com/cloud/network/NetworkManager.java +++ b/server/src/com/cloud/network/NetworkManager.java @@ -50,6 +50,7 @@ import com.cloud.network.rules.StaticNat; import com.cloud.offering.NetworkOffering; import com.cloud.offerings.NetworkOfferingVO; import com.cloud.user.Account; +import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; import com.cloud.vm.Nic; import com.cloud.vm.NicProfile; @@ -189,6 +190,8 @@ public interface NetworkManager extends NetworkService { UserDataServiceProvider getPasswordResetProvider(Network network); + UserDataServiceProvider getUserDataUpdateProvider(Network network); + boolean networkIsConfiguredForExternalNetworking(long zoneId, long networkId); Map getNetworkServiceCapabilities(long networkId, Service service); diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 5c40f521562..ec0ca79e89a 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -184,6 +184,7 @@ import com.cloud.user.User; import com.cloud.user.UserContext; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.UserStatisticsDao; +import com.cloud.uservm.UserVm; import com.cloud.utils.AnnotationHelper; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; @@ -4341,6 +4342,18 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag return (UserDataServiceProvider)getElementImplementingProvider(passwordProvider); } + @Override + public UserDataServiceProvider getUserDataUpdateProvider(Network network) { + String userDataProvider = _ntwkSrvcDao.getProviderForServiceInNetwork(network.getId(), Service.UserData); + + if (userDataProvider == null) { + s_logger.debug("Network " + network + " doesn't support service " + Service.UserData.getName()); + return null; + } + + return (UserDataServiceProvider)getElementImplementingProvider(userDataProvider); + } + @Override public boolean networkIsConfiguredForExternalNetworking(long zoneId, long networkId) { boolean netscalerInNetwork = isProviderForNetwork(Network.Provider.Netscaler, networkId); diff --git a/server/src/com/cloud/network/element/CloudZonesNetworkElement.java b/server/src/com/cloud/network/element/CloudZonesNetworkElement.java index bb9ae8101fb..ae8be0d3317 100644 --- a/server/src/com/cloud/network/element/CloudZonesNetworkElement.java +++ b/server/src/com/cloud/network/element/CloudZonesNetworkElement.java @@ -251,6 +251,12 @@ public class CloudZonesNetworkElement extends AdapterBase implements NetworkElem return false; } + @Override + public boolean saveUserData(Network network, NicProfile nic, VirtualMachineProfile vm) throws ResourceUnavailableException { + // TODO Auto-generated method stub + return false; + } + @Override public boolean verifyServicesCombination(Set services) { return true; diff --git a/server/src/com/cloud/network/element/VirtualRouterElement.java b/server/src/com/cloud/network/element/VirtualRouterElement.java index 07b182309db..4ff10820ec7 100755 --- a/server/src/com/cloud/network/element/VirtualRouterElement.java +++ b/server/src/com/cloud/network/element/VirtualRouterElement.java @@ -661,6 +661,24 @@ public class VirtualRouterElement extends AdapterBase implements VirtualRouterEl return _routerMgr.savePasswordToRouter(network, nic, uservm, routers); } + @Override + public boolean saveUserData(Network network, NicProfile nic, VirtualMachineProfile vm) + throws ResourceUnavailableException { + if (!canHandle(network, null)) { + return false; + } + List routers = _routerDao.listByNetworkAndRole(network.getId(), Role.VIRTUAL_ROUTER); + if (routers == null || routers.isEmpty()) { + s_logger.debug("Can't find virtual router element in network " + network.getId()); + return true; + } + + @SuppressWarnings("unchecked") + VirtualMachineProfile uservm = (VirtualMachineProfile) vm; + + return _routerMgr.saveUserDataToRouter(network, nic, uservm, routers); + } + @Override public String getPropertiesFile() { return "virtualrouter_commands.properties"; diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java index c57e4725b76..29808717299 100644 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java @@ -62,7 +62,10 @@ public interface VirtualNetworkApplianceManager extends Manager, VirtualNetworkA */ boolean savePasswordToRouter(Network network, NicProfile nic, VirtualMachineProfile profile, List routers) throws ResourceUnavailableException; - + + boolean saveUserDataToRouter(Network network, NicProfile nic, VirtualMachineProfile profile, + List routers) throws ResourceUnavailableException; + List deployVirtualRouterInGuestNetwork(Network guestNetwork, DeployDestination dest, Account owner, Map params, boolean isRedundant) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException; diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 928ce0e0863..f5ec28ce2c5 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -464,6 +464,24 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian }); } + @Override + public boolean saveUserDataToRouter(Network network, final NicProfile nic, VirtualMachineProfile profile, List routers) throws ResourceUnavailableException { + _userVmDao.loadDetails((UserVmVO) profile.getVirtualMachine()); + + final VirtualMachineProfile updatedProfile = profile; + + return applyRules(network, routers, "save userdata entry", false, null, false, new RuleApplier() { + @Override + public boolean execute(Network network, VirtualRouter router) throws ResourceUnavailableException { + // for basic zone, send vm data/password information only to the router in the same pod + Commands cmds = new Commands(OnError.Stop); + NicVO nicVo = _nicDao.findById(nic.getId()); + createVmDataCommand(router, updatedProfile.getVirtualMachine(), nicVo, null, cmds); + return sendCommandsToRouter(router, cmds); + } + }); + } + @Override @ActionEvent(eventType = EventTypes.EVENT_ROUTER_STOP, eventDescription = "stopping router Vm", async = true) public VirtualRouter stopRouter(long routerId, boolean forced) throws ResourceUnavailableException, ConcurrentOperationException { UserContext context = UserContext.current(); diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 96f1a2d20ef..153341682b5 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -1756,13 +1756,14 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager @Override @ActionEvent(eventType = EventTypes.EVENT_VM_UPDATE, eventDescription = "updating Vm") - public UserVm updateVirtualMachine(UpdateVMCmd cmd) { + public UserVm updateVirtualMachine(UpdateVMCmd cmd) + throws ResourceUnavailableException, InsufficientCapacityException { String displayName = cmd.getDisplayName(); String group = cmd.getGroup(); Boolean ha = cmd.getHaEnable(); Long id = cmd.getId(); Long osTypeId = cmd.getOsTypeId(); - String userData = cmd.getUserData(); + String userData = cmd.getUserData().replace("\\n", ""); // Input validation UserVmVO vmInstance = null; @@ -1799,9 +1800,11 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager throw new InvalidParameterValueException("Vm with id " + id + " is not in the right state"); } + boolean updateUserdata = false; if (userData != null) { validateUserData(userData); // update userData on domain router. + updateUserdata = true; } else { userData = vmInstance.getUserData(); } @@ -1833,9 +1836,43 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager _vmDao.updateVM(id, displayName, ha, osTypeId, userData); + if (updateUserdata) { + boolean result = updateUserDataInternal(_vmDao.findById(id)); + if (result) { + s_logger.debug("User data successfully updated for vm id="+id); + } else { + throw new CloudRuntimeException("Failed to reset userdata for the virtual machine "); + } + } + return _vmDao.findById(id); } + private boolean updateUserDataInternal(UserVm vm) + throws ResourceUnavailableException, InsufficientCapacityException { + VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); + Nic defaultNic = _networkMgr.getDefaultNic(vm.getId()); + if (defaultNic == null) { + s_logger.error("Unable to update userdata for vm id=" + vm.getId() + " as the instance doesn't have default nic"); + return false; + } + + Network defaultNetwork = _networkDao.findById(defaultNic.getNetworkId()); + NicProfile defaultNicProfile = new NicProfile(defaultNic, defaultNetwork, null, null, null, + _networkMgr.isSecurityGroupSupportedInNetwork(defaultNetwork), + _networkMgr.getNetworkTag(template.getHypervisorType(), defaultNetwork)); + + VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl((VMInstanceVO)vm); + + UserDataServiceProvider element = _networkMgr.getUserDataUpdateProvider(defaultNetwork); + if (element == null) { + throw new CloudRuntimeException("Can't find network element for " + Service.UserData.getName() + " provider needed for UserData update"); + } + boolean result = element.saveUserData(defaultNetwork, defaultNicProfile, vmProfile); + + return true; + } + @Override @ActionEvent(eventType = EventTypes.EVENT_VM_START, eventDescription = "starting Vm", async = true) public UserVm startVirtualMachine(StartVMCmd cmd) throws ExecutionException, ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { diff --git a/server/test/com/cloud/network/MockNetworkManagerImpl.java b/server/test/com/cloud/network/MockNetworkManagerImpl.java index 8c7b67f7bf0..a038e71c3a9 100755 --- a/server/test/com/cloud/network/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/network/MockNetworkManagerImpl.java @@ -615,6 +615,12 @@ public class MockNetworkManagerImpl implements NetworkManager, Manager, NetworkS return null; } + @Override + public UserDataServiceProvider getUserDataUpdateProvider(Network network) { + // TODO Auto-generated method stub + return null; + } + @Override public PhysicalNetworkServiceProvider updateNetworkServiceProvider(Long id, String state, List enabledServices) { // TODO Auto-generated method stub diff --git a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java index e1b6ad429a9..dc0e880996e 100644 --- a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java @@ -810,6 +810,12 @@ public class MockNetworkManagerImpl implements NetworkManager, Manager{ return null; } + @Override + public UserDataServiceProvider getUserDataUpdateProvider(Network network) { + // TODO Auto-generated method stub + return null; + } + /* (non-Javadoc) * @see com.cloud.network.NetworkManager#networkIsConfiguredForExternalNetworking(long, long) */