/* * Copyright 2000-2011 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.buildServer.clouds.vmware; import com.intellij.openapi.diagnostic.Logger; import java.util.*; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import jetbrains.buildServer.clouds.*; import jetbrains.buildServer.clouds.vmware.settings.VMImageInfo; import jetbrains.buildServer.clouds.vmware.vmrun.VMRun; import jetbrains.buildServer.clouds.vmware.vmrun.VMRunParameters; import jetbrains.buildServer.clouds.vmware.vmrun.remote.server.ClientState; import org.jetbrains.annotations.NotNull; /** * @author Eugene Petrenko * Created: 20.03.2010 18:04:08 */ public class VMRemoteHost extends ErrorHolder { private static final Logger LOG = Logger.getInstance(VMRemoteHost.class.getName()); private final ScheduledExecutorService myExecutor; private final Map myImages = new HashMap(); private final Map myImageIdToImage = new HashMap(); private final VMRun myVMRun; private final ClientState myVMRunState; private final AtomicBoolean myImagesSynchronized = new AtomicBoolean(false); public VMRemoteHost(@NotNull final VMRun run, @NotNull final ClientState state, final ScheduledExecutorService executor) { myExecutor = executor; myVMRun = run; myVMRunState = state; myExecutor.execute(wrap("Check version", new Runnable() { public void run() { final int version = run.getVersion(VMRun.VERSION); if (version != VMRun.VERSION) { LOG.warn("Client: " + run.getHostName() + " is obsolete!"); } } })); myExecutor.scheduleWithFixedDelay(wrap("Update images", new Runnable() { public void run() { if (!myVMRunState.isAlive()) { dispose(); return; } final Collection allInstances = myVMRun.getLocalImages(); final Set runningInstances = new HashSet(); for (VMImageInfo instance : myVMRun.getRunningImages()) { runningInstances.add(instance.getId()); } updateImagesAndInstances(allInstances, runningInstances); myImagesSynchronized.set(true); } }), 15, 15, TimeUnit.SECONDS); } @NotNull public String getId() { return myVMRun.getLocationId(); } @NotNull public String getHostName() { return myVMRun.getHostName(); } private synchronized void updateImagesAndInstances(final Collection allInstances, final Set runningInstances) { Set obsoleteImages = new HashSet(myImageIdToImage.keySet()); for (VMImageInfo info : allInstances) { VMWareImage image = myImageIdToImage.get(info.getId()); obsoleteImages.remove(info.getId()); if (image != null) { image.update(info); } else { image = new VMWareImage(info, this); myImageIdToImage.put(info.getId(), image); } if (runningInstances.contains(info.getId())) { image.setRunningInstanceDetected(); } else { image.setNoRunningInstance(); } } for (String obsoleteImage : obsoleteImages) { final VMWareImage image = myImageIdToImage.remove(obsoleteImage); if (image != null) { image.dispose(); } } myImages.clear(); for (VMWareImage image : myImageIdToImage.values()) { myImages.put(image.getId(), image); } } @NotNull public synchronized VMWareInstance startNewInstance(@NotNull final VMWareImage image, @NotNull final CloudInstanceUserData tag) { if (!canStartNewInstance(image)) throw new QuotaException("Failed to start more than one instance of the same type"); final VMWareInstance instance = image.createStartingInstance(); tag.addAgentConfigurationParameter(VMWareConstants.VMWARE_NAME_PROPOSAL, instance.getAgentName()); myExecutor.submit(instance.wrapPermanentError("start new instnace", new Runnable() { public void run() { try { final VMWareImage im = instance.getImage(); myVMRun.startMachine(new VMRunParameters(instance.getInstanceId(), instance.getImageId(), myVMRun.getLocationId(), im.getInfo()), tag); instance.setStatus(InstanceStatus.STARTING); } catch (QuotaException e) { LOG.info("Quota error: " + e.getMessage()); instance.getImage().setNoRunningInstance(); instance.dispose(); } } })); return instance; } public synchronized boolean canStartNewInstance(final VMWareImage checkImage) { //Check if current image is running a machine if (checkImage.isInstanceRunning()) return false; int allowed = myVMRun.getAllowedMachinesToRun(); for (VMWareImage image : getImages()) { if (image.isInstanceRunning()) { allowed--; } } return allowed > 0; } public synchronized void terminateInstance(@NotNull final VMWareInstance instance) { if (findImageById(instance.getImageId()) == null) { throw new CloudException("Image of instance " + instance + " was not found"); } instance.setStatus(InstanceStatus.SCHEDULED_TO_STOP); myExecutor.submit(instance.wrapPermanentError("terminate instance", new Runnable() { public void run() { try { myVMRun.terminateMachine(instance.getImage().getInfo()); } finally { instance.setStatus(InstanceStatus.STOPPING); } } })); } public synchronized void dispose() { for (VMWareImage image : myImages.values()) { image.dispose(); } myImages.clear(); myExecutor.shutdownNow(); } public boolean isInitialized() { return myImagesSynchronized.get(); } public void waitForInitialized() { if (isInitialized()) return; final Semaphore sem = new Semaphore(1); sem.acquireUninterruptibly(); myExecutor.submit(new Runnable() { public void run() { sem.release(); } }); try { sem.acquire(); } catch (InterruptedException e) { return; } if (isInitialized()) return; //TODO: avoid sleep try { Thread.sleep(500); } catch (InterruptedException e) { return; } waitForInitialized(); } public synchronized CloudImage findImageById(@NotNull final String imageId) throws CloudException { return myImages.get(imageId); } @NotNull public synchronized Collection getImages() throws CloudException { return new ArrayList(myImages.values()); } }