package vlove.web.vms; import static vlove.model.TypeConverter.domainState; import java.io.IOException; import java.lang.management.ManagementFactory; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.AjaxLink; import org.apache.wicket.markup.html.IHeaderResponse; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.HiddenField; import org.apache.wicket.markup.html.image.Image; import org.apache.wicket.markup.html.image.NonCachingImage; import org.apache.wicket.markup.html.link.BookmarkablePageLink; import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.markup.html.list.ListView; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.model.util.ListModel; import org.apache.wicket.protocol.http.servlet.ServletWebRequest; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.request.resource.ContextRelativeResource; import org.apache.wicket.spring.injection.annot.SpringBean; import org.apache.wicket.util.string.StringValue; import org.codehaus.jackson.map.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.wicketstuff.annotation.mount.MountPath; import vlove.VirtException; import vlove.model.DomainState; import vlove.model.InternalDomain; import vlove.model.Pair; import vlove.service.VirtManager; import vlove.web.BasePage; import vlove.web.error.ErrorPage; import com.sun.management.OperatingSystemMXBean; @MountPath("/vms/list") @SuppressWarnings("restriction") public class VmListPage extends BasePage { protected static final Logger log = LoggerFactory.getLogger(VmListPage.class); protected static final ObjectMapper m = new ObjectMapper(); @SpringBean VirtManager vm; final DecimalFormat usage = new DecimalFormat("##0.00"); final Map<String, List<Long>> cpuStat = new HashMap<>(); final Map<String, List<String>> memStat = new HashMap<>(); long lastTime = 0l; long lastCpuTime = 0l; private final long totalMem; public VmListPage() { super(); // Init stuff OperatingSystemMXBean os = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); totalMem = os.getTotalPhysicalMemorySize() / 1000; add(new BookmarkablePageLink<>("createVmLink", VmCreatePage.class)); final WebMarkupContainer container = new WebMarkupContainer("container"); add(container.setOutputMarkupId(true)); final ReloadableModel reloadableModel; try { reloadableModel = new ReloadableModel(vm); } catch (VirtException ve) { log.error("Could not create VM model.", ve); setResponsePage(ErrorPage.class); return; } final ListView<InternalDomain> vms = new ListView<InternalDomain>("repeater", reloadableModel) { @Override protected void populateItem(ListItem<InternalDomain> item) { final InternalDomain d = item.getModelObject(); final Integer domainId = d.getDomainId(); final String uuid = d.getUuid(); final String domainName = d.getDomainName(); final DomainState s = d.getState(); item.add(new Label("name", domainName)); item.add(new Label("domainId", Integer.toString(d.getDomainId()))); item.add(new HiddenField<>("uuid", new PropertyModel<String>(d, "uuid"))); final Pair<String, String> domainState = domainState(d.getState()); log.debug("Domain state: {}", domainState); item.add(/*domainState._2 == null ? */ new WebMarkupContainer("statusImage").setVisible(false) /* : new Image("statusImage", new ContextRelativeResource("images/" + domainState._2))*/); final Label status = new Label("status", domainState._1); item.add(status.setOutputMarkupId(true)); final Label memoryUsage = new Label("memoryUsage", ""); item.add(memoryUsage.setOutputMarkupId(true).setMarkupId("memoryUsage-" + uuid)); final Label cpuUsage = new Label("cpuUsage", ""); item.add(cpuUsage.setOutputMarkupId(true).setMarkupId("cpuUsage-" + uuid)); // Buttons final AjaxLink<Object> power = new AjaxLink<Object>("power") { @Override public void onClick(AjaxRequestTarget target) { try { if (s == DomainState.VIR_DOMAIN_RUNNING) { log.debug("Shutting down {}/{}", domainId, domainName); vm.shutdown(domainId); } else { log.debug("Starting/resuming {}/{}.", domainId, domainName); if (domainId == 0) { vm.start(domainName); } else { vm.resume(domainId); } } reloadableModel.reload(); target.add(container); } catch (VirtException ve) { log.error(String.format("Could not %s domain %s.", s.toString(), domainName), ve); // TODO show the user an error } } }; if (s == DomainState.VIR_DOMAIN_RUNNING) { power.add(new NonCachingImage("powerImage", new ContextRelativeResource("/images/power_off.png"))); } else { power.add(new NonCachingImage("powerImage", new ContextRelativeResource("/images/power_on.png"))); } item.add(power.setOutputMarkupId(true)); final AjaxLink<Object> pause = new AjaxLink<Object>("pause") { @Override public void onClick(AjaxRequestTarget target) { log.debug("Pausing down {}.", domainId); try { vm.pause(domainId); reloadableModel.reload(); target.add(container); } catch (VirtException ve) { log.error("Could not pause VM.", ve); // TODO show an error to the user } } }; pause.add(new Image("pauseImage", new ContextRelativeResource("/images/pause.png"))); item.add(pause.setOutputMarkupId(true).setEnabled(s == DomainState.VIR_DOMAIN_RUNNING)); final AjaxLink<Object> destroy = new AjaxLink<Object>("destroy") { @Override public void onClick(AjaxRequestTarget target) { log.debug("Destroying {}.", domainId); try { InternalDomain id = vm.getDomain(domainId); memStat.remove(id.getUuid()); cpuStat.remove(id.getUuid()); vm.destroy(domainId); reloadableModel.reload(); target.add(container); } catch (VirtException ve) { log.error(String.format("Could not destroy VM with ID %d.", domainId), ve); } } }; destroy.add(new Image("destroyImage", new ContextRelativeResource("/images/destroy.png"))); item.add(destroy.setOutputMarkupId(true).setEnabled(s != DomainState.VIR_DOMAIN_SHUTOFF)); } }; container.add(vms.setMarkupId("repeater").setOutputMarkupId(true)); final AbstractDefaultAjaxBehavior abstractAjaxBehavior = new AbstractDefaultAjaxBehavior() { @Override protected void respond(AjaxRequestTarget target) { ServletWebRequest r = (ServletWebRequest) RequestCycle.get().getRequest(); StringValue uuids = r.getRequestParameters().getParameterValue("uuids[]"); if (uuids != null && !uuids.isEmpty()) { log.debug("UUIDs: {}", uuids); /* * for (String uuid : uuids) { try { updatePerformanceStats(uuid); } * catch (VirtException ve) { * log.error(String.format("Could not process stats for %s.", uuid), * ve); } } */ } try { final String cpuStats = String.format("cpuStat=%s;", m.writeValueAsString(cpuStat)); final String memStats = String.format("memStat=%s;", m.writeValueAsString(memStat)); target.appendJavaScript(cpuStats); target.appendJavaScript(memStats); } catch (IOException ie) { log.error("Could not convert object to JSON.", ie); } } }; add(abstractAjaxBehavior); StringBuffer sb = new StringBuffer("var callbackUrl = '"); sb.append(abstractAjaxBehavior.getCallbackUrl()); sb.append("';"); add(new Label("callbackUrl", sb.toString()).setEscapeModelStrings(false)); } void updatePerformanceStats(String uuid) throws VirtException { InternalDomain id = vm.getDomainByUUID(uuid); if (id.getDomainId() < 0) { return; } List<Long> cpu = cpuStat.get(uuid); if (cpu == null) { cpu = new ArrayList<>(); cpuStat.put(uuid, cpu); } if (cpu.size() >= 10) { cpu.remove(0); } final long currTime = id.getCpuTime(); if (lastTime == 0) { lastCpuTime = currTime; lastTime = System.nanoTime(); cpu.add(0l); } else { Long rightNow = System.nanoTime(); final long cpuTime = (id.getCpuTime() - lastCpuTime) * 100; final long timeChange = (rightNow - lastTime); long avg = cpuTime / (timeChange * vm.getCapabilities().getNumProcs()); cpu.add(avg); lastTime = rightNow; lastCpuTime = currTime; } List<String> mem = memStat.get(uuid); if (mem == null) { mem = new ArrayList<>(); memStat.put(uuid, mem); } if (mem.size() >= 10) { mem.remove(0); } final double memUsage = ((double) id.getMemoryUsage() / (double) totalMem) * 100; mem.add(usage.format(memUsage)); } @Override public void renderHead(IHeaderResponse response) { response.renderJavaScriptReference("js/jquery-sparkline-1.5.1.min.js"); } private static final class ReloadableModel extends ListModel<InternalDomain> { private final VirtManager virtMgr; public ReloadableModel(VirtManager vm) throws VirtException { this.virtMgr = vm; setObject(vm.getDomains()); } public void reload() throws VirtException { setObject(virtMgr.getDomains()); } } }