All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jclouds.virtualbox.functions.MastersLoadingCache Maven / Gradle / Ivy

/**
 * Licensed to jclouds, Inc. (jclouds) under one or more
 * contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  jclouds licenses this file
 * to you 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 org.jclouds.virtualbox.functions;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_DEFAULT_DIR;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_INSTALLATION_KEY_SEQUENCE;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_NAME_SEPARATOR;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_WORKINGDIR;
import static org.jclouds.virtualbox.util.MachineUtils.machineNotFoundException;

import java.io.File;
import java.net.URI;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.jclouds.Constants;
import org.jclouds.compute.domain.Image;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.logging.Logger;
import org.jclouds.virtualbox.domain.HardDisk;
import org.jclouds.virtualbox.domain.IsoSpec;
import org.jclouds.virtualbox.domain.Master;
import org.jclouds.virtualbox.domain.MasterSpec;
import org.jclouds.virtualbox.domain.NetworkAdapter;
import org.jclouds.virtualbox.domain.NetworkInterfaceCard;
import org.jclouds.virtualbox.domain.NetworkSpec;
import org.jclouds.virtualbox.domain.StorageController;
import org.jclouds.virtualbox.domain.VmSpec;
import org.jclouds.virtualbox.domain.YamlImage;
import org.virtualbox_4_1.CleanupMode;
import org.virtualbox_4_1.IMachine;
import org.virtualbox_4_1.NetworkAttachmentType;
import org.virtualbox_4_1.StorageBus;
import org.virtualbox_4_1.VBoxException;
import org.virtualbox_4_1.VirtualBoxManager;

import com.google.common.base.Function;
import com.google.common.base.Splitter;
import com.google.common.base.Supplier;
import com.google.common.cache.AbstractLoadingCache;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;

/**
 * A {@link LoadingCache} for masters. If the requested master has been previously created this
 * returns it, if not it coordinates its creation including downloading isos and creating
 * cache/config directories.
 * 
 * @author dralves
 * 
 */
@Singleton
public class MastersLoadingCache extends AbstractLoadingCache {

   // TODO parameterize
   public static final int MASTER_PORT = 2222;

   @Resource
   @Named(ComputeServiceConstants.COMPUTE_LOGGER)
   protected Logger logger = Logger.NULL;

   private final Map masters = Maps.newHashMap();
   private final Function masterCreatorAndInstaller;
   private final Map imageMapping;
   private final String workingDir;
   private final String installationKeySequence;
   private final String isosDir;
   private Supplier manager;
   private Function isoDownloader;
   private String version;

   @Inject
   public MastersLoadingCache(@Named(Constants.PROPERTY_BUILD_VERSION) String version,
            @Named(VIRTUALBOX_INSTALLATION_KEY_SEQUENCE) String installationKeySequence,
            @Named(VIRTUALBOX_WORKINGDIR) String workingDir, Function masterLoader,
            Supplier> yamlMapper, Supplier manager,
            Function isoDownloader) {
      checkNotNull(version, "version");
      checkNotNull(installationKeySequence, "installationKeySequence");
      checkNotNull(manager, "vboxmanager");
      this.manager = manager;
      this.masterCreatorAndInstaller = masterLoader;
      this.installationKeySequence = installationKeySequence;
      this.workingDir = workingDir == null ? VIRTUALBOX_DEFAULT_DIR : workingDir;
      this.isosDir = workingDir + File.separator + "isos";
      this.imageMapping = Maps.newLinkedHashMap();
      for (Entry entry : yamlMapper.get().entrySet()) {
         this.imageMapping.put(entry.getKey().getId(), entry.getValue());
      }
      this.version = Iterables.get(Splitter.on('r').split(version), 0);
      this.isoDownloader = isoDownloader;
   }

   @PostConstruct
   public void createCacheDirStructure() {
      if (!new File(workingDir).exists()) {
         new File(workingDir, "isos").mkdirs();
      }
   }

   @Override
   public synchronized Master get(Image key) throws ExecutionException {
      // check if we have loaded this machine before
      if (masters.containsKey(key.getId())) {
         return masters.get(key.getId());
      }

      // the yaml image
      YamlImage yamlImage = imageMapping.get(key.getId());

      checkNotNull(yamlImage, "could not find yaml image for image: " + key);
      
      checkState(!yamlImage.id.contains(VIRTUALBOX_NODE_NAME_SEPARATOR), "master image names cannot contain \""
               + VIRTUALBOX_NODE_NAME_SEPARATOR + "\"");

      String guestAdditionsFileName = String.format("VBoxGuestAdditions_%s.iso", version);
      String guestAdditionsIso = String.format("%s/%s", isosDir, guestAdditionsFileName);
      String guestAdditionsUri = "http://download.virtualbox.org/virtualbox/" + version + "/" + guestAdditionsFileName;
      if (!new File(guestAdditionsIso).exists()) {
         getFilePathOrDownload(guestAdditionsUri);
      }
      checkState(new File(guestAdditionsIso).exists(), "guest additions iso does not exist at: " + guestAdditionsIso);

      // check if the iso is here, download if not
      String localIsoUrl = getFilePathOrDownload(yamlImage.iso);

      String vmName = VIRTUALBOX_IMAGE_PREFIX + yamlImage.id;

      String adminDisk = workingDir + File.separator + vmName + ".vdi";

      HardDisk hardDisk = HardDisk.builder().diskpath(adminDisk).autoDelete(true).controllerPort(0).deviceSlot(1)
               .build();

      StorageController ideController = StorageController.builder().name("IDE Controller").bus(StorageBus.IDE)
               .attachISO(0, 0, localIsoUrl).attachHardDisk(hardDisk).attachISO(1, 0, guestAdditionsIso).build();

      VmSpec vmSpecification = VmSpec.builder().id(yamlImage.id).name(vmName).memoryMB(512).osTypeId("")
               .controller(ideController).forceOverwrite(true).cleanUpMode(CleanupMode.Full).build();

      NetworkAdapter networkAdapter = NetworkAdapter.builder().networkAttachmentType(NetworkAttachmentType.NAT)
               .tcpRedirectRule("127.0.0.1", MASTER_PORT , "", 22).build();

      NetworkInterfaceCard networkInterfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(networkAdapter)
               .slot(0L).build();

      NetworkSpec networkSpec = NetworkSpec.builder().addNIC(networkInterfaceCard).build();

      MasterSpec masterSpec = MasterSpec
               .builder()
               .vm(vmSpecification)
               .iso(IsoSpec.builder().sourcePath(localIsoUrl)
                        .installationScript(installationKeySequence.replace("HOSTNAME", vmSpecification.getVmName()))
                        .build()).network(networkSpec).build();

      IMachine masterMachine;

      // try and find a master machine in vbox
      try {
         masterMachine = manager.get().getVBox().findMachine(vmName);
      } catch (VBoxException e) {
         if (machineNotFoundException(e)) {
            // create the master machine if it can't be found
            masterMachine = masterCreatorAndInstaller.apply(masterSpec);
         } else {
            throw e;
         }
      }

      Master master = Master.builder().machine(masterMachine).spec(masterSpec).build();

      masters.put(key.getId(), master);

      return master;
   }

   @Override
   public synchronized Master getIfPresent(Image key) {
      if (masters.containsKey(key.getId())) {
         return masters.get(key.getId());
      }
      return null;
   }

   private String getFilePathOrDownload(String httpUrl) throws ExecutionException {
      String fileName = httpUrl.substring(httpUrl.lastIndexOf('/') + 1, httpUrl.length());
      File localFile = new File(isosDir, fileName);
      if (!localFile.exists()) {
         logger.debug("iso not found in cache, downloading: %s", httpUrl);
         localFile = isoDownloader.apply(URI.create(httpUrl));
      }
      checkState(localFile.exists(), "iso file has not been downloaded: " + fileName);
      return localFile.getAbsolutePath();
   }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy