com.eucalyptus.cluster.VmInstance.java Source code

Java tutorial

Introduction

Here is the source code for com.eucalyptus.cluster.VmInstance.java

Source

/*******************************************************************************
 *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();
    }

}