Package org.jclouds.virtualbox.util

Source Code of org.jclouds.virtualbox.util.NetworkUtils

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.util;

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 java.net.URI;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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

import com.google.common.util.concurrent.Uninterruptibles;
import org.jclouds.compute.callables.RunScriptOnNode;
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.compute.reference.ComputeServiceConstants;
import org.jclouds.location.Provider;
import org.jclouds.logging.Logger;
import org.jclouds.scriptbuilder.domain.Statements;
import org.jclouds.virtualbox.domain.BridgedIf;
import org.jclouds.virtualbox.domain.NetworkAdapter;
import org.jclouds.virtualbox.domain.NetworkInterfaceCard;
import org.jclouds.virtualbox.domain.NetworkSpec;
import org.jclouds.virtualbox.functions.IpAddressesLoadingCache;
import org.jclouds.virtualbox.functions.RetrieveActiveBridgedInterfaces;
import org.jclouds.virtualbox.statements.EnableNetworkInterface;
import org.jclouds.virtualbox.statements.GetIPAddressFromMAC;
import org.jclouds.virtualbox.statements.ScanNetworkWithPing;
import org.virtualbox_4_2.HostNetworkInterfaceType;
import org.virtualbox_4_2.IDHCPServer;
import org.virtualbox_4_2.IHostNetworkInterface;
import org.virtualbox_4_2.INetworkAdapter;
import org.virtualbox_4_2.NetworkAttachmentType;
import org.virtualbox_4_2.VirtualBoxManager;

import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.Inject;

/**
* Utilities to manage VirtualBox networks on guests
*
* @author Andrea Turli
*/

@Singleton
public class NetworkUtils {

   // TODO parameterize
   public static final int MASTER_PORT = 2222;
   private static final String VIRTUALBOX_HOST_GATEWAY = "10.0.2.15";

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

   private final Supplier<VirtualBoxManager> manager;
   private final MachineUtils machineUtils;
   private final Supplier<NodeMetadata> host;
   private final Supplier<URI> providerSupplier;
   private final IpAddressesLoadingCache ipAddressesLoadingCache;
   private final RunScriptOnNode.Factory scriptRunnerFactory;
   private final Supplier<NodeMetadata> hostSupplier;

   @Inject
   public NetworkUtils(Supplier<VirtualBoxManager> manager, MachineUtils machineUtils, Supplier<NodeMetadata> host,
                       @Provider Supplier<URI> providerSupplier, IpAddressesLoadingCache ipAddressesLoadingCache,
                       Supplier<NodeMetadata> hostSupplier, RunScriptOnNode.Factory scriptRunnerFactory) {
      this.manager = manager;
      this.machineUtils = machineUtils;
      this.host = checkNotNull(host, "host can't be null");
      this.providerSupplier = checkNotNull(providerSupplier, "endpoint to virtualbox web server can't be null");
      this.ipAddressesLoadingCache = ipAddressesLoadingCache;
      this.scriptRunnerFactory = scriptRunnerFactory;
      this.hostSupplier = hostSupplier;
   }

   public NetworkSpec createNetworkSpecWhenVboxIsLocalhost() {
      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();
      return createNetworkSpecForHostOnlyNATNICs(natIfaceCard, hostOnlyIfaceCard);
   }

   public NetworkInterfaceCard createHostOnlyNIC(long port) {
      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();
      return NetworkInterfaceCard.builder().addNetworkAdapter(hostOnlyAdapter).addHostInterfaceName(hostOnlyIfName)
            .slot(port).build();
   }

   public boolean enableNetworkInterface(NodeMetadata nodeMetadata, NetworkInterfaceCard networkInterfaceCard) {
      ListenableFuture<ExecResponse> execEnableNetworkInterface = machineUtils.runScriptOnNode(nodeMetadata,
              new EnableNetworkInterface(networkInterfaceCard), RunScriptOptions.NONE);
      ExecResponse execEnableNetworkInterfaceResponse = Futures.getUnchecked(execEnableNetworkInterface);
      return execEnableNetworkInterfaceResponse.getExitStatus() == 0;
   }

   private NetworkSpec createNetworkSpecForHostOnlyNATNICs(NetworkInterfaceCard natIfaceCard,
         NetworkInterfaceCard hostOnlyIfaceCard) {
      return NetworkSpec.builder().addNIC(hostOnlyIfaceCard).addNIC(natIfaceCard).build();
   }

   public 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<IHostNetworkInterface> availableNetworkInterfaces = manager.get().getVBox().getHost().getNetworkInterfaces();

      IHostNetworkInterface iHostNetworkInterfaceWithHostOnlyIfName = Iterables.getOnlyElement(Iterables.filter(
            availableNetworkInterfaces, new Predicate<IHostNetworkInterface>() {

               @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";
      NodeMetadata hostNodeMetadata = getHostNodeMetadata();

      ExecResponse response = scriptRunnerFactory
            .create(
                  hostNodeMetadata,
                  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);
   }

   private String createHostOnlyIf() {
      NodeMetadata hostNodeMetadata = getHostNodeMetadata();
      ExecResponse createHostOnlyResponse = scriptRunnerFactory
            .create(hostNodeMetadata, Statements.exec("VBoxManage hostonlyif create"),
                  runAsRoot(false).wrapInInitScript(false)).init().call();
      String output = createHostOnlyResponse.getOutput();
      checkState(createHostOnlyResponse.getExitStatus() == 0, "cannot create hostonly interface ");
      checkState(output.contains("'"), "cannot create hostonly interface");
      return output.substring(output.indexOf("'") + 1, output.lastIndexOf("'"));
   }

   private NodeMetadata getHostNodeMetadata() {
      return NodeMetadataBuilder.fromNodeMetadata(host.get())
            .publicAddresses(ImmutableList.of(providerSupplier.get().getHost())).build();
   }

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

   private Iterable<IHostNetworkInterface> filterAvailableNetworkInterfaceByHostOnlyAndDHCPenabled(
         Iterable<IHostNetworkInterface> availableNetworkInterfaces) {
      return Iterables.filter(availableNetworkInterfaces,
            new Predicate<IHostNetworkInterface>() {
               @Override
               public boolean apply(IHostNetworkInterface iHostNetworkInterface) {
                  // this is an horrible workaround cause
                  // iHostNetworkInterface.getDhcpEnabled is working only for
                  // windows host
                  boolean match = false;
                  List<IDHCPServer> 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;
               }
            });
   }

   public String getValidHostOnlyIpFromVm(String machineNameOrId) {
      long nicSlot = 0;
      int count = 0;
      String ipAddress = "";
      while (nicSlot < 4 && ipAddress.isEmpty()) {
         MachineNameOrIdAndNicSlot machineNameOrIdAndNicSlot =
                 MachineNameOrIdAndNicSlot.fromParts(machineNameOrId, nicSlot);
         while (count < 10 && ipAddress.isEmpty()) {
            Uninterruptibles.sleepUninterruptibly(3, TimeUnit.SECONDS);
            ipAddress = getIpAddressFromNicSlot(machineNameOrIdAndNicSlot);
            if (!isValidIpForHostOnly(ipAddress)) {
               ipAddressesLoadingCache.invalidate(machineNameOrIdAndNicSlot);
               ipAddress = "";
            }
            count++;
         }
         nicSlot++;
      }
      return checkNotNull(Strings.emptyToNull(ipAddress),
              String.format("Cannot find a valid IP address for the %s's HostOnly NIC", machineNameOrId));
   }

   public String getIpAddressFromNicSlot(String machineNameOrId, long nicSlot) {
      MachineNameOrIdAndNicSlot machineNameOrIdAndNicSlot = MachineNameOrIdAndNicSlot.fromParts(machineNameOrId,
            nicSlot);
      return getIpAddressFromNicSlot(machineNameOrIdAndNicSlot);
   }

   public String getIpAddressFromNicSlot(MachineNameOrIdAndNicSlot machineNameOrIdAndNicSlot) {
      try {
         return ipAddressesLoadingCache.get(machineNameOrIdAndNicSlot);
      } catch (ExecutionException e) {
         logger.error("Problem in using the ipAddressCache", e.getCause());
         throw Throwables.propagate(e);
      }
   }

   public boolean isValidIpForHostOnly(String ip) {
      return !ip.isEmpty() && isIpv4(ip) && !ipBelongsToNatRange(ip) && !ipEqualsToNatGateway(ip);
   }

   public static boolean isIpv4(String s) {
      String IP_V4_ADDRESS_PATTERN = "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
            + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$";
      Pattern pattern = Pattern.compile(IP_V4_ADDRESS_PATTERN);
      Matcher matcher = pattern.matcher(s);
      return matcher.matches();
   }

   private static boolean ipEqualsToNatGateway(String ip) {
      return ip.equals(VIRTUALBOX_HOST_GATEWAY);
   }

   private static boolean ipBelongsToNatRange(String ip) {
      return ip.startsWith("10.0.3");
   }

   protected String getIpAddressFromBridgedNIC(INetworkAdapter networkAdapter) {
      // RetrieveActiveBridgedInterfaces
      List<BridgedIf> activeBridgedInterfaces = new RetrieveActiveBridgedInterfaces(scriptRunnerFactory)
            .apply(hostSupplier.get());
      BridgedIf activeBridgedIf = checkNotNull(Iterables.get(activeBridgedInterfaces, 0), "activeBridgedInterfaces");
      String network = activeBridgedIf.getIpAddress();

      // scan ip
      RunScriptOnNode ipScanRunScript = scriptRunnerFactory.create(hostSupplier.get(),
            new ScanNetworkWithPing(network), RunScriptOptions.NONE);
      ExecResponse execResponse = ipScanRunScript.init().call();
      checkState(execResponse.getExitStatus() == 0);

      // retrieve ip from mac
      RunScriptOnNode getIpFromMACAddressRunScript = scriptRunnerFactory.create(hostSupplier.get(),
            new GetIPAddressFromMAC(networkAdapter.getMACAddress()), RunScriptOptions.NONE);
      ExecResponse ipExecResponse = getIpFromMACAddressRunScript.init().call();
      checkState(ipExecResponse.getExitStatus() == 0);
      return checkNotNull(ipExecResponse.getOutput(), "ipAddress");
   }
}
TOP

Related Classes of org.jclouds.virtualbox.util.NetworkUtils

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.