package com.neverwinterdp.vm.service;
import java.util.List;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.mycila.jmx.annotation.JmxBean;
import com.neverwinterdp.registry.Node;
import com.neverwinterdp.registry.NodeCreateMode;
import com.neverwinterdp.registry.event.NodeEvent;
import com.neverwinterdp.registry.Registry;
import com.neverwinterdp.registry.RegistryException;
import com.neverwinterdp.registry.event.RegistryListener;
import com.neverwinterdp.vm.VMConfig;
import com.neverwinterdp.vm.VMDescriptor;
import com.neverwinterdp.vm.VMStatus;
import com.neverwinterdp.vm.event.VMHeartbeatNodeWatcher;
@Singleton
@JmxBean("role=vm-manager, type=VMService, name=VMService")
public class VMService {
final static public String ALLOCATED_PATH = "/vm/allocated";
final static public String HISTORY_PATH = "/vm/history";
final static public String MASTER_PATH = "/vm/master";
final static public String LEADER_PATH = MASTER_PATH + "/leader";
final static public String EVENTS_PATH = "/vm/events";
static public enum Status { INIT, RUNNING, TERMINATED }
@Inject
private Registry registry;
@Inject
private RegistryListener registryListener;
@Inject
private VMServicePlugin plugin ;
@Inject
public void onInit() throws Exception {
registry.createIfNotExist(MASTER_PATH + "/status") ;
registry.createIfNotExist(LEADER_PATH) ;
registry.createIfNotExist(EVENTS_PATH) ;
registry.createIfNotExist(ALLOCATED_PATH) ;
registry.createIfNotExist(HISTORY_PATH) ;
registry.setData(MASTER_PATH + "/status", Status.INIT);
}
public void shutdown() {
plugin.shutdown();
}
public boolean isClosed() { return registry == null ; }
public Registry getRegistry() { return this.registry; }
public void setStatus(Status status) throws Exception {
registry.setData(MASTER_PATH + "/status", status);
}
public VMDescriptor[] getAllocatedVMDescriptors() throws RegistryException {
List<String> names = registry.getChildren(ALLOCATED_PATH) ;
VMDescriptor[] descriptor = new VMDescriptor[names.size()];
for(int i = 0; i < names.size(); i++) {
String name = names.get(i) ;
descriptor[i] = registry.getDataAs(ALLOCATED_PATH + "/" + name, VMDescriptor.class) ;
}
return descriptor;
}
public void register(VMDescriptor descriptor) throws Exception {
Node vmNode = registry.create(ALLOCATED_PATH + "/" + descriptor.getVmConfig().getName(), NodeCreateMode.PERSISTENT);
descriptor.setStoredPath(vmNode.getPath());
vmNode.setData(descriptor);
vmNode.createChild("status", VMStatus.ALLOCATED, NodeCreateMode.PERSISTENT);
vmNode.createChild("commands", NodeCreateMode.PERSISTENT);
watch(descriptor);
}
public void watch(VMDescriptor vmDescriptor) throws Exception {
VMHeartbeatNodeWatcher heartbeatWatcher = new VMHeartbeatNodeWatcher(registry) {
@Override
public void onDisconnected(NodeEvent event, VMDescriptor vmDescriptor) {
try {
unregister(vmDescriptor);
} catch (Exception e) {
e.printStackTrace();
}
}
};
String heartbeatPath = getVMHeartbeatPath(vmDescriptor.getVmConfig().getName());
registryListener.watch(heartbeatPath, heartbeatWatcher, true);
}
public void unregister(VMDescriptor descriptor) throws Exception {
//TODO: fix this check by removing the watcher
if(isClosed()) return;
if(!registry.exists(descriptor.getStoredPath())) return;
//Copy the vm descriptor to the history path. This is not efficient,
//but zookeeper does not provide the move method
Node vmNode =
registry.create(HISTORY_PATH + "/" + descriptor.getVmConfig().getName() + "-", NodeCreateMode.PERSISTENT_SEQUENTIAL);
vmNode.setData(descriptor);
//Recursively delete the vm data in the allocated path
registry.rdelete(descriptor.getStoredPath());
}
public boolean isRunning(VMDescriptor descriptor) throws Exception {
Node statusNode = registry.get(ALLOCATED_PATH + "/" + descriptor.getVmConfig().getName() + "/status");
if(!statusNode.exists()) return false;
VMStatus status = statusNode.getDataAs(VMStatus.class);
if(status == VMStatus.ALLOCATED) {
return true;
} else {
return statusNode.hasChild("heartbeat");
}
}
public void kill(VMDescriptor descriptor) throws Exception {
plugin.killVM(this, descriptor);
}
public VMDescriptor allocate(VMConfig vmConfig) throws RegistryException, Exception {
VMDescriptor vmDescriptor = new VMDescriptor(vmConfig);
register(vmDescriptor);
plugin.allocateVM(this, vmConfig);
return vmDescriptor;
}
static public String getVMStatusPath(String vmName) { return ALLOCATED_PATH + "/" + vmName + "/status" ; }
static public String getVMHeartbeatPath(String vmName) { return ALLOCATED_PATH + "/" + vmName + "/status/heartbeat" ; }
static public void register(Registry registry, VMDescriptor descriptor) throws Exception {
registry.createIfNotExist(ALLOCATED_PATH) ;
registry.createIfNotExist(LEADER_PATH) ;
Node vmNode = registry.create(ALLOCATED_PATH + "/" + descriptor.getVmConfig().getName(), NodeCreateMode.PERSISTENT);
descriptor.setStoredPath(vmNode.getPath());
vmNode.setData(descriptor);
vmNode.createChild("status", VMStatus.ALLOCATED, NodeCreateMode.PERSISTENT);
vmNode.createChild("commands", NodeCreateMode.PERSISTENT);
}
}