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.cluster; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NavigableSet; import java.util.Set; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.atomic.AtomicMarkableReference; import org.apache.commons.lang.time.StopWatch; import org.apache.log4j.Logger; import org.bouncycastle.util.encoders.Base64; import com.eucalyptus.bootstrap.Component; import com.eucalyptus.component.Configurations; import com.eucalyptus.records.EventClass; import com.eucalyptus.records.EventRecord; import com.eucalyptus.records.EventType; import com.eucalyptus.util.HasName; import com.eucalyptus.util.LogUtil; import com.eucalyptus.vm.SystemState; import com.eucalyptus.vm.VmState; import com.eucalyptus.vm.SystemState.Reason; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import edu.ucsb.eucalyptus.cloud.Network; import edu.ucsb.eucalyptus.cloud.VmImageInfo; import edu.ucsb.eucalyptus.cloud.VmKeyInfo; import edu.ucsb.eucalyptus.msgs.AttachedVolume; import edu.ucsb.eucalyptus.msgs.NetworkConfigType; import edu.ucsb.eucalyptus.msgs.RunningInstancesItemType; import edu.ucsb.eucalyptus.msgs.VmTypeInfo; public class VmInstance implements HasName<VmInstance> { private static Logger LOG = Logger.getLogger(VmInstance.class); public static String DEFAULT_IP = "0.0.0.0"; public static String DEFAULT_TYPE = "m1.small"; private final String reservationId; private final int launchIndex; private final String instanceId; private final String ownerId; private final String placement; private final byte[] userData; private final List<Network> networks = Lists.newArrayList(); private final NetworkConfigType networkConfig = new NetworkConfigType(); private VmImageInfo imageInfo; private VmKeyInfo keyInfo; private VmTypeInfo vmTypeInfo; private final AtomicMarkableReference<VmState> state = new AtomicMarkableReference<VmState>(VmState.PENDING, false); private final ConcurrentSkipListSet<AttachedVolume> volumes = new ConcurrentSkipListSet<AttachedVolume>(); private final StopWatch stopWatch = new StopWatch(); private Date launchTime = new Date(); private String serviceTag; private SystemState.Reason reason; private final List<String> reasonDetails = Lists.newArrayList(); private StringBuffer consoleOutput = new StringBuffer(); private String passwordData; private Boolean privateNetwork; public VmInstance(final String reservationId, final int launchIndex, final String instanceId, final String ownerId, final String placement, final byte[] userData, final VmImageInfo imageInfo, final VmKeyInfo keyInfo, final VmTypeInfo vmTypeInfo, final List<Network> networks, final String networkIndex) { this.reservationId = reservationId; this.launchIndex = launchIndex; this.instanceId = instanceId; this.ownerId = ownerId; this.placement = placement; this.userData = userData; this.imageInfo = imageInfo; this.keyInfo = keyInfo; this.vmTypeInfo = vmTypeInfo; this.networks.addAll(networks); this.networkConfig.setMacAddress("d0:0d:" + VmInstances.asMacAddress(this.instanceId)); this.networkConfig.setIpAddress(DEFAULT_IP); this.networkConfig.setIgnoredPublicIp(DEFAULT_IP); this.networkConfig.setNetworkIndex(Integer.parseInt(networkIndex)); this.stopWatch.start(); this.updateDns(); } public void updateNetworkIndex(Integer newIndex) { if (this.getNetworkConfig().getNetworkIndex() > 0 && newIndex > 0 && (VmState.RUNNING.equals(this.getState()) || VmState.PENDING.equals(this.getState()))) { this.getNetworkConfig().setNetworkIndex(newIndex); } } public void updateAddresses(String privateAddr, String publicAddr) { this.updatePrivateAddress(privateAddr); this.updatePublicAddress(publicAddr); } public void updatePublicAddress(String publicAddr) { if (!VmInstance.DEFAULT_IP.equals(publicAddr) && !"".equals(publicAddr) && publicAddr != null) { this.getNetworkConfig().setIgnoredPublicIp(publicAddr); } } private void updateDns() { String dnsDomain = "dns-disabled"; try { dnsDomain = edu.ucsb.eucalyptus.cloud.entities.SystemConfiguration.getSystemConfiguration() .getDnsDomain(); } catch (Exception e) { } this.getNetworkConfig().updateDns(dnsDomain); } public void updatePrivateAddress(String privateAddr) { if (!VmInstance.DEFAULT_IP.equals(privateAddr) && !"".equals(privateAddr) && privateAddr != null) { this.getNetworkConfig().setIpAddress(privateAddr); } updateDns(); } public boolean clearPending() { if (this.state.isMarked() && this.getState().ordinal() > VmState.RUNNING.ordinal()) { this.state.set(this.getState(), false); VmInstances.cleanUp(this); return true; } else { this.state.set(this.getState(), false); return false; } } public VmState getState() { return this.state.getReference(); } public void setState(final VmState state) { this.setState(state, SystemState.Reason.NORMAL); } public String getReason() { if (this.reason == null) { this.reason = Reason.NORMAL; } return this.reason.name() + ": " + this.reason + (this.reasonDetails != null ? " -- " + this.reasonDetails : ""); } private int stateCounter = 0; private static String SEND_USER_TERMINATE = "SIGTERM"; private void addReasonDetail(String... extra) { for (String s : extra) { this.reasonDetails.add(s); } } public void setState(final VmState newState, SystemState.Reason reason, String... extra) { this.resetStopWatch(); VmState oldState = this.state.getReference(); if (VmState.SHUTTING_DOWN.equals(newState) && VmState.SHUTTING_DOWN.equals(oldState) && Reason.USER_TERMINATED.equals(reason)) { VmInstances.cleanUp(this); if (!this.reasonDetails.contains(SEND_USER_TERMINATE)) { this.addReasonDetail(SEND_USER_TERMINATE); } } else if (VmState.TERMINATED.equals(newState) && VmState.TERMINATED.equals(oldState)) { VmInstances.getInstance().deregister(this.getName()); } else if (!this.getState().equals(newState)) { if (Reason.APPEND.equals(reason)) { reason = this.reason; } this.addReasonDetail(extra); LOG.info(String.format("%s state change: %s -> %s", this.getInstanceId(), this.getState(), newState)); this.reason = reason; if (this.state.isMarked() && VmState.PENDING.equals(this.getState())) { if (VmState.SHUTTING_DOWN.equals(newState) || VmState.PENDING.equals(newState)) { this.state.set(newState, true); } else { this.state.set(newState, false); } } else if (this.state.isMarked() && VmState.SHUTTING_DOWN.equals(this.getState())) { LOG.debug("Ignoring events for state transition because the instance is marked as pending: " + oldState + " to " + this.getState()); } else if (!this.state.isMarked()) { if (oldState.ordinal() <= VmState.RUNNING.ordinal() && newState.ordinal() > VmState.RUNNING.ordinal()) { this.state.set(newState, false); VmInstances.cleanUp(this); } else if (VmState.PENDING.equals(oldState) && VmState.RUNNING.equals(newState)) { this.state.set(newState, false); } else if (VmState.TERMINATED.equals(newState) && oldState.ordinal() <= VmState.RUNNING.ordinal()) { this.state.set(newState, false); VmInstances.getInstance().disable(this.getName()); VmInstances.cleanUp(this); } else if (VmState.TERMINATED.equals(newState) && oldState.ordinal() > VmState.RUNNING.ordinal()) { this.state.set(newState, false); VmInstances.getInstance().disable(this.getName()); } else if (oldState.ordinal() > VmState.RUNNING.ordinal() && newState.ordinal() <= VmState.RUNNING.ordinal()) { this.state.set(oldState, false); VmInstances.cleanUp(this); } else if (newState.ordinal() > oldState.ordinal()) { this.state.set(newState, false); } //EventRecord.here( VmInstance.class, EventClass.VM, EventType.VM_STATE, "user="+this.getOwnerId( ), "instance="+this.getInstanceId( ), "type="+this.getVmTypeInfo( ).getName( ), "state="+ this.state.getReference( ).name( ), "details="+this.reasonDetails.toString( ) ).info(); } else { LOG.debug("Ignoring events for state transition because the instance is marked as pending: " + oldState + " to " + this.getState()); } if (!this.getState().equals(oldState)) { //EventRecord.caller( VmInstance.class, EventType.VM_STATE, this.instanceId, this.ownerId, this.state.getReference( ).name( ), this.launchTime ); } } } public String getByKey(String path) { Map<String, String> m = getMetadataMap(); if (path == null) path = ""; LOG.debug("Servicing metadata request:" + path + " -> " + m.get(path)); if (m.containsKey(path + "/")) path += "/"; return m.get(path).replaceAll("\n*\\z", ""); } private Map<String, String> getMetadataMap() { Map<String, String> m = new HashMap<String, String>(); m.put("ami-id", this.getImageInfo().getImageId()); m.put("product-codes", this.getImageInfo().getProductCodes().toString().replaceAll("[\\Q[]\\E]", "") .replaceAll(", ", "\n")); m.put("ami-launch-index", "" + this.getLaunchIndex()); m.put("ancestor-ami-ids", this.getImageInfo().getAncestorIds().toString().replaceAll("[\\Q[]\\E]", "") .replaceAll(", ", "\n")); m.put("ami-manifest-path", this.getImageInfo().getImageLocation()); m.put("hostname", this.getPublicAddress()); m.put("instance-id", this.getInstanceId()); m.put("instance-type", this.getVmTypeInfo().getName()); if (Component.dns.isLocal()) { m.put("local-hostname", this.getNetworkConfig().getPrivateDnsName()); } else { m.put("local-hostname", this.getNetworkConfig().getIpAddress()); } m.put("local-ipv4", this.getNetworkConfig().getIpAddress()); if (Component.dns.isLocal()) { m.put("public-hostname", this.getNetworkConfig().getPublicDnsName()); } else { m.put("public-hostname", this.getPublicAddress()); } m.put("public-ipv4", this.getPublicAddress()); m.put("reservation-id", this.getReservationId()); m.put("kernel-id", this.getImageInfo().getKernelId()); if (this.getImageInfo().getRamdiskId() != null) { m.put("ramdisk-id", this.getImageInfo().getRamdiskId()); } m.put("security-groups", this.getNetworkNames().toString().replaceAll("[\\Q[]\\E]", "").replaceAll(", ", "\n")); m.put("block-device-mapping/", "emi\nephemeral\nephemeral0\nroot\nswap"); m.put("block-device-mapping/emi", "sda1"); m.put("block-device-mapping/ami", "sda1"); m.put("block-device-mapping/ephemeral", "sda2"); m.put("block-device-mapping/ephemeral0", "sda2"); m.put("block-device-mapping/swap", "sda3"); m.put("block-device-mapping/root", "/dev/sda1"); m.put("public-keys/", "0=" + this.getKeyInfo().getName()); m.put("public-keys/0", "openssh-key"); m.put("public-keys/0/", "openssh-key"); m.put("public-keys/0/openssh-key", this.getKeyInfo().getValue()); m.put("placement/", "availability-zone"); m.put("placement/availability-zone", this.getPlacement()); String dir = ""; for (String entry : m.keySet()) { if ((entry.contains("/") && !entry.endsWith("/")) || ("ramdisk-id".equals(entry) && this.getImageInfo().getRamdiskId() == null)) { continue; } dir += entry + "\n"; } m.put("", dir); return m; } @Override public int compareTo(final VmInstance that) { return this.getName().compareTo(that.getName()); } public synchronized long resetStopWatch() { this.stopWatch.stop(); long ret = this.stopWatch.getTime(); this.stopWatch.reset(); this.stopWatch.start(); return ret; } public synchronized long getSplitTime() { this.stopWatch.split(); long ret = this.stopWatch.getSplitTime(); this.stopWatch.unsplit(); return ret; } public RunningInstancesItemType getAsRunningInstanceItemType(boolean dns) { RunningInstancesItemType runningInstance = new RunningInstancesItemType(); runningInstance.setAmiLaunchIndex(Integer.toString(this.launchIndex)); runningInstance.setStateCode(Integer.toString(this.state.getReference().getCode())); runningInstance.setStateName(this.state.getReference().getName()); runningInstance.setInstanceId(this.instanceId); runningInstance.setImageId(this.imageInfo.getImageId()); runningInstance.setKernel(this.imageInfo.getKernelId()); runningInstance.setRamdisk(this.imageInfo.getRamdiskId()); runningInstance.setProductCodes(this.imageInfo.getProductCodes()); if (dns) { runningInstance.setDnsName(this.getNetworkConfig().getPublicDnsName()); runningInstance.setPrivateDnsName(this.getNetworkConfig().getPrivateDnsName()); } else { runningInstance.setPrivateDnsName(this.getNetworkConfig().getIpAddress()); if (!VmInstance.DEFAULT_IP.equals(this.getPublicAddress())) { runningInstance.setDnsName(this.getPublicAddress()); } else { runningInstance.setDnsName(this.getNetworkConfig().getIpAddress()); } } runningInstance.setReason(this.getReason()); if (this.getKeyInfo() != null) runningInstance.setKeyName(this.getKeyInfo().getName()); else runningInstance.setKeyName(""); runningInstance.setInstanceType(this.getVmTypeInfo().getName()); runningInstance.setPlacement(this.placement); runningInstance.setLaunchTime(this.launchTime); return runningInstance; } public void setServiceTag(String serviceTag) { this.serviceTag = serviceTag; } public String getServiceTag() { return serviceTag; } public boolean hasPublicAddress() { NetworkConfigType conf = getNetworkConfig(); return conf != null && !(DEFAULT_IP.equals(conf.getIgnoredPublicIp()) || conf.getIpAddress().equals(conf.getIgnoredPublicIp())); } public String getName() { return this.instanceId; } public void setLaunchTime(final Date launchTime) { this.launchTime = launchTime; } public String getReservationId() { return reservationId; } public String getInstanceId() { return instanceId; } public String getOwnerId() { return ownerId; } public int getLaunchIndex() { return launchIndex; } public String getPlacement() { return placement; } public Date getLaunchTime() { return launchTime; } public byte[] getUserData() { return userData; } public VmKeyInfo getKeyInfo() { return keyInfo; } public String getConsoleOutputString() { return new String(Base64.encode(this.consoleOutput.toString().getBytes())); } public StringBuffer getConsoleOutput() { return this.consoleOutput; } public void setConsoleOutput(final StringBuffer consoleOutput) { this.consoleOutput = consoleOutput; if (this.passwordData == null) { String tempCo = consoleOutput.toString().replaceAll("[\r\n]*", ""); if (tempCo.matches(".*<Password>[\\w=+/]*</Password>.*")) { this.passwordData = tempCo.replaceAll(".*<Password>", "").replaceAll("</Password>.*", ""); } } } public void setKeyInfo(final VmKeyInfo keyInfo) { this.keyInfo = keyInfo; } public VmTypeInfo getVmTypeInfo() { return vmTypeInfo; } public void setVmTypeInfo(final VmTypeInfo vmTypeInfo) { this.vmTypeInfo = vmTypeInfo; } public List<Network> getNetworks() { return networks; } public List<String> getNetworkNames() { List<String> nets = new ArrayList<String>(); for (Network net : this.getNetworks()) nets.add(net.getNetworkName()); return nets; } public String getPrivateAddress() { return networkConfig.getIpAddress(); } public String getPublicAddress() { return networkConfig.getIgnoredPublicIp(); } public NetworkConfigType getNetworkConfig() { return networkConfig; } public VmImageInfo getImageInfo() { return imageInfo; } public void setImageInfo(final VmImageInfo imageInfo) { this.imageInfo = imageInfo; } public void updateVolumeState(final String volumeId, String state) { AttachedVolume v = Iterables.find(this.volumes, new Predicate<AttachedVolume>() { @Override public boolean apply(AttachedVolume arg0) { return arg0.getVolumeId().equals(volumeId); } }); v.setStatus(state); } public NavigableSet<AttachedVolume> getVolumes() { return this.volumes; } public void setVolumes(final List<AttachedVolume> newVolumes) { for (AttachedVolume vol : newVolumes) { vol.setInstanceId(this.getInstanceId()); vol.setStatus("attached"); } Set<AttachedVolume> oldVolumes = Sets.newHashSet(this.getVolumes()); this.volumes.retainAll(volumes); this.volumes.addAll(volumes); for (AttachedVolume v : oldVolumes) { if ("attaching".equals(v.getStatus()) && !this.volumes.contains(v)) { this.volumes.add(v); } } } public String getPasswordData() { return this.passwordData; } public void setPasswordData(String passwordData) { this.passwordData = passwordData; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; VmInstance vmInstance = (VmInstance) o; if (!instanceId.equals(vmInstance.instanceId)) return false; return true; } @Override public int hashCode() { return instanceId.hashCode(); } @Override public String toString() { return String.format( "VmInstance [imageInfo=%s, instanceId=%s, keyInfo=%s, launchIndex=%s, launchTime=%s, networkConfig=%s, networks=%s, ownerId=%s, placement=%s, privateNetwork=%s, reason=%s, reservationId=%s, state=%s, stopWatch=%s, userData=%s, vmTypeInfo=%s, volumes=%s]", this.imageInfo, this.instanceId, this.keyInfo, this.launchIndex, this.launchTime, this.networkConfig, this.networks, this.ownerId, this.placement, this.privateNetwork, this.reason, this.reservationId, this.state, this.stopWatch, this.userData, this.vmTypeInfo, this.volumes); } public int getNetworkIndex() { return this.getNetworkConfig().getNetworkIndex(); } }