mirror of https://github.com/apache/cloudstack.git
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 <bhaisaab@apache.org>
Reported-by: Nick Wales
Signed-off-by: Rohit Yadav <bhaisaab@apache.org>
This commit is contained in:
parent
7ae8c4ad7e
commit
4c86b1545d
|
|
@ -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){
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -251,6 +251,12 @@ public class CloudZonesNetworkElement extends AdapterBase implements NetworkElem
|
|||
return false;
|
||||
}
|
||||
|
||||
@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;
|
||||
|
|
|
|||
|
|
@ -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<? 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";
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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<String> enabledServices) {
|
||||
// TODO Auto-generated method stub
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue