1
/*******************************************************************************
2
*Copyright (c) 2009 Eucalyptus Systems, Inc.
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation, only version 3 of the License.
9
* This file is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* You should have received a copy of the GNU General Public License along
15
* with this program. If not, see <http://www.gnu.org/licenses/>.
17
* Please contact Eucalyptus Systems, Inc., 130 Castilian
18
* Dr., Goleta, CA 93101 USA or visit <http://www.eucalyptus.com/licenses/>
19
* if you need additional information or have any questions.
21
* This file may incorporate work covered under the following copyright and
24
* Software License Agreement (BSD License)
26
* Copyright (c) 2008, Regents of the University of California
27
* All rights reserved.
29
* Redistribution and use of this software in source and binary forms, with
30
* or without modification, are permitted provided that the following
33
* Redistributions of source code must retain the above copyright notice,
34
* this list of conditions and the following disclaimer.
36
* Redistributions in binary form must reproduce the above copyright
37
* notice, this list of conditions and the following disclaimer in the
38
* documentation and/or other materials provided with the distribution.
40
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
41
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
42
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
43
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
44
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
46
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
47
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
48
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
49
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
50
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. USERS OF
51
* THIS SOFTWARE ACKNOWLEDGE THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE
52
* LICENSED MATERIAL, COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS
53
* SOFTWARE, AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
54
* IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, SANTA
55
* BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, WHICH IN
56
* THE REGENTS’ DISCRETION MAY INCLUDE, WITHOUT LIMITATION, REPLACEMENT
57
* OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO IDENTIFIED, OR
58
* WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT NEEDED TO COMPLY WITH
59
* ANY SUCH LICENSES OR RIGHTS.
60
*******************************************************************************/
62
* Author: chris grzegorczyk <grze@eucalyptus.com>
65
package com.eucalyptus.cluster;
67
import java.util.ArrayList;
68
import java.util.Date;
69
import java.util.HashMap;
70
import java.util.List;
72
import java.util.NavigableSet;
74
import java.util.concurrent.ConcurrentSkipListSet;
75
import java.util.concurrent.atomic.AtomicMarkableReference;
76
import org.apache.commons.lang.time.StopWatch;
77
import org.apache.log4j.Logger;
78
import com.eucalyptus.bootstrap.Component;
79
import com.eucalyptus.component.Configurations;
80
import com.eucalyptus.records.EventClass;
81
import com.eucalyptus.records.EventRecord;
82
import com.eucalyptus.records.EventType;
83
import com.eucalyptus.util.HasName;
84
import com.eucalyptus.util.LogUtil;
85
import com.eucalyptus.vm.SystemState;
86
import com.eucalyptus.vm.VmState;
87
import com.eucalyptus.vm.SystemState.Reason;
88
import com.google.common.base.Predicate;
89
import com.google.common.collect.Iterables;
90
import com.google.common.collect.Lists;
91
import com.google.common.collect.Sets;
92
import edu.ucsb.eucalyptus.cloud.Network;
93
import edu.ucsb.eucalyptus.cloud.VmImageInfo;
94
import edu.ucsb.eucalyptus.cloud.VmKeyInfo;
95
import edu.ucsb.eucalyptus.msgs.AttachedVolume;
96
import edu.ucsb.eucalyptus.msgs.NetworkConfigType;
97
import edu.ucsb.eucalyptus.msgs.RunningInstancesItemType;
98
import edu.ucsb.eucalyptus.msgs.VmTypeInfo;
100
public class VmInstance implements HasName {
101
private static Logger LOG = Logger.getLogger( VmInstance.class );
102
public static String DEFAULT_IP = "0.0.0.0";
103
public static String DEFAULT_TYPE = "m1.small";
105
private final String reservationId;
106
private final int launchIndex;
107
private final String instanceId;
108
private final String ownerId;
109
private final String placement;
110
private final String userData;
111
private final List<Network> networks = Lists.newArrayList( );
112
private final NetworkConfigType networkConfig = new NetworkConfigType( );
113
private VmImageInfo imageInfo;
114
private VmKeyInfo keyInfo;
115
private VmTypeInfo vmTypeInfo;
117
private final AtomicMarkableReference<VmState> state = new AtomicMarkableReference<VmState>( VmState.PENDING, false );
118
private final ConcurrentSkipListSet<AttachedVolume> volumes = new ConcurrentSkipListSet<AttachedVolume>( );
119
private final StopWatch stopWatch = new StopWatch( );
121
private Date launchTime = new Date( );
122
private String serviceTag;
123
private SystemState.Reason reason;
124
private final List<String> reasonDetails = Lists.newArrayList( );
125
private StringBuffer consoleOutput = new StringBuffer( );
126
private String passwordData;
127
private Boolean privateNetwork;
129
public VmInstance( final String reservationId, final int launchIndex, final String instanceId, final String ownerId, final String placement,
130
final String userData, final VmImageInfo imageInfo, final VmKeyInfo keyInfo, final VmTypeInfo vmTypeInfo, final List<Network> networks,
131
final String networkIndex ) {
132
this.reservationId = reservationId;
133
this.launchIndex = launchIndex;
134
this.instanceId = instanceId;
135
this.ownerId = ownerId;
136
this.placement = placement;
137
this.userData = userData;
138
this.imageInfo = imageInfo;
139
this.keyInfo = keyInfo;
140
this.vmTypeInfo = vmTypeInfo;
141
this.networks.addAll( networks );
142
this.networkConfig.setMacAddress( "d0:0d:" + VmInstances.asMacAddress( this.instanceId ) );
143
this.networkConfig.setIpAddress( DEFAULT_IP );
144
this.networkConfig.setIgnoredPublicIp( DEFAULT_IP );
145
this.networkConfig.setNetworkIndex( Integer.parseInt( networkIndex ) );
146
this.stopWatch.start( );
150
public void updateNetworkIndex( Integer newIndex ) {
151
if ( this.getNetworkConfig( ).getNetworkIndex( ) > 0 && newIndex > 0
152
&& ( VmState.RUNNING.equals( this.getState( ) ) || VmState.PENDING.equals( this.getState( ) ) ) ) {
153
this.getNetworkConfig( ).setNetworkIndex( newIndex );
157
public void updateAddresses( String privateAddr, String publicAddr ) {
158
this.updatePrivateAddress( privateAddr );
159
this.updatePublicAddress( publicAddr );
162
public void updatePublicAddress( String publicAddr ) {
163
if ( !VmInstance.DEFAULT_IP.equals( publicAddr ) && !"".equals( publicAddr )
164
&& publicAddr != null ) {
165
this.getNetworkConfig( ).setIgnoredPublicIp( publicAddr );
169
private void updateDns( ) {
170
String dnsDomain = "dns-disabled";
172
dnsDomain = edu.ucsb.eucalyptus.cloud.entities.SystemConfiguration.getSystemConfiguration( ).getDnsDomain( );
173
} catch ( Exception e ) {}
174
this.getNetworkConfig( ).updateDns( dnsDomain );
177
public void updatePrivateAddress( String privateAddr ) {
178
if ( !VmInstance.DEFAULT_IP.equals( privateAddr ) && !"".equals( privateAddr ) && privateAddr != null ) {
179
this.getNetworkConfig( ).setIpAddress( privateAddr );
184
public boolean clearPending( ) {
185
if ( this.state.isMarked( ) && this.getState( ).ordinal( ) > VmState.RUNNING.ordinal( ) ) {
186
this.state.set( this.getState( ), false );
187
VmInstances.cleanUp( this );
190
this.state.set( this.getState( ), false );
195
public VmState getState( ) {
196
return this.state.getReference( );
199
public void setState( final VmState state ) {
200
this.setState( state, SystemState.Reason.NORMAL );
203
public String getReason( ) {
204
if ( this.reason == null ) {
205
this.reason = Reason.NORMAL;
207
return this.reason.name( ) + ": " + this.reason + ( this.reasonDetails != null ? " -- " + this.reasonDetails : "" );
210
private int stateCounter = 0;
211
private static String SEND_USER_TERMINATE = "SIGTERM";
213
private void addReasonDetail( String... extra ) {
214
for ( String s : extra ) {
215
this.reasonDetails.add( s );
219
public void setState( final VmState newState, SystemState.Reason reason, String... extra ) {
220
this.resetStopWatch( );
221
VmState oldState = this.state.getReference( );
222
if ( VmState.SHUTTING_DOWN.equals( newState ) && VmState.SHUTTING_DOWN.equals( oldState ) && Reason.USER_TERMINATED.equals( reason ) ) {
223
VmInstances.cleanUp( this );
224
if ( !this.reasonDetails.contains( SEND_USER_TERMINATE ) ) {
225
this.addReasonDetail( SEND_USER_TERMINATE );
227
} else if ( VmState.TERMINATED.equals( newState ) && VmState.TERMINATED.equals( oldState ) ) {
228
VmInstances.getInstance( ).deregister( this.getName( ) );
229
} else if ( !this.getState( ).equals( newState ) ) {
230
if ( Reason.APPEND.equals( reason ) ) {
231
reason = this.reason;
233
this.addReasonDetail( extra );
234
LOG.info( String.format( "%s state change: %s -> %s", this.getInstanceId( ), this.getState( ), newState ) );
235
this.reason = reason;
236
if ( this.state.isMarked( ) && VmState.PENDING.equals( this.getState( ) ) ) {
237
if ( VmState.SHUTTING_DOWN.equals( newState ) || VmState.PENDING.equals( newState ) ) {
238
this.state.set( newState, true );
240
this.state.set( newState, false );
242
} else if ( this.state.isMarked( ) && VmState.SHUTTING_DOWN.equals( this.getState( ) ) ) {
243
LOG.debug( "Ignoring events for state transition because the instance is marked as pending: " + oldState + " to " + this.getState( ) );
244
} else if ( !this.state.isMarked( ) ) {
245
if ( oldState.ordinal( ) <= VmState.RUNNING.ordinal( ) && newState.ordinal( ) > VmState.RUNNING.ordinal( ) ) {
246
this.state.set( newState, false );
247
VmInstances.cleanUp( this );
248
} else if ( VmState.PENDING.equals( oldState ) && VmState.RUNNING.equals( newState ) ) {
249
this.state.set( newState, false );
250
} else if ( VmState.TERMINATED.equals( newState ) && oldState.ordinal( ) <= VmState.RUNNING.ordinal( ) ) {
251
this.state.set( newState, false );
252
VmInstances.getInstance( ).disable( this.getName( ) );
253
VmInstances.cleanUp( this );
254
} else if ( VmState.TERMINATED.equals( newState ) && oldState.ordinal( ) > VmState.RUNNING.ordinal( ) ) {
255
this.state.set( newState, false );
256
VmInstances.getInstance( ).disable( this.getName( ) );
257
} else if ( oldState.ordinal( ) > VmState.RUNNING.ordinal( ) && newState.ordinal( ) <= VmState.RUNNING.ordinal( ) ) {
258
this.state.set( oldState, false );
259
VmInstances.cleanUp( this );
260
} else if ( newState.ordinal( ) > oldState.ordinal( ) ) {
261
this.state.set( newState, false );
263
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();
265
LOG.debug( "Ignoring events for state transition because the instance is marked as pending: " + oldState + " to " + this.getState( ) );
267
if ( !this.getState( ).equals( oldState ) ) {
268
EventRecord.caller( VmInstance.class, EventType.VM_STATE, this.instanceId, this.ownerId, this.state.getReference( ).name( ), this.launchTime );
273
public String getByKey( String path ) {
274
Map<String, String> m = getMetadataMap( );
275
if ( path == null ) path = "";
276
LOG.debug( "Servicing metadata request:" + path + " -> " + m.get( path ) );
277
if ( m.containsKey( path + "/" ) ) path += "/";
278
return m.get( path ).replaceAll( "\n*\\z", "" );
281
private Map<String, String> getMetadataMap( ) {
282
Map<String, String> m = new HashMap<String, String>( );
283
m.put( "ami-id", this.getImageInfo( ).getImageId( ) );
284
m.put( "product-codes", this.getImageInfo( ).getProductCodes( ).toString( ).replaceAll( "[\\Q[]\\E]", "" ).replaceAll( ", ", "\n" ) );
285
m.put( "ami-launch-index", "" + this.getLaunchIndex( ) );
286
m.put( "ancestor-ami-ids", this.getImageInfo( ).getAncestorIds( ).toString( ).replaceAll( "[\\Q[]\\E]", "" ).replaceAll( ", ", "\n" ) );
288
m.put( "ami-manifest-path", this.getImageInfo( ).getImageLocation( ) );
289
m.put( "hostname", this.getPublicAddress( ) );
290
m.put( "instance-id", this.getInstanceId( ) );
291
m.put( "instance-type", this.getVmTypeInfo( ).getName( ) );
292
if ( Component.dns.isLocal( ) ) {
293
m.put( "local-hostname", this.getNetworkConfig( ).getPrivateDnsName( ) );
295
m.put( "local-hostname", this.getNetworkConfig( ).getIpAddress( ) );
297
m.put( "local-ipv4", this.getNetworkConfig( ).getIpAddress( ) );
298
if ( Component.dns.isLocal( ) ) {
299
m.put( "public-hostname", this.getNetworkConfig( ).getPublicDnsName( ) );
301
m.put( "public-hostname", this.getPublicAddress( ) );
303
m.put( "public-ipv4", this.getPublicAddress( ) );
304
m.put( "reservation-id", this.getReservationId( ) );
305
m.put( "kernel-id", this.getImageInfo( ).getKernelId( ) );
306
if ( this.getImageInfo( ).getRamdiskId( ) != null ) {
307
m.put( "ramdisk-id", this.getImageInfo( ).getRamdiskId( ) );
309
m.put( "security-groups", this.getNetworkNames( ).toString( ).replaceAll( "[\\Q[]\\E]", "" ).replaceAll( ", ", "\n" ) );
311
m.put( "block-device-mapping/", "emi\nephemeral\nephemeral0\nroot\nswap" );
312
m.put( "block-device-mapping/emi", "sda1" );
313
m.put( "block-device-mapping/ami", "sda1" );
314
m.put( "block-device-mapping/ephemeral", "sda2" );
315
m.put( "block-device-mapping/ephemeral0", "sda2" );
316
m.put( "block-device-mapping/swap", "sda3" );
317
m.put( "block-device-mapping/root", "/dev/sda1" );
319
m.put( "public-keys/", "0=" + this.getKeyInfo( ).getName( ) );
320
m.put( "public-keys/0", "openssh-key" );
321
m.put( "public-keys/0/", "openssh-key" );
322
m.put( "public-keys/0/openssh-key", this.getKeyInfo( ).getValue( ) );
324
m.put( "placement/", "availability-zone" );
325
m.put( "placement/availability-zone", this.getPlacement( ) );
327
for ( String entry : m.keySet( ) ) {
328
if ( entry.contains( "/" ) && !entry.endsWith( "/" ) ) continue;
335
public int compareTo( final Object o ) {
336
VmInstance that = ( VmInstance ) o;
337
return this.getName( ).compareTo( that.getName( ) );
340
public synchronized long resetStopWatch( ) {
341
this.stopWatch.stop( );
342
long ret = this.stopWatch.getTime( );
343
this.stopWatch.reset( );
344
this.stopWatch.start( );
348
public synchronized long getSplitTime( ) {
349
this.stopWatch.split( );
350
long ret = this.stopWatch.getSplitTime( );
351
this.stopWatch.unsplit( );
355
public RunningInstancesItemType getAsRunningInstanceItemType( boolean dns ) {
356
RunningInstancesItemType runningInstance = new RunningInstancesItemType( );
358
runningInstance.setAmiLaunchIndex( Integer.toString( this.launchIndex ) );
359
runningInstance.setStateCode( Integer.toString( this.state.getReference( ).getCode( ) ) );
360
runningInstance.setStateName( this.state.getReference( ).getName( ) );
361
runningInstance.setInstanceId( this.instanceId );
362
runningInstance.setImageId( this.imageInfo.getImageId( ) );
363
runningInstance.setKernel( this.imageInfo.getKernelId( ) );
364
runningInstance.setRamdisk( this.imageInfo.getRamdiskId( ) );
365
runningInstance.setProductCodes( this.imageInfo.getProductCodes( ) );
368
runningInstance.setDnsName( this.getNetworkConfig( ).getPublicDnsName( ) );
369
runningInstance.setPrivateDnsName( this.getNetworkConfig( ).getPrivateDnsName( ) );
371
runningInstance.setPrivateDnsName( this.getNetworkConfig( ).getIpAddress( ) );
372
if ( !VmInstance.DEFAULT_IP.equals( this.getPublicAddress( ) ) ) {
373
runningInstance.setDnsName( this.getPublicAddress( ) );
375
runningInstance.setDnsName( this.getNetworkConfig( ).getIpAddress( ) );
378
runningInstance.setReason( this.getReason( ) );
380
if ( this.getKeyInfo( ) != null )
381
runningInstance.setKeyName( this.getKeyInfo( ).getName( ) );
382
else runningInstance.setKeyName( "" );
384
runningInstance.setInstanceType( this.getVmTypeInfo( ).getName( ) );
385
runningInstance.setPlacement( this.placement );
387
runningInstance.setLaunchTime( this.launchTime );
389
return runningInstance;
392
public void setServiceTag( String serviceTag ) {
393
this.serviceTag = serviceTag;
396
public String getServiceTag( ) {
400
public boolean hasPublicAddress( ) {
401
NetworkConfigType conf = getNetworkConfig( );
402
return conf != null && !( DEFAULT_IP.equals( conf.getIgnoredPublicIp( ) ) || conf.getIpAddress( ).equals( conf.getIgnoredPublicIp( ) ) );
405
public String getName( ) {
406
return this.instanceId;
409
public void setLaunchTime( final Date launchTime ) {
410
this.launchTime = launchTime;
413
public String getReservationId( ) {
414
return reservationId;
417
public String getInstanceId( ) {
421
public String getOwnerId( ) {
425
public int getLaunchIndex( ) {
429
public String getPlacement( ) {
433
public Date getLaunchTime( ) {
437
public String getUserData( ) {
441
public VmKeyInfo getKeyInfo( ) {
445
public StringBuffer getConsoleOutput( ) {
446
return consoleOutput;
449
public void setConsoleOutput( final StringBuffer consoleOutput ) {
450
this.consoleOutput = consoleOutput;
451
if ( this.passwordData == null ) {
452
String tempCo = consoleOutput.toString( ).replaceAll( "[\r\n]*", "" );
453
if ( tempCo.matches( ".*<Password>[\\w=+/]*</Password>.*" ) ) {
454
this.passwordData = tempCo.replaceAll( ".*<Password>", "" ).replaceAll( "</Password>.*", "" );
459
public void setKeyInfo( final VmKeyInfo keyInfo ) {
460
this.keyInfo = keyInfo;
463
public VmTypeInfo getVmTypeInfo( ) {
467
public void setVmTypeInfo( final VmTypeInfo vmTypeInfo ) {
468
this.vmTypeInfo = vmTypeInfo;
471
public List<Network> getNetworks( ) {
475
public List<String> getNetworkNames( ) {
476
List<String> nets = new ArrayList<String>( );
477
for ( Network net : this.getNetworks( ) )
478
nets.add( net.getNetworkName( ) );
482
public String getPrivateAddress( ) {
483
return networkConfig.getIpAddress( );
486
public String getPublicAddress( ) {
487
return networkConfig.getIgnoredPublicIp( );
490
public NetworkConfigType getNetworkConfig( ) {
491
return networkConfig;
494
public VmImageInfo getImageInfo( ) {
498
public void setImageInfo( final VmImageInfo imageInfo ) {
499
this.imageInfo = imageInfo;
502
public void updateVolumeState( final String volumeId, String state ) {
503
AttachedVolume v = Iterables.find( this.volumes, new Predicate<AttachedVolume>( ) {
505
public boolean apply( AttachedVolume arg0 ) {
506
return arg0.getVolumeId( ).equals( volumeId );
509
v.setStatus( state );
512
public NavigableSet<AttachedVolume> getVolumes( ) {
516
public void setVolumes( final List<AttachedVolume> newVolumes ) {
517
for ( AttachedVolume vol : newVolumes ) {
518
vol.setInstanceId( this.getInstanceId( ) );
519
vol.setStatus( "attached" );
521
Set<AttachedVolume> oldVolumes = Sets.newHashSet( this.getVolumes( ) );
522
this.volumes.retainAll( volumes );
523
this.volumes.addAll( volumes );
524
for ( AttachedVolume v : oldVolumes ) {
525
if ( "attaching".equals( v.getStatus( ) ) && !this.volumes.contains( v ) ) {
526
this.volumes.add( v );
531
public String getPasswordData( ) {
532
return this.passwordData;
535
public void setPasswordData( String passwordData ) {
536
this.passwordData = passwordData;
540
public boolean equals( Object o ) {
541
if ( this == o ) return true;
542
if ( o == null || getClass( ) != o.getClass( ) ) return false;
544
VmInstance vmInstance = ( VmInstance ) o;
546
if ( !instanceId.equals( vmInstance.instanceId ) ) return false;
552
public int hashCode( ) {
553
return instanceId.hashCode( );
557
public String toString( ) {
560
"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]",
561
this.imageInfo, this.instanceId, this.keyInfo, this.launchIndex, this.launchTime, this.networkConfig, this.networks, this.ownerId,
562
this.placement, this.privateNetwork, this.reason, this.reservationId, this.state, this.stopWatch, this.userData, this.vmTypeInfo,
566
public int getNetworkIndex( ) {
567
return this.getNetworkConfig( ).getNetworkIndex( );