Java tutorial
/******************************************************************************* *Copyright (c) 2009 Eucalyptus Systems, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, only version 3 of the License. * * * This file is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. * * Please contact Eucalyptus Systems, Inc., 130 Castilian * Dr., Goleta, CA 93101 USA or visit <http://www.eucalyptus.com/licenses/> * if you need additional information or have any questions. * * This file may incorporate work covered under the following copyright and * permission notice: * * Software License Agreement (BSD License) * * Copyright (c) 2008, Regents of the University of California * All rights reserved. * * Redistribution and use of this software in source and binary forms, with * or without modification, are permitted provided that the following * conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. USERS OF * THIS SOFTWARE ACKNOWLEDGE THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE * LICENSED MATERIAL, COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS * SOFTWARE, AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING * IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, SANTA * BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, WHICH IN * THE REGENTS DISCRETION MAY INCLUDE, WITHOUT LIMITATION, REPLACEMENT * OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO IDENTIFIED, OR * WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT NEEDED TO COMPLY WITH * ANY SUCH LICENSES OR RIGHTS. *******************************************************************************/ /* * Author: chris grzegorczyk <grze@eucalyptus.com> */ package com.eucalyptus.sla; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import org.apache.log4j.Logger; import com.eucalyptus.address.Address; import com.eucalyptus.address.AddressCategory; import com.eucalyptus.address.Addresses; import com.eucalyptus.cluster.Cluster; import com.eucalyptus.cluster.ClusterThreadFactory; import com.eucalyptus.cluster.Clusters; import com.eucalyptus.cluster.Networks; import com.eucalyptus.cluster.NoSuchTokenException; import com.eucalyptus.cluster.StatefulMessageSet; import com.eucalyptus.cluster.SuccessCallback; import com.eucalyptus.cluster.VmInstance; import com.eucalyptus.cluster.VmInstances; import com.eucalyptus.cluster.callback.ConfigureNetworkCallback; import com.eucalyptus.cluster.callback.QueuedEventCallback; import com.eucalyptus.cluster.callback.StartNetworkCallback; import com.eucalyptus.cluster.callback.VmRunCallback; import com.eucalyptus.records.EventType; import com.eucalyptus.util.Exceptions; import com.eucalyptus.vm.SystemState; import com.eucalyptus.vm.VmState; import com.eucalyptus.vm.SystemState.Reason; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import edu.ucsb.eucalyptus.cloud.Network; import edu.ucsb.eucalyptus.cloud.NetworkToken; import edu.ucsb.eucalyptus.cloud.ResourceToken; import edu.ucsb.eucalyptus.cloud.VmAllocationInfo; import edu.ucsb.eucalyptus.cloud.VmImageInfo; import edu.ucsb.eucalyptus.cloud.VmInfo; import edu.ucsb.eucalyptus.cloud.VmKeyInfo; import edu.ucsb.eucalyptus.cloud.VmRunResponseType; import edu.ucsb.eucalyptus.cloud.VmRunType; import com.eucalyptus.records.EventRecord; import edu.ucsb.eucalyptus.msgs.BaseMessage; import edu.ucsb.eucalyptus.msgs.RunInstancesType; import edu.ucsb.eucalyptus.msgs.VmTypeInfo; public class ClusterAllocator extends Thread { private static Logger LOG = Logger.getLogger(ClusterAllocator.class); enum State { START, CREATE_NETWORK, CREATE_NETWORK_RULES, CREATE_VMS, ASSIGN_ADDRESSES, FINISHED, ROLLBACK; } public static Boolean SPLIT_REQUESTS = true; private StatefulMessageSet<State> messages; private Cluster cluster; private VmAllocationInfo vmAllocInfo; public static void create(ResourceToken t, VmAllocationInfo vmAllocInfo) { ClusterThreadFactory.getThreadFactory(t.getCluster()).newThread(new ClusterAllocator(t, vmAllocInfo)) .start(); } private ClusterAllocator(ResourceToken vmToken, VmAllocationInfo vmAllocInfo) { this.vmAllocInfo = vmAllocInfo; if (vmToken != null) { try { this.cluster = Clusters.getInstance().lookup(vmToken.getCluster()); this.messages = new StatefulMessageSet<State>(cluster, State.values()); for (NetworkToken networkToken : vmToken.getNetworkTokens()) this.setupNetworkMessages(networkToken); this.setupVmMessages(vmToken); } catch (Throwable e) { LOG.debug(e, e); try { Clusters.getInstance().lookup(vmToken.getCluster()).getNodeState().releaseToken(vmToken); } catch (Throwable e1) { LOG.debug(e1); LOG.trace(e1, e1); } for (String addr : vmToken.getAddresses()) { try { Addresses.release(Addresses.getInstance().lookup(addr)); } catch (Throwable e1) { LOG.debug(e1); LOG.trace(e1, e1); } } try { if (vmToken.getPrimaryNetwork() != null) { Network net = Networks.getInstance().lookup(vmToken.getPrimaryNetwork().getName()); for (Integer i : vmToken.getPrimaryNetwork().getIndexes()) { net.returnNetworkIndex(i); } } } catch (Throwable e1) { LOG.debug(e1); LOG.trace(e1, e1); } for (String vmId : vmToken.getInstanceIds()) { try { VmInstance vm = VmInstances.getInstance().lookup(vmId); vm.setState(VmState.TERMINATED, Reason.FAILED, e.getMessage()); VmInstances.getInstance().disable(vmId); } catch (Exception e1) { LOG.debug(e1, e1); } } } } } @SuppressWarnings("unchecked") private void setupNetworkMessages(NetworkToken networkToken) { if (networkToken != null) { QueuedEventCallback callback = new StartNetworkCallback(networkToken) .regardingUserRequest(vmAllocInfo.getRequest()); this.messages.addRequest(State.CREATE_NETWORK, callback); //EventRecord.here( ClusterAllocator.class, EventType.VM_PREPARE, callback.getClass( ).getSimpleName( ), networkToken.toString( ) ).debug( ); } try { RunInstancesType request = this.vmAllocInfo.getRequest(); if (networkToken != null) { Network network = Networks.getInstance().lookup(networkToken.getName()); //EventRecord.here( ClusterAllocator.class, EventType.VM_PREPARE, ConfigureNetworkCallback.class.getSimpleName( ), network.getRules( ).toString( ) ).debug( ); if (!network.getRules().isEmpty()) { this.messages.addRequest(State.CREATE_NETWORK_RULES, new ConfigureNetworkCallback( this.vmAllocInfo.getRequest().getUserId(), network.getRules())); } //:: need to refresh the rules on the backend for all active networks which point to this network ::// for (Network otherNetwork : Networks.getInstance().listValues()) { if (otherNetwork.isPeer(network.getUserName(), network.getNetworkName())) { LOG.warn("Need to refresh rules for incoming named network ingress on: " + otherNetwork.getName()); LOG.debug(otherNetwork); if (!otherNetwork.getRules().isEmpty()) { this.messages.addRequest(State.CREATE_NETWORK_RULES, new ConfigureNetworkCallback( otherNetwork.getUserName(), otherNetwork.getRules())); } } } } } catch (NoSuchElementException e) { } /* just added this network, shouldn't happen, if so just smile and nod */ } private void setupVmMessages(final ResourceToken token) { Integer vlan = null; List<String> networkNames = null; ArrayList<String> networkIndexes = Lists.newArrayList(); if (token.getPrimaryNetwork() != null) { vlan = token.getPrimaryNetwork().getVlan(); if (vlan < 0) vlan = 9;//FIXME: general vlan, should be min-1? networkNames = Lists.newArrayList(token.getPrimaryNetwork().getNetworkName()); for (Integer index : token.getPrimaryNetwork().getIndexes()) { networkIndexes.add(index.toString()); } } else { vlan = -1; networkNames = Lists.newArrayList(Collections.nCopies(token.getAmount(), "default")); networkIndexes = Lists.newArrayList(Collections.nCopies(token.getAmount(), "-1")); } final List<String> addresses = Lists.newArrayList(token.getAddresses()); RunInstancesType request = this.vmAllocInfo.getRequest(); String rsvId = this.vmAllocInfo.getReservationId(); VmImageInfo imgInfo = this.vmAllocInfo.getImageInfo(); VmKeyInfo keyInfo = this.vmAllocInfo.getKeyInfo(); VmTypeInfo vmInfo = this.vmAllocInfo.getVmTypeInfo(); byte[] userData = this.vmAllocInfo.getUserData(); QueuedEventCallback cb = null; try { int index = 0; for (ResourceToken childToken : this.cluster.getNodeState().splitToken(token)) { cb = makeRunRequest(request, childToken, rsvId, imgInfo, keyInfo, vmInfo, userData); this.messages.addRequest(State.CREATE_VMS, cb); index++; } } catch (NoSuchTokenException e) { cb = makeRunRequest(request, token, rsvId, token.getInstanceIds(), imgInfo, keyInfo, vmInfo, vlan, networkNames, networkIndexes, addresses, userData); } if (cb != null) { this.messages.addRequest(State.CREATE_VMS, cb); } else { Exceptions.eat("Failed to create VM run callback: " + token); } } private QueuedEventCallback makeRunRequest(RunInstancesType request, ResourceToken childToken, String rsvId, List<String> instanceIds, VmImageInfo imgInfo, VmKeyInfo keyInfo, VmTypeInfo vmInfo, Integer vlan, List<String> networkNames, List<String> netIndexes, final List<String> addrList, byte[] userData) { List<String> macs = Lists.transform(instanceIds, new Function<String, String>() { @Override public String apply(String instanceId) { return VmInstances.getAsMAC(instanceId); } }); VmRunType run = new VmRunType(rsvId, request.getUserData(), childToken.getAmount(), imgInfo, vmInfo, keyInfo, instanceIds, macs, vlan, networkNames, netIndexes).regardingUserRequest(request); VmRunCallback cb = new VmRunCallback(run, childToken); if (!addrList.isEmpty()) { cb.then(new SuccessCallback<VmRunResponseType>() { @Override public void apply(VmRunResponseType response) { Iterator<String> addrs = addrList.iterator(); for (VmInfo vmInfo : response.getVms()) {//TODO: this will have some funny failure characteristics final Address addr = Addresses.getInstance().lookup(addrs.next()); final VmInstance vm = VmInstances.getInstance().lookup(vmInfo.getInstanceId()); addr.assign(vm.getInstanceId(), vm.getPrivateAddress()).getCallback() .dispatch(addr.getCluster()); } } }); } return cb; } private QueuedEventCallback makeRunRequest(RunInstancesType request, final ResourceToken childToken, String rsvId, VmImageInfo imgInfo, VmKeyInfo keyInfo, VmTypeInfo vmInfo, byte[] userData) { List<String> macs = Lists.transform(childToken.getInstanceIds(), new Function<String, String>() { @Override public String apply(String instanceId) { return VmInstances.getAsMAC(instanceId); } }); NetworkToken primaryNet = childToken.getPrimaryNetwork(); int vlan; List<String> netIndexes; List<String> networkNames; if (primaryNet != null) { vlan = primaryNet.getVlan(); networkNames = Lists.newArrayList(primaryNet.getNetworkName()); netIndexes = Lists .newArrayList(Iterables.transform(primaryNet.getIndexes(), Functions.toStringFunction())); } else { vlan = -1; networkNames = Lists.newArrayList("default"); netIndexes = Lists.newArrayList("-1"); } VmRunType run = new VmRunType(rsvId, request.getUserData(), childToken.getAmount(), imgInfo, vmInfo, keyInfo, childToken.getInstanceIds(), macs, vlan, networkNames, netIndexes) .regardingUserRequest(request); VmRunCallback cb = new VmRunCallback(run, childToken); if (!childToken.getAddresses().isEmpty()) { final String address = childToken.getAddresses().get(0); cb.then(new SuccessCallback<VmRunResponseType>() { @Override public void apply(VmRunResponseType response) { for (VmInfo vmInfo : response.getVms()) {//TODO: this will have some funny failure characteristics try { final Address addr = Addresses.getInstance().lookup(address); final VmInstance vm = VmInstances.getInstance() .lookup(childToken.getInstanceIds().get(0)); addr.assign(vmInfo.getInstanceId(), vmInfo.getNetParams().getIpAddress()).getCallback() .dispatch(addr.getCluster()); } catch (NoSuchElementException ex) { LOG.debug(ex, ex); } } } }); } return cb; } public void run() { this.messages.run(); } }