package hudson.plugins.vmware; import hudson.Plugin; import static hudson.Util.fixNull; import hudson.model.Hudson; import hudson.util.FormValidation; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import java.io.File; import java.io.IOException; import java.io.Writer; import java.io.Closeable; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; /** * Entry point of vmware plugin. * * @author Stephen Connolly * @plugin */ public class PluginImpl extends Plugin { /** * Returns the plugin instance. * * @return The plugin instance. */ public static PluginImpl getInstance() { return Hudson.getInstance().getPlugin(PluginImpl.class); } private static class VixReference { private final Closeable closeable; private int refCount; public VixReference(Closeable closeable) { closeable.getClass(); this.closeable = closeable; this.refCount = 1; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; VixReference that = (VixReference) o; // use object identity return closeable == that.closeable; } @Override public int hashCode() { return System.identityHashCode(closeable); } public synchronized void inc() { refCount++; } public synchronized void dec() throws IOException { refCount--; if (refCount == 0) { closeable.close(); } } } private static final ConcurrentMap<String, String> vmIPAddresses = new ConcurrentHashMap<String, String>(); private static final ConcurrentMap<String, CountDownLatch> nameLatches = new ConcurrentHashMap<String, CountDownLatch>(); private final String URL_PREFIX = "file:/"; public static String findDefaultVixLibraryPath() { if (System.getProperty("os.name").toLowerCase().startsWith("windows")) { if (new File("C:\\Program Files\\VMware\\VMware VIX\\server-1\\32bit\\vix.dll").exists()) { return "C:\\Program Files\\VMware\\VMware VIX\\server-1\\32bit"; } else if (new File("C:\\Program Files\\VMware\\VMware VIX\\vix.dll").exists()) { return "C:\\Program Files\\VMware\\VMware VIX"; } else { return ""; } } else { if (new File("/usr/lib/vmware-vix/lib/server-1/32bit/libvix.so").exists()) { return "/usr/lib/vmware-vix/lib/server-1/32bit"; } else if (new File("/usr/lib/vmware-vix/lib/libvix.so").exists()) { return "/usr/lib/vmware-vix/lib"; } else { return ""; } } } /** * Checks if the VIX path is a valid VIX path. */ public FormValidation doVixLibraryCheck(@QueryParameter final String value) { // this can be used to check the existence of a file on the server, so needs to be protected if (!Hudson.getInstance().hasPermission(Hudson.ADMINISTER)) return FormValidation.ok(); File f = new File(fixNull(value)); if (!f.isDirectory()) { return FormValidation.error(Messages.PluginImpl_NotADirectory(f)); } File winDll = new File(f, "vix.dll"); File linuxSO = new File(f, "libvix.so"); if (!winDll.exists() && !linuxSO.exists()) { return FormValidation.error(Messages.PluginImpl_NotAVixLibraryDirectory(f)); } return FormValidation.ok(); } /** * Gets the current name-value pairs of virtual machine names and IP addresses. * * @return The name-value pairs. */ public Map<String, String> getVmIPAddresses() { return Collections.unmodifiableMap(vmIPAddresses); } /** * Stapler handler for setting a VM IP. * * @param req The request. * @param rsp The response. * @throws IOException If there are problems with IO. */ public void doSet(StaplerRequest req, StaplerResponse rsp) throws IOException { Writer w = rsp.getCompressedWriter(req); String key = req.getParameter("name"); String ip1 = req.getParameter("override"); String ip2 = req.getRemoteAddr(); String ip = ip1 == null ? ip2 : ip1; if (key == null) { w.append(Messages.PluginImpl_MissingParameterName() + "\n"); w.append(Messages.PluginImpl_HowToOverrideIP() + "\n"); } else { w.append(Messages.PluginImpl_IPAddressSet(key, ip) + "\n"); setVMIP(key, ip); } w.append(Messages.PluginImpl_RequestOriginatedFrom(ip2)); w.close(); } /** * Stapler handler for unsetting a VM IP. * * @param req The request. * @param rsp The response. * @throws IOException If there are problems with IO. */ public void doUnset(StaplerRequest req, StaplerResponse rsp) throws IOException { Writer w = rsp.getCompressedWriter(req); String key = req.getParameter("name"); if (key == null) { w.append(Messages.PluginImpl_MissingParameterName() + "\n"); } else { w.append(Messages.PluginImpl_IPAddressCleared(key) + "\n"); clearVMIP(key); } w.append(Messages.PluginImpl_RequestOriginatedFrom(req.getRemoteAddr())); w.close(); } /** * Stapler handler for querying a VM IP. * * @param req The request. * @param rsp The response. * @throws IOException If there are problems with IO. */ public void doQuery(StaplerRequest req, StaplerResponse rsp) throws IOException { Writer w = rsp.getCompressedWriter(req); String key = req.getParameter("name"); if (key == null) { w.append(Messages.PluginImpl_MissingParameterName() + "\n"); } else { w.append(getVMIP(key)); } w.close(); } /** * Waits until the specified key has been set. Will return immediately if the key is already set. * * @param key The key to look for. * @throws InterruptedException If interrupted. */ public static void awaitVMIP(String key) throws InterruptedException { if (vmIPAddresses.containsKey(key)) { return; } watchVMIP(key); final CountDownLatch latch = nameLatches.get(key); assert latch != null; latch.await(); } /** * Waits at most <code>timeout</code> for the specified key to be set. Will return immediately if the key is * already set. * * @param key The key to look for. * @param timeout The timeout. * @param unit The units of the timeout. * @return <code>true</code> if the key has been set. * @throws InterruptedException If interrupted. */ public static boolean awaitVMIP(String key, long timeout, TimeUnit unit) throws InterruptedException { if (vmIPAddresses.containsKey(key)) { return true; } watchVMIP(key); final CountDownLatch latch = nameLatches.get(key); assert latch != null; return latch.await(timeout, unit); } public static void watchVMIP(String key) { if (!nameLatches.containsKey(key)) { nameLatches.putIfAbsent(key, new CountDownLatch(1)); } } /** * Sets the key, releasing any threads that were waiting for it to be set. * * @param key The name. * @param ip The value. */ public static void setVMIP(String key, String ip) { vmIPAddresses.put(key, ip); final CountDownLatch latch = nameLatches.get(key); if (latch != null) { latch.countDown(); nameLatches.remove(key, latch); } } /** * Clears the key. * * @param key The name. */ public static void clearVMIP(String key) { vmIPAddresses.remove(key); } /** * Returns the current value of the key. * * @param key The key. * @return The current value or <code>null</code> if empty. */ public static String getVMIP(String key) { return vmIPAddresses.get(key); } /** * Gets a set of all the current names. * * @return all the current names. */ public static Set<String> getVMs() { return Collections.unmodifiableSet(vmIPAddresses.keySet()); } private static final java.util.logging.Logger LOGGER = Logger.getLogger(PluginImpl.class.getName()); }