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.HashMap;
69
import java.util.List;
71
import java.util.NoSuchElementException;
73
import org.apache.log4j.Logger;
75
import com.eucalyptus.auth.util.Hashes;
76
import com.eucalyptus.cluster.Cluster;
77
import com.eucalyptus.cluster.Clusters;
78
import com.eucalyptus.config.Configuration;
79
import com.eucalyptus.config.StorageControllerConfiguration;
80
import com.eucalyptus.images.util.StorageUtil;
81
import com.eucalyptus.util.EntityWrapper;
82
import com.eucalyptus.util.EucalyptusCloudException;
84
import edu.ucsb.eucalyptus.cloud.cluster.QueuedEvent;
85
import edu.ucsb.eucalyptus.cloud.cluster.VmInstance;
86
import edu.ucsb.eucalyptus.cloud.cluster.VmInstances;
87
import edu.ucsb.eucalyptus.cloud.cluster.VolumeAttachCallback;
88
import edu.ucsb.eucalyptus.cloud.cluster.VolumeDetachCallback;
89
import edu.ucsb.eucalyptus.cloud.state.Snapshot;
90
import edu.ucsb.eucalyptus.cloud.state.State;
91
import edu.ucsb.eucalyptus.cloud.state.Volume;
92
import edu.ucsb.eucalyptus.msgs.AttachVolumeResponseType;
93
import edu.ucsb.eucalyptus.msgs.AttachVolumeType;
94
import edu.ucsb.eucalyptus.msgs.AttachedVolume;
95
import edu.ucsb.eucalyptus.msgs.CreateStorageVolumeResponseType;
96
import edu.ucsb.eucalyptus.msgs.CreateStorageVolumeType;
97
import edu.ucsb.eucalyptus.msgs.CreateVolumeResponseType;
98
import edu.ucsb.eucalyptus.msgs.CreateVolumeType;
99
import edu.ucsb.eucalyptus.msgs.DeleteStorageVolumeType;
100
import edu.ucsb.eucalyptus.msgs.DeleteVolumeResponseType;
101
import edu.ucsb.eucalyptus.msgs.DeleteVolumeType;
102
import edu.ucsb.eucalyptus.msgs.DescribeVolumesResponseType;
103
import edu.ucsb.eucalyptus.msgs.DescribeVolumesType;
104
import edu.ucsb.eucalyptus.msgs.DetachVolumeResponseType;
105
import edu.ucsb.eucalyptus.msgs.DetachVolumeType;
107
public class VolumeManager {
108
static String PERSISTENCE_CONTEXT = "eucalyptus_images";
110
private static String ID_PREFIX = "vol";
111
private static Logger LOG = Logger.getLogger( VolumeManager.class );
113
public static EntityWrapper<Volume> getEntityWrapper() {
114
return new EntityWrapper<Volume>( PERSISTENCE_CONTEXT );
117
public CreateVolumeResponseType CreateVolume( CreateVolumeType request ) throws EucalyptusCloudException {
118
if( ( request.getSnapshotId() == null && request.getSize() == null ) ) {
119
throw new EucalyptusCloudException( "One of size or snapshotId is required as a parameter." );
122
Configuration.getClusterConfiguration( request.getAvailabilityZone( ) );
123
} catch ( Exception e ) {
124
throw new EucalyptusCloudException( "Zone does not exist: " + request.getAvailabilityZone(), e );
126
StorageControllerConfiguration sc;
128
sc = Configuration.getStorageControllerConfiguration( request.getAvailabilityZone( ) );
129
} catch ( Exception e ) {
130
throw new EucalyptusCloudException("Storage services are not available for the requested availability zone.", e );
132
EntityWrapper<Volume> db = VolumeManager.getEntityWrapper();
133
if ( request.getSnapshotId() != null ) {
134
String userName = request.isAdministrator() ? null : request.getUserId();
136
db.recast( Snapshot.class ).getUnique( Snapshot.named( userName, request.getSnapshotId() ) );
137
} catch ( EucalyptusCloudException e ) {
140
throw new EucalyptusCloudException( "Snapshot does not exist: " + request.getSnapshotId() );
144
Volume newVol = null;
146
newId = Hashes.generateId( request.getUserId(), ID_PREFIX );
148
db.getUnique( Volume.named( null, newId ) );
149
} catch ( EucalyptusCloudException e ) {
151
newVol = new Volume( request.getUserId(), newId, new Integer( request.getSize() != null ? request.getSize() : "-1" ),
152
request.getAvailabilityZone(), request.getSnapshotId() );
156
} catch ( Throwable e1 ) {
158
db = VolumeManager.getEntityWrapper();
162
newVol.setState( State.GENERATING );
164
CreateStorageVolumeType req = new CreateStorageVolumeType( newId, request.getSize( ), request.getSnapshotId( ) );
165
req.setUserId( request.getUserId( ) );
166
req.setEffectiveUserId( request.getEffectiveUserId( ) );
167
StorageUtil.lookup( sc.getHostName( ) ).send( req, CreateStorageVolumeResponseType.class );
168
} catch ( EucalyptusCloudException e ) {
171
db = VolumeManager.getEntityWrapper();
172
Volume d = db.getUnique( Volume.named( newVol.getUserName( ), newVol.getDisplayName( ) ) );
175
} catch ( Throwable e1 ) {
179
throw new EucalyptusCloudException( "Error while communicating with Storage Controller: CreateStorageVolume:" + e.getMessage() );
181
CreateVolumeResponseType reply = ( CreateVolumeResponseType ) request.getReply();
182
reply.setVolume( newVol.morph( new edu.ucsb.eucalyptus.msgs.Volume() ) );
186
public DeleteVolumeResponseType DeleteVolume( DeleteVolumeType request ) throws EucalyptusCloudException {
187
DeleteVolumeResponseType reply = ( DeleteVolumeResponseType ) request.getReply();
188
reply.set_return( false );
190
EntityWrapper<Volume> db = VolumeManager.getEntityWrapper();
191
String userName = request.isAdministrator() ? null : request.getUserId();
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() ) ) {
203
db.getSession( ).flush( );
204
if( !vol.getState( ).equals( State.ANNILATED ) ) {
205
StorageUtil.dispatchAll( new DeleteStorageVolumeType( vol.getDisplayName() ) );
208
} catch ( EucalyptusCloudException e ) {
213
reply.set_return( true );
217
public DescribeVolumesResponseType DescribeVolumes( DescribeVolumesType request ) throws EucalyptusCloudException {
218
DescribeVolumesResponseType reply = ( DescribeVolumesResponseType ) request.getReply();
219
EntityWrapper<Volume> db = getEntityWrapper();
221
String userName = request.isAdministrator() ? null : request.getUserId();
223
Map<String, AttachedVolume> attachedVolumes = new HashMap<String, AttachedVolume>();
224
for ( VmInstance vm : VmInstances.getInstance().listValues() ) {
225
for ( AttachedVolume av : vm.getVolumes() ) {
226
attachedVolumes.put( av.getVolumeId(), av );
230
List<Volume> volumes = db.query( Volume.ownedBy( userName ) );
232
for ( Volume v : volumes ) {
233
if ( request.getVolumeSet().isEmpty() || request.getVolumeSet().contains( v.getDisplayName() ) ) {
235
edu.ucsb.eucalyptus.msgs.Volume aVolume = StorageUtil.getVolumeReply( attachedVolumes, v );
236
reply.getVolumeSet().add( aVolume );
237
} catch ( Exception e ) {
238
LOG.warn( "Error getting volume information from the Storage Controller: " + e );
244
} catch (Throwable t ) {
250
public AttachVolumeResponseType AttachVolume( AttachVolumeType request ) throws EucalyptusCloudException {
251
AttachVolumeResponseType reply = ( AttachVolumeResponseType ) request.getReply();
253
if( request.getDevice( ) == null || request.getDevice().endsWith( "sda" ) ) {
254
throw new EucalyptusCloudException( "Invalid device name specified: " + request.getDevice( ) );
256
VmInstance vm = null;
258
vm = VmInstances.getInstance().lookup( request.getInstanceId() );
259
} catch ( NoSuchElementException e ) {
261
throw new EucalyptusCloudException( "Instance does not exist: " + request.getInstanceId() );
263
for( AttachedVolume attachedVol : vm.getVolumes() ) {
264
if( attachedVol.getDevice().replaceAll("unknown,requested:","").equals( request.getDevice() ) ) {
265
throw new EucalyptusCloudException( "Already have a device attached to: " + request.getDevice() );
268
Cluster cluster = null;
270
cluster = Clusters.getInstance().lookup( vm.getPlacement() );
271
} catch ( NoSuchElementException e ) {
273
throw new EucalyptusCloudException( "Cluster does not exist: " + vm.getPlacement() );
276
for ( VmInstance v : VmInstances.getInstance().listValues() ) {
277
for ( AttachedVolume vol : v.getVolumes() ) {
278
if ( vol.getVolumeId().equals( request.getVolumeId() ) ) {
279
throw new EucalyptusCloudException( "Volume already attached: " + request.getVolumeId() );
284
EntityWrapper<Volume> db = VolumeManager.getEntityWrapper();
285
String userName = request.isAdministrator() ? null : request.getUserId();
286
Volume volume = null;
288
volume = db.getUnique( Volume.named( userName, request.getVolumeId() ) );
290
} catch ( EucalyptusCloudException e ) {
293
throw new EucalyptusCloudException( "Volume does not exist: " + request.getVolumeId() );
296
if(!volume.getCluster().equals(cluster.getName())) {
297
throw new EucalyptusCloudException("Can only attach volumes in the same cluster: " + request.getVolumeId());
298
} else if("invalid".equals(volume.getRemoteDevice())) {
299
throw new EucalyptusCloudException("Volume is not yet available: " + request.getVolumeId());
301
request.setRemoteDevice( volume.getRemoteDevice() );
302
QueuedEvent<AttachVolumeType> event = QueuedEvent.make( new VolumeAttachCallback( ), request );
303
cluster.getMessageQueue().enqueue( event );
305
AttachedVolume attachVol = new AttachedVolume( volume.getDisplayName(), vm.getInstanceId(), request.getDevice(), volume.getRemoteDevice() );
306
vm.getVolumes().add( attachVol );
307
volume.setState( State.BUSY );
308
reply.setAttachedVolume( attachVol );
312
public DetachVolumeResponseType detach( DetachVolumeType request ) throws EucalyptusCloudException {
313
DetachVolumeResponseType reply = ( DetachVolumeResponseType ) request.getReply();
315
EntityWrapper<Volume> db = VolumeManager.getEntityWrapper();
316
String userName = request.isAdministrator() ? null : request.getUserId();
318
db.getUnique( Volume.named( userName, request.getVolumeId() ) );
319
} catch ( EucalyptusCloudException e ) {
322
throw new EucalyptusCloudException( "Volume does not exist: " + request.getVolumeId() );
326
VmInstance vm = null;
327
AttachedVolume volume = null;
328
for ( VmInstance v : VmInstances.getInstance().listValues() ) {
329
for ( AttachedVolume vol : v.getVolumes() ) {
330
if ( vol.getVolumeId().equals( request.getVolumeId() ) ) {
336
if ( volume == null) {
337
throw new EucalyptusCloudException( "Volume is not attached: " + request.getVolumeId() );
339
if( !vm.getInstanceId().equals( request.getInstanceId() ) && request.getInstanceId() != null && !request.getInstanceId().equals("" ) ) {
340
throw new EucalyptusCloudException( "Volume is not attached to instance: " + request.getInstanceId() );
342
if ( request.getDevice() != null && !request.getDevice().equals("") && !volume.getDevice().equals( request.getDevice() ) ) {
343
throw new EucalyptusCloudException( "Volume is not attached to device: " + request.getDevice() );
346
Cluster cluster = null;
348
cluster = Clusters.getInstance().lookup( vm.getPlacement() );
349
} catch ( NoSuchElementException e ) {
351
throw new EucalyptusCloudException( "Cluster does not exist: " + vm.getPlacement() );
354
request.setVolumeId( volume.getVolumeId() );
355
request.setRemoteDevice( volume.getRemoteDevice() );
356
request.setDevice( volume.getDevice().replaceAll("unknown,requested:","") );
357
request.setInstanceId( vm.getInstanceId() );
358
QueuedEvent<DetachVolumeType> event = QueuedEvent.make( new VolumeDetachCallback( ), request );
359
cluster.getMessageQueue().enqueue( event );
361
reply.setDetachedVolume( volume );