Java tutorial
/* * 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"); } }