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

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

There is a newer version: 1.6.0
Show newest version
 /**
 * 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.compute.options.RunScriptOptions.Builder.runAsRoot;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_IMAGE_PREFIX;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_NAME_SEPARATOR;
import static org.jclouds.virtualbox.config.VirtualBoxConstants.VIRTUALBOX_NODE_PREFIX;

import java.util.List;

import javax.inject.Inject;
import javax.inject.Singleton;

import org.jclouds.compute.ComputeServiceAdapter.NodeAndInitialCredentials;
import org.jclouds.compute.callables.RunScriptOnNode;
import org.jclouds.compute.callables.RunScriptOnNode.Factory;
import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.domain.NodeMetadataBuilder;
import org.jclouds.compute.options.RunScriptOptions;
import org.jclouds.domain.LoginCredentials;
import org.jclouds.scriptbuilder.domain.Statements;
import org.jclouds.virtualbox.config.VirtualBoxComputeServiceContextModule;
import org.jclouds.virtualbox.domain.CloneSpec;
import org.jclouds.virtualbox.domain.Master;
import org.jclouds.virtualbox.domain.NetworkAdapter;
import org.jclouds.virtualbox.domain.NetworkInterfaceCard;
import org.jclouds.virtualbox.domain.NetworkSpec;
import org.jclouds.virtualbox.domain.NodeSpec;
import org.jclouds.virtualbox.domain.VmSpec;
import org.jclouds.virtualbox.statements.DeleteGShadowLock;
import org.jclouds.virtualbox.statements.EnableNetworkInterface;
import org.jclouds.virtualbox.util.MachineController;
import org.jclouds.virtualbox.util.MachineUtils;
import org.virtualbox_4_1.CleanupMode;
import org.virtualbox_4_1.HostNetworkInterfaceType;
import org.virtualbox_4_1.IDHCPServer;
import org.virtualbox_4_1.IHostNetworkInterface;
import org.virtualbox_4_1.IMachine;
import org.virtualbox_4_1.IProgress;
import org.virtualbox_4_1.ISession;
import org.virtualbox_4_1.NetworkAttachmentType;
import org.virtualbox_4_1.VirtualBoxManager;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;

/**
 * Creates nodes, by cloning a master vm and based on the provided {@link NodeSpec}. Must be
 * synchronized mainly because of snapshot creation (must be synchronized on a per-master-basis).
 * 
 * @author David Alves, Andrea Turli
 * 
 */
@Singleton
public class NodeCreator implements Function> {

   private final Supplier manager;
   private final Function cloner;
   private final MachineUtils machineUtils;
   private final MachineController machineController;
   private final Factory runScriptOnNodeFactory;
   private final Supplier host;


   @Inject
   public NodeCreator(Supplier manager, Function cloner,  Factory runScriptOnNodeFactory,
            Supplier host,
            MachineUtils machineUtils, RunScriptOnNode.Factory scriptRunnerFactory, MachineController machineController) {
      this.manager = manager;
      this.cloner = cloner;
      this.runScriptOnNodeFactory = checkNotNull(runScriptOnNodeFactory, "runScriptOnNodeFactory");
      this.host = checkNotNull(host, "host");

      this.machineUtils = machineUtils;
      this.machineController = machineController;
   }

   @Override
   public synchronized NodeAndInitialCredentials apply(NodeSpec nodeSpec) {

      checkNotNull(nodeSpec, "NodeSpec");

      Master master = nodeSpec.getMaster();
      checkNotNull(master, "Master");

      if (master.getMachine().getCurrentSnapshot() != null) {
         ISession session;
         try {
            session = manager.get().openMachineSession(master.getMachine());
         } catch (Exception e) {
            throw new RuntimeException("error opening vbox machine session: " + e.getMessage(), e);
         }
         IProgress progress = session.getConsole().deleteSnapshot(master.getMachine().getCurrentSnapshot().getId());
         progress.waitForCompletion(-1);
         session.unlockMachine();
      }
      String masterNameWithoutPrefix = master.getMachine().getName().replace(VIRTUALBOX_IMAGE_PREFIX, "");

      String cloneName = VIRTUALBOX_NODE_PREFIX + masterNameWithoutPrefix + VIRTUALBOX_NODE_NAME_SEPARATOR
               + nodeSpec.getTag() + VIRTUALBOX_NODE_NAME_SEPARATOR + nodeSpec.getName();

      int ram = 512;
      if (nodeSpec.getTemplate() != null && nodeSpec.getTemplate().getHardware() != null
               && nodeSpec.getTemplate().getHardware().getRam() > 0) {
         ram = nodeSpec.getTemplate().getHardware().getRam();
      }
      
      VmSpec cloneVmSpec = VmSpec.builder().id(cloneName).name(cloneName).memoryMB(ram).cleanUpMode(CleanupMode.Full)
               .forceOverwrite(true).build();

      // CASE NAT + HOST-ONLY
      NetworkAdapter natAdapter = NetworkAdapter.builder().networkAttachmentType(NetworkAttachmentType.NAT)
               .build();
      NetworkInterfaceCard natIfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(natAdapter).slot(1L).build();

      NetworkAdapter hostOnlyAdapter = NetworkAdapter.builder().networkAttachmentType(NetworkAttachmentType.HostOnly)
               .build();

      // create new hostOnly interface if needed, otherwise use the one already there with dhcp enabled ...
      String hostOnlyIfName = getHostOnlyIfOrCreate();
      
      NetworkInterfaceCard hostOnlyIfaceCard = NetworkInterfaceCard.builder().addNetworkAdapter(hostOnlyAdapter)
               .addHostInterfaceName(hostOnlyIfName).slot(0L).build();

      NetworkSpec networkSpec = createNetworkSpecForHostOnlyNATNICs(natIfaceCard, hostOnlyIfaceCard);

      CloneSpec cloneSpec = CloneSpec.builder().linked(true).master(master.getMachine()).network(networkSpec)
               .vm(cloneVmSpec).build();

      IMachine cloned = cloner.apply(cloneSpec);

      machineController.ensureMachineIsLaunched(cloneVmSpec.getVmName());

      // IMachineToNodeMetadata produces the final ip's but these need to be set before so we build a
      // NodeMetadata just for the sake of running the gshadow and setip scripts 
      NodeMetadata partialNodeMetadata = buildPartialNodeMetadata(cloned);

      // see DeleteGShadowLock for a detailed explanation
      machineUtils.runScriptOnNode(partialNodeMetadata, new DeleteGShadowLock(), RunScriptOptions.NONE);

      // CASE NAT + HOST-ONLY
      machineUtils.runScriptOnNode(partialNodeMetadata, new EnableNetworkInterface(natIfaceCard), RunScriptOptions.NONE);
      

      // TODO get credentials from somewhere else (they are also HC in
      // IMachineToSshClient)
      NodeAndInitialCredentials nodeAndInitialCredentials = new NodeAndInitialCredentials(cloned,
               cloneName, LoginCredentials.builder().user("toor").password("password").authenticateSudo(true).build());

      return nodeAndInitialCredentials;
   }
   
   private NodeMetadata buildPartialNodeMetadata(IMachine clone) {
      NodeMetadataBuilder nodeMetadataBuilder = new NodeMetadataBuilder();
      nodeMetadataBuilder.id(clone.getName());
      nodeMetadataBuilder.status(VirtualBoxComputeServiceContextModule.toPortableNodeStatus.get(clone.getState()));
      nodeMetadataBuilder.publicAddresses(ImmutableSet.of(machineUtils.getIpAddressFromHostOnlyNIC(clone.getName())));
      
      LoginCredentials loginCredentials = new LoginCredentials("toor", "password", null, true);
      nodeMetadataBuilder.credentials(loginCredentials);
      
      return  nodeMetadataBuilder.build();
   }

   private NetworkSpec createNetworkSpecForHostOnlyNATNICs(NetworkInterfaceCard natIfaceCard,
            NetworkInterfaceCard hostOnlyIfaceCard) {
      return NetworkSpec.builder().addNIC(natIfaceCard).addNIC(hostOnlyIfaceCard).build();
   }
   
   /**
    * @return
    */
   private String getHostOnlyIfOrCreate() {     
      
      IHostNetworkInterface availableHostInterfaceIf = returnExistingHostNetworkInterfaceWithDHCPenabledOrNull(manager
               .get().getVBox().getHost().getNetworkInterfaces());
      if (availableHostInterfaceIf==null) {
         final String hostOnlyIfName = createHostOnlyIf();
         assignDHCPtoHostOnlyInterface(hostOnlyIfName);
         return hostOnlyIfName;
      } else {
         return availableHostInterfaceIf.getName();
      }
      
   }

   private void assignDHCPtoHostOnlyInterface(final String hostOnlyIfName) {
      List availableNetworkInterfaces = manager.get().getVBox().getHost()
               .getNetworkInterfaces();
      
      IHostNetworkInterface iHostNetworkInterfaceWithHostOnlyIfName = Iterables.getOnlyElement(Iterables.filter(availableNetworkInterfaces, new Predicate() {

         @Override
         public boolean apply(IHostNetworkInterface iHostNetworkInterface) {
            return iHostNetworkInterface.getName().equals(hostOnlyIfName);
         }
      }));
      
      String hostOnlyIfIpAddress = iHostNetworkInterfaceWithHostOnlyIfName.getIPAddress();
      String dhcpIpAddress = hostOnlyIfIpAddress.substring(0, hostOnlyIfIpAddress.lastIndexOf(".")) + ".254";
      String dhcpNetmask = "255.255.255.0";
      String dhcpLowerIp = hostOnlyIfIpAddress.substring(0, hostOnlyIfIpAddress.lastIndexOf(".")) + ".2";
      String dhcpUpperIp = hostOnlyIfIpAddress.substring(0, hostOnlyIfIpAddress.lastIndexOf(".")) + ".253";
      
      ExecResponse response = runScriptOnNodeFactory
               .create(host.get(),
                        Statements.exec(String
                                 .format("VBoxManage dhcpserver add --ifname %s --ip %s --netmask %s --lowerip %s --upperip %s --enable",
                                          hostOnlyIfName, dhcpIpAddress, dhcpNetmask, dhcpLowerIp, dhcpUpperIp)), runAsRoot(false).wrapInInitScript(false)).init().call();
      checkState(response.getExitStatus()==0);
      /*
      runScriptOnNodeFactory
               .create(host.get(),
                        Statements.exec(String.format("VBoxManage hostonlyif ipconfig %s --ip %s",
                                 hostOnlyIfName, hostOnlyIfIpAddress)), runAsRoot(false).wrapInInitScript(false)).init().call();
      */
   }

   private String createHostOnlyIf() {
      final String hostOnlyIfName;
      ExecResponse createHostOnyResponse = runScriptOnNodeFactory
               .create(host.get(), Statements.exec("VBoxManage hostonlyif create"),
                        runAsRoot(false).wrapInInitScript(false)).init().call();
      String output = createHostOnyResponse.getOutput();
      checkState(createHostOnyResponse.getExitStatus()==0);
      checkState(output.contains("'"), "cannot create hostonlyif");
      hostOnlyIfName = output.substring(output.indexOf("'") + 1, output.lastIndexOf("'"));
      return hostOnlyIfName;
   }

   private IHostNetworkInterface returnExistingHostNetworkInterfaceWithDHCPenabledOrNull(Iterable availableNetworkInterfaces) {
      checkNotNull(availableNetworkInterfaces);
      return Iterables.getFirst(filterAvailableNetworkInterfaceByHostOnlyAndDHCPenabled(availableNetworkInterfaces), null);
   }

   /**
    * @param availableNetworkInterfaces 
    * @param hostOnlyIfIpAddress
    * @return
    */
   private Iterable filterAvailableNetworkInterfaceByHostOnlyAndDHCPenabled(Iterable availableNetworkInterfaces) {
      Iterable filteredNetworkInterfaces = Iterables.filter(availableNetworkInterfaces, new Predicate() {
         @Override
         public boolean apply(IHostNetworkInterface iHostNetworkInterface) {
            // this is an horrible workaround cause iHostNetworkInterface.getDhcpEnabled is working only for windows host
            boolean match = false;
            List availableDHCPservers = manager.get().getVBox().getDHCPServers();
            for (IDHCPServer idhcpServer : availableDHCPservers) {
               if(idhcpServer.getEnabled() && idhcpServer.getNetworkName().equals(iHostNetworkInterface.getNetworkName()))
                  match  = true;
            }
            return iHostNetworkInterface.getInterfaceType().equals(HostNetworkInterfaceType.HostOnly) &&
                    match;
            }
      });
      return filteredNetworkInterfaces;
   }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy