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
*******************************************************************************/
63
* Author: chris grzegorczyk <grze@eucalyptus.com>
66
package edu.ucsb.eucalyptus.cloud.ws;
68
import java.util.ArrayList;
69
import java.util.HashMap;
70
import java.util.List;
72
import java.util.NoSuchElementException;
73
import org.apache.log4j.Logger;
74
import com.eucalyptus.auth.crypto.Crypto;
75
import com.eucalyptus.cluster.Cluster;
76
import com.eucalyptus.cluster.Clusters;
77
import com.eucalyptus.cluster.callback.VolumeAttachCallback;
78
import com.eucalyptus.cluster.callback.VolumeDetachCallback;
79
import com.eucalyptus.config.Configuration;
80
import com.eucalyptus.config.StorageControllerConfiguration;
81
import com.eucalyptus.entities.EntityWrapper;
82
import com.eucalyptus.images.util.StorageUtil;
83
import com.eucalyptus.util.EucalyptusCloudException;
84
import com.google.common.collect.Lists;
85
import edu.ucsb.eucalyptus.cloud.cluster.VmInstance;
86
import edu.ucsb.eucalyptus.cloud.cluster.VmInstances;
87
import edu.ucsb.eucalyptus.cloud.state.Snapshot;
88
import edu.ucsb.eucalyptus.cloud.state.State;
89
import edu.ucsb.eucalyptus.cloud.state.Volume;
90
import edu.ucsb.eucalyptus.msgs.AttachVolumeResponseType;
91
import edu.ucsb.eucalyptus.msgs.AttachVolumeType;
92
import edu.ucsb.eucalyptus.msgs.AttachedVolume;
93
import edu.ucsb.eucalyptus.msgs.CreateStorageVolumeResponseType;
94
import edu.ucsb.eucalyptus.msgs.CreateStorageVolumeType;
95
import edu.ucsb.eucalyptus.msgs.CreateVolumeResponseType;
96
import edu.ucsb.eucalyptus.msgs.CreateVolumeType;
97
import edu.ucsb.eucalyptus.msgs.DeleteStorageVolumeResponseType;
98
import edu.ucsb.eucalyptus.msgs.DeleteStorageVolumeType;
99
import edu.ucsb.eucalyptus.msgs.DeleteVolumeResponseType;
100
import edu.ucsb.eucalyptus.msgs.DeleteVolumeType;
101
import edu.ucsb.eucalyptus.msgs.DescribeVolumesResponseType;
102
import edu.ucsb.eucalyptus.msgs.DescribeVolumesType;
103
import edu.ucsb.eucalyptus.msgs.DetachVolumeResponseType;
104
import edu.ucsb.eucalyptus.msgs.DetachVolumeType;
106
public class VolumeManager {
107
static String PERSISTENCE_CONTEXT = "eucalyptus_images";
109
private static String ID_PREFIX = "vol";
110
private static Logger LOG = Logger.getLogger( VolumeManager.class );
112
public static EntityWrapper<Volume> getEntityWrapper() {
113
return new EntityWrapper<Volume>( PERSISTENCE_CONTEXT );
116
public CreateVolumeResponseType CreateVolume( CreateVolumeType request ) throws EucalyptusCloudException {
117
if( ( request.getSnapshotId() == null && request.getSize() == null ) ) {
118
throw new EucalyptusCloudException( "One of size or snapshotId is required as a parameter." );
121
Configuration.getClusterConfiguration( request.getAvailabilityZone( ) );
122
} catch ( Exception e ) {
123
throw new EucalyptusCloudException( "Zone does not exist: " + request.getAvailabilityZone(), e );
125
StorageControllerConfiguration sc;
127
sc = Configuration.getStorageControllerConfiguration( request.getAvailabilityZone( ) );
128
} catch ( Exception e ) {
129
throw new EucalyptusCloudException("Storage services are not available for the requested availability zone.", e );
131
EntityWrapper<Volume> db = VolumeManager.getEntityWrapper();
132
if ( request.getSnapshotId() != null ) {
133
String userName = request.isAdministrator() ? null : request.getUserId();
135
db.recast( Snapshot.class ).getUnique( Snapshot.named( userName, request.getSnapshotId() ) );
136
} catch ( EucalyptusCloudException e ) {
139
throw new EucalyptusCloudException( "Snapshot does not exist: " + request.getSnapshotId() );
143
Volume newVol = null;
145
newId = Crypto.generateId( request.getUserId(), ID_PREFIX );
147
db.getUnique( Volume.named( null, newId ) );
148
} catch ( EucalyptusCloudException e ) {
150
newVol = new Volume( request.getUserId(), newId, new Integer( request.getSize() != null ? request.getSize() : "-1" ),
151
request.getAvailabilityZone(), request.getSnapshotId() );
155
} catch ( Throwable e1 ) {
157
db = VolumeManager.getEntityWrapper();
161
newVol.setState( State.GENERATING );
163
CreateStorageVolumeType req = new CreateStorageVolumeType( newId, request.getSize( ), request.getSnapshotId( ) );
164
req.setUserId( request.getUserId( ) );
165
req.setEffectiveUserId( request.getEffectiveUserId( ) );
166
StorageUtil.send( sc.getName( ), req );
167
} catch ( EucalyptusCloudException e ) {
170
db = VolumeManager.getEntityWrapper();
171
Volume d = db.getUnique( Volume.named( newVol.getUserName( ), newVol.getDisplayName( ) ) );
174
} catch ( Throwable e1 ) {
178
throw new EucalyptusCloudException( "Error while communicating with Storage Controller: CreateStorageVolume:" + e.getMessage() );
180
CreateVolumeResponseType reply = ( CreateVolumeResponseType ) request.getReply();
181
reply.setVolume( newVol.morph( new edu.ucsb.eucalyptus.msgs.Volume() ) );
185
public DeleteVolumeResponseType DeleteVolume( DeleteVolumeType request ) throws EucalyptusCloudException {
186
DeleteVolumeResponseType reply = ( DeleteVolumeResponseType ) request.getReply();
187
reply.set_return( false );
189
EntityWrapper<Volume> db = VolumeManager.getEntityWrapper();
190
String userName = request.isAdministrator() ? null : request.getUserId();
191
boolean reallyFailed = false;
193
Volume vol = db.getUnique( Volume.named( userName, request.getVolumeId() ) );
194
for( VmInstance vm : VmInstances.getInstance().listValues() ) {
195
for( AttachedVolume attachedVol : vm.getVolumes() ) {
196
if( request.getVolumeId().equals( attachedVol.getVolumeId() ) ) {
202
if( State.FAIL.equals(vol.getState() ) ) {
207
DeleteStorageVolumeResponseType scReply = StorageUtil.send( vol.getCluster( ), new DeleteStorageVolumeType( vol.getDisplayName( ) ) );
208
if( scReply.get_return( ) ) {
210
db.getSession( ).flush( );
211
if( !vol.getState( ).equals( State.ANNILATED ) ) {
212
StorageUtil.dispatchAll( new DeleteStorageVolumeType( vol.getDisplayName() ) );
217
throw new EucalyptusCloudException( "Storage Controller returned false: Contact the administrator to report the problem." );
219
} catch ( EucalyptusCloudException e ) {
228
reply.set_return( true );
232
public DescribeVolumesResponseType DescribeVolumes( DescribeVolumesType request ) throws EucalyptusCloudException {
233
DescribeVolumesResponseType reply = ( DescribeVolumesResponseType ) request.getReply();
234
EntityWrapper<Volume> db = getEntityWrapper();
236
String userName = request.isAdministrator() ? null : request.getUserId();
238
Map<String, AttachedVolume> attachedVolumes = new HashMap<String, AttachedVolume>();
239
for ( VmInstance vm : VmInstances.getInstance().listValues() ) {
240
for ( AttachedVolume av : vm.getVolumes() ) {
241
attachedVolumes.put( av.getVolumeId(), av );
245
List<Volume> volumes = db.query( Volume.ownedBy( userName ) );
246
List<Volume> describeVolumes = Lists.newArrayList( );
247
for ( Volume v : volumes ) {
248
if ( request.getVolumeSet().isEmpty() || request.getVolumeSet().contains( v.getDisplayName() ) ) {
249
describeVolumes.add( v );
253
ArrayList<edu.ucsb.eucalyptus.msgs.Volume> volumeReplyList = StorageUtil.getVolumeReply( attachedVolumes, volumes );
254
reply.getVolumeSet().addAll( volumeReplyList );
255
} catch ( Exception e ) {
256
LOG.warn( "Error getting volume information from the Storage Controller: " + e );
260
} catch (Throwable t ) {
266
public AttachVolumeResponseType AttachVolume( AttachVolumeType request ) throws EucalyptusCloudException {
267
AttachVolumeResponseType reply = ( AttachVolumeResponseType ) request.getReply();
269
if( request.getDevice( ) == null || request.getDevice().endsWith( "sda" ) ) {
270
throw new EucalyptusCloudException( "Invalid device name specified: " + request.getDevice( ) );
272
VmInstance vm = null;
274
vm = VmInstances.getInstance().lookup( request.getInstanceId() );
275
} catch ( NoSuchElementException e ) {
277
throw new EucalyptusCloudException( "Instance does not exist: " + request.getInstanceId() );
279
for( AttachedVolume attachedVol : vm.getVolumes() ) {
280
if( attachedVol.getDevice().replaceAll("unknown,requested:","").equals( request.getDevice() ) ) {
281
throw new EucalyptusCloudException( "Already have a device attached to: " + request.getDevice() );
284
Cluster cluster = null;
286
cluster = Clusters.getInstance().lookup( vm.getPlacement() );
287
} catch ( NoSuchElementException e ) {
289
throw new EucalyptusCloudException( "Cluster does not exist: " + vm.getPlacement() );
292
for ( VmInstance v : VmInstances.getInstance().listValues() ) {
293
for ( AttachedVolume vol : v.getVolumes() ) {
294
if ( vol.getVolumeId().equals( request.getVolumeId() ) ) {
295
throw new EucalyptusCloudException( "Volume already attached: " + request.getVolumeId() );
300
EntityWrapper<Volume> db = VolumeManager.getEntityWrapper();
301
String userName = request.isAdministrator() ? null : request.getUserId();
302
Volume volume = null;
304
volume = db.getUnique( Volume.named( userName, request.getVolumeId() ) );
306
} catch ( EucalyptusCloudException e ) {
309
throw new EucalyptusCloudException( "Volume does not exist: " + request.getVolumeId() );
312
if(!volume.getCluster().equals(cluster.getName())) {
313
throw new EucalyptusCloudException("Can only attach volumes in the same cluster: " + request.getVolumeId());
314
} else if("invalid".equals(volume.getRemoteDevice())) {
315
throw new EucalyptusCloudException("Volume is not yet available: " + request.getVolumeId());
317
request.setRemoteDevice( volume.getRemoteDevice() );
318
new VolumeAttachCallback( request ).dispatch( cluster );
320
AttachedVolume attachVol = new AttachedVolume( volume.getDisplayName(), vm.getInstanceId(), request.getDevice(), volume.getRemoteDevice() );
321
attachVol.setStatus( "attaching" );
322
vm.getVolumes().add( attachVol );
323
volume.setState( State.BUSY );
324
reply.setAttachedVolume( attachVol );
328
public DetachVolumeResponseType detach( DetachVolumeType request ) throws EucalyptusCloudException {
329
DetachVolumeResponseType reply = ( DetachVolumeResponseType ) request.getReply();
331
EntityWrapper<Volume> db = VolumeManager.getEntityWrapper();
332
String userName = request.isAdministrator() ? null : request.getUserId();
334
db.getUnique( Volume.named( userName, request.getVolumeId() ) );
335
} catch ( EucalyptusCloudException e ) {
338
throw new EucalyptusCloudException( "Volume does not exist: " + request.getVolumeId() );
342
VmInstance vm = null;
343
AttachedVolume volume = null;
344
for ( VmInstance v : VmInstances.getInstance().listValues() ) {
345
for ( AttachedVolume vol : v.getVolumes() ) {
346
if ( vol.getVolumeId().equals( request.getVolumeId() ) ) {
352
if ( volume == null) {
353
throw new EucalyptusCloudException( "Volume is not attached: " + request.getVolumeId() );
355
if( !vm.getInstanceId().equals( request.getInstanceId() ) && request.getInstanceId() != null && !request.getInstanceId().equals("" ) ) {
356
throw new EucalyptusCloudException( "Volume is not attached to instance: " + request.getInstanceId() );
358
if ( request.getDevice() != null && !request.getDevice().equals("") && !volume.getDevice().equals( request.getDevice() ) ) {
359
throw new EucalyptusCloudException( "Volume is not attached to device: " + request.getDevice() );
362
Cluster cluster = null;
364
cluster = Clusters.getInstance().lookup( vm.getPlacement() );
365
} catch ( NoSuchElementException e ) {
367
throw new EucalyptusCloudException( "Cluster does not exist: " + vm.getPlacement() );
370
request.setVolumeId( volume.getVolumeId() );
371
request.setRemoteDevice( volume.getRemoteDevice() );
372
request.setDevice( volume.getDevice().replaceAll("unknown,requested:","") );
373
request.setInstanceId( vm.getInstanceId() );
374
new VolumeDetachCallback( request ).dispatch( cluster );
375
volume.setStatus( "detaching" );
376
reply.setDetachedVolume( volume );