You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by bh...@apache.org on 2012/11/15 14:14:12 UTC

[2/3] git commit: CLOUDSTACK-424: Update userdata should propagate to VR

CLOUDSTACK-424: Update userdata should propagate to VR

The code that would propage userdata to router, when updateVirtualMachine api
is called, was missing. As per the docs [0], userdata should be a base64 encoded
string upto 2KB which is put on domr's html directory adn using HTTP GET the
userdata information can be obtained from the domr.

The updateVirtualMachine api [0] would accept a base64 encoded string
and decoded and put into the domr's /var/www/html/userdata/<uservm ip>/user-data
file. The operation does not require the VM to be in stopped state, though it is
advised to stop and call this api in case the user vm has a script which gets
the userdata information from domr while starting.

For example, this script can be used to fetch the data:
server_ip=$(grep dhcp-server-identifier /var/lib/dhclient-eth0.leases | tail -1 | awk '{print $NF}' | tr '\;' ' ')
wget http://${server_ip}/latest/user-data

This feature can be useful, for example to use into puppet facts [1], or to do
automation and horizontal scaling etc. based on userdata.

[0] http://incubator.apache.org/cloudstack/docs/api/apidocs-4.0.0/domain_admin/updateVirtualMachine.html
[1] http://geek.jasonhancock.com/2011/11/09/cloudstack-userdata-into-puppet-facts/

BUG-ID : CLOUDSTACK-424
Reviewed-by: Rohit Yadav <bh...@apache.org>
Reported-by: Nick Wales

Signed-off-by: Rohit Yadav <bh...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/commit/4c86b154
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/tree/4c86b154
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/diff/4c86b154

Branch: refs/heads/master
Commit: 4c86b1545dda53b74c534d479133692331e12a28
Parents: 7ae8c4a
Author: Rohit Yadav <bh...@apache.org>
Authored: Thu Nov 15 17:53:33 2012 +0530
Committer: Rohit Yadav <bh...@apache.org>
Committed: Thu Nov 15 18:43:51 2012 +0530

----------------------------------------------------------------------
 api/src/com/cloud/api/commands/UpdateVMCmd.java    |   17 ++++---
 .../network/element/UserDataServiceProvider.java   |    1 +
 api/src/com/cloud/vm/UserVmService.java            |    2 +-
 server/src/com/cloud/network/NetworkManager.java   |    3 +
 .../src/com/cloud/network/NetworkManagerImpl.java  |   13 +++++
 .../network/element/CloudZonesNetworkElement.java  |    6 ++
 .../network/element/VirtualRouterElement.java      |   18 ++++++
 .../router/VirtualNetworkApplianceManager.java     |    5 ++-
 .../router/VirtualNetworkApplianceManagerImpl.java |   18 ++++++
 server/src/com/cloud/vm/UserVmManagerImpl.java     |   41 ++++++++++++++-
 .../com/cloud/network/MockNetworkManagerImpl.java  |    6 ++
 .../test/com/cloud/vpc/MockNetworkManagerImpl.java |    6 ++
 12 files changed, 125 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4c86b154/api/src/com/cloud/api/commands/UpdateVMCmd.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/api/commands/UpdateVMCmd.java b/api/src/com/cloud/api/commands/UpdateVMCmd.java
index 38d2273..462fe19 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){

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4c86b154/api/src/com/cloud/network/element/UserDataServiceProvider.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/network/element/UserDataServiceProvider.java b/api/src/com/cloud/network/element/UserDataServiceProvider.java
index d848216..321ccc7 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<? extends VirtualMachine> vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException;
     boolean savePassword(Network network, NicProfile nic, VirtualMachineProfile<? extends VirtualMachine> vm) throws ResourceUnavailableException;
+    boolean saveUserData(Network network, NicProfile nic, VirtualMachineProfile<? extends VirtualMachine> vm) throws ResourceUnavailableException;
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4c86b154/api/src/com/cloud/vm/UserVmService.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/vm/UserVmService.java b/api/src/com/cloud/vm/UserVmService.java
index 02682b0..98d02db 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;
 

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4c86b154/server/src/com/cloud/network/NetworkManager.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/network/NetworkManager.java b/server/src/com/cloud/network/NetworkManager.java
index 4124b19..057f473 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<Capability, String> getNetworkServiceCapabilities(long networkId, Service service);

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4c86b154/server/src/com/cloud/network/NetworkManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java
index 5c40f52..ec0ca79 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;
@@ -4342,6 +4343,18 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag
     }
 
     @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);
         boolean juniperInNetwork = isProviderForNetwork(Network.Provider.JuniperSRX, networkId);

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4c86b154/server/src/com/cloud/network/element/CloudZonesNetworkElement.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/network/element/CloudZonesNetworkElement.java b/server/src/com/cloud/network/element/CloudZonesNetworkElement.java
index bb9ae81..ae8be0d 100644
--- a/server/src/com/cloud/network/element/CloudZonesNetworkElement.java
+++ b/server/src/com/cloud/network/element/CloudZonesNetworkElement.java
@@ -252,6 +252,12 @@ public class CloudZonesNetworkElement extends AdapterBase implements NetworkElem
     }
 
     @Override
+    public boolean saveUserData(Network network, NicProfile nic, VirtualMachineProfile<? extends VirtualMachine> vm) throws ResourceUnavailableException {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
     public boolean verifyServicesCombination(Set<Service> services) {
         return true;
     }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4c86b154/server/src/com/cloud/network/element/VirtualRouterElement.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/network/element/VirtualRouterElement.java b/server/src/com/cloud/network/element/VirtualRouterElement.java
index 07b1823..4ff1082 100755
--- a/server/src/com/cloud/network/element/VirtualRouterElement.java
+++ b/server/src/com/cloud/network/element/VirtualRouterElement.java
@@ -662,6 +662,24 @@ public class VirtualRouterElement extends AdapterBase implements VirtualRouterEl
     }
 
     @Override
+    public boolean saveUserData(Network network, NicProfile nic, VirtualMachineProfile<? extends VirtualMachine> vm)
+            throws ResourceUnavailableException {
+        if (!canHandle(network, null)) {
+            return false;
+        }
+        List<DomainRouterVO> 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> uservm = (VirtualMachineProfile<UserVm>) vm;
+
+        return _routerMgr.saveUserDataToRouter(network, nic, uservm, routers);
+    }
+
+    @Override
     public String getPropertiesFile() {
         return "virtualrouter_commands.properties";
     }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4c86b154/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java
index c57e472..2980871 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<UserVm> profile, 
             List<? extends VirtualRouter> routers) throws ResourceUnavailableException;
-    	
+
+    boolean saveUserDataToRouter(Network network, NicProfile nic, VirtualMachineProfile<UserVm> profile,
+            List<? extends VirtualRouter> routers) throws ResourceUnavailableException;
+
 	List<DomainRouterVO> deployVirtualRouterInGuestNetwork(Network guestNetwork, DeployDestination dest, Account owner, 
 	        Map<VirtualMachineProfile.Param, Object> params, boolean isRedundant) throws InsufficientCapacityException,
 	        ResourceUnavailableException, ConcurrentOperationException;

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4c86b154/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
index 928ce0e..f5ec28c 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<UserVm> profile, List<? extends VirtualRouter> routers) throws ResourceUnavailableException {
+        _userVmDao.loadDetails((UserVmVO) profile.getVirtualMachine());
+
+        final VirtualMachineProfile<UserVm> 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();

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4c86b154/server/src/com/cloud/vm/UserVmManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java
index 96f1a2d..1533416 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<VMInstanceVO> vmProfile = new VirtualMachineProfileImpl<VMInstanceVO>((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 {

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4c86b154/server/test/com/cloud/network/MockNetworkManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/network/MockNetworkManagerImpl.java b/server/test/com/cloud/network/MockNetworkManagerImpl.java
index 8c7b67f..a038e71 100755
--- a/server/test/com/cloud/network/MockNetworkManagerImpl.java
+++ b/server/test/com/cloud/network/MockNetworkManagerImpl.java
@@ -616,6 +616,12 @@ public class MockNetworkManagerImpl implements NetworkManager, Manager, NetworkS
     }
 
     @Override
+    public UserDataServiceProvider getUserDataUpdateProvider(Network network) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
     public PhysicalNetworkServiceProvider updateNetworkServiceProvider(Long id, String state, List<String> enabledServices) {
         // TODO Auto-generated method stub
         return null;

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4c86b154/server/test/com/cloud/vpc/MockNetworkManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java
index e1b6ad4..dc0e880 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)
      */