~kirkland/eucalyptus/label-metadata

« back to all changes in this revision

Viewing changes to clc/modules/image-manager/src/main/java/edu/ucsb/eucalyptus/cloud/ws/VolumeManager.java

  • Committer: Neil
  • Date: 2010-06-18 23:43:02 UTC
  • mfrom: (1195.1.47 main)
  • Revision ID: neil@pall-20100618234302-ylt6pznuzk7j09pw
latest merge from main.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*******************************************************************************
2
 
*Copyright (c) 2009  Eucalyptus Systems, Inc.
3
 
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.
7
 
8
 
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
12
 
*  for more details.
13
 
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/>.
16
 
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.
20
 
21
 
*  This file may incorporate work covered under the following copyright and
22
 
*  permission notice:
23
 
24
 
*    Software License Agreement (BSD License)
25
 
26
 
*    Copyright (c) 2008, Regents of the University of California
27
 
*    All rights reserved.
28
 
29
 
*    Redistribution and use of this software in source and binary forms, with
30
 
*    or without modification, are permitted provided that the following
31
 
*    conditions are met:
32
 
33
 
*      Redistributions of source code must retain the above copyright notice,
34
 
*      this list of conditions and the following disclaimer.
35
 
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.
39
 
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
 
*******************************************************************************/
61
 
/*
62
 
 *
63
 
 * Author: chris grzegorczyk <grze@eucalyptus.com>
64
 
 */
65
 
 
66
 
package edu.ucsb.eucalyptus.cloud.ws;
67
 
 
68
 
import java.util.ArrayList;
69
 
import java.util.HashMap;
70
 
import java.util.List;
71
 
import java.util.Map;
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;
105
 
 
106
 
public class VolumeManager {
107
 
  static String PERSISTENCE_CONTEXT = "eucalyptus_images";
108
 
 
109
 
  private static String ID_PREFIX = "vol";
110
 
  private static Logger LOG = Logger.getLogger( VolumeManager.class );
111
 
 
112
 
  public static EntityWrapper<Volume> getEntityWrapper() {
113
 
    return new EntityWrapper<Volume>( PERSISTENCE_CONTEXT );
114
 
  }
115
 
 
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." );
119
 
    }
120
 
    try {
121
 
      Configuration.getClusterConfiguration( request.getAvailabilityZone( ) );
122
 
    } catch ( Exception e ) {
123
 
      throw new EucalyptusCloudException( "Zone does not exist: " + request.getAvailabilityZone(), e );
124
 
    }
125
 
    StorageControllerConfiguration sc;
126
 
    try {
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 );
130
 
    }
131
 
    EntityWrapper<Volume> db = VolumeManager.getEntityWrapper();
132
 
    if ( request.getSnapshotId() != null ) {
133
 
      String userName = request.isAdministrator() ? null : request.getUserId();
134
 
      try {
135
 
        db.recast( Snapshot.class ).getUnique( Snapshot.named( userName, request.getSnapshotId() ) );
136
 
      } catch ( EucalyptusCloudException e ) {
137
 
        LOG.debug( e, e );
138
 
        db.rollback();
139
 
        throw new EucalyptusCloudException( "Snapshot does not exist: " + request.getSnapshotId() );
140
 
      }
141
 
    }    
142
 
    String newId = null;
143
 
    Volume newVol = null;
144
 
    while ( true ) {
145
 
      newId = Crypto.generateId( request.getUserId(), ID_PREFIX );
146
 
      try {
147
 
        db.getUnique( Volume.named( null, newId ) );
148
 
      } catch ( EucalyptusCloudException e ) {
149
 
        try {
150
 
          newVol = new Volume( request.getUserId(), newId, new Integer( request.getSize() != null ? request.getSize() : "-1" ),
151
 
                               request.getAvailabilityZone(), request.getSnapshotId() );
152
 
          db.add( newVol );
153
 
          db.commit();
154
 
          break;
155
 
        } catch ( Throwable e1 ) {
156
 
          db.rollback( );
157
 
          db = VolumeManager.getEntityWrapper();
158
 
        }
159
 
      }
160
 
    }
161
 
    newVol.setState( State.GENERATING );
162
 
    try {
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 ) {
168
 
      LOG.debug( e, e );
169
 
      try {
170
 
        db = VolumeManager.getEntityWrapper();
171
 
        Volume d = db.getUnique( Volume.named( newVol.getUserName( ), newVol.getDisplayName( ) ) );
172
 
        db.delete( d );
173
 
        db.commit( );
174
 
      } catch ( Throwable e1 ) {
175
 
        db.rollback( );
176
 
        LOG.debug( e1, e1 );
177
 
      }
178
 
      throw new EucalyptusCloudException( "Error while communicating with Storage Controller: CreateStorageVolume:" + e.getMessage() );
179
 
    }
180
 
    CreateVolumeResponseType reply = ( CreateVolumeResponseType ) request.getReply();
181
 
    reply.setVolume( newVol.morph( new edu.ucsb.eucalyptus.msgs.Volume() ) );
182
 
    return reply;
183
 
  }
184
 
 
185
 
  public DeleteVolumeResponseType DeleteVolume( DeleteVolumeType request ) throws EucalyptusCloudException {
186
 
    DeleteVolumeResponseType reply = ( DeleteVolumeResponseType ) request.getReply();
187
 
    reply.set_return( false );
188
 
 
189
 
    EntityWrapper<Volume> db = VolumeManager.getEntityWrapper();
190
 
    String userName = request.isAdministrator() ? null : request.getUserId();
191
 
    boolean reallyFailed = false;
192
 
    try {
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() ) ) {
197
 
            db.rollback( );
198
 
            return reply;
199
 
          }
200
 
        }
201
 
      }
202
 
      if( State.FAIL.equals(vol.getState() ) ) {
203
 
          db.delete(vol);
204
 
          db.commit();
205
 
          return reply;
206
 
      }      
207
 
      DeleteStorageVolumeResponseType scReply = StorageUtil.send( vol.getCluster( ), new DeleteStorageVolumeType( vol.getDisplayName( ) ) );
208
 
      if( scReply.get_return( ) ) {
209
 
        db.delete( vol );
210
 
        db.getSession( ).flush( );
211
 
        if( !vol.getState(  ).equals( State.ANNILATED ) ) {
212
 
          StorageUtil.dispatchAll( new DeleteStorageVolumeType( vol.getDisplayName() ) );
213
 
        }
214
 
        db.commit();
215
 
      } else {
216
 
        reallyFailed = true;
217
 
        throw new EucalyptusCloudException( "Storage Controller returned false:  Contact the administrator to report the problem." );
218
 
      }
219
 
    } catch ( EucalyptusCloudException e ) {
220
 
      LOG.debug( e, e );
221
 
      db.rollback();
222
 
      if( reallyFailed ) {
223
 
        throw e;
224
 
      } else {
225
 
        return reply;
226
 
      }
227
 
    }
228
 
    reply.set_return( true );
229
 
    return reply;
230
 
  }
231
 
 
232
 
  public DescribeVolumesResponseType DescribeVolumes( DescribeVolumesType request ) throws EucalyptusCloudException {
233
 
    DescribeVolumesResponseType reply = ( DescribeVolumesResponseType ) request.getReply();
234
 
    EntityWrapper<Volume> db = getEntityWrapper();
235
 
    try {
236
 
      String userName = request.isAdministrator() ? null : request.getUserId();
237
 
 
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 );
242
 
        }
243
 
      }
244
 
      
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 );
250
 
        }
251
 
      }
252
 
      try {
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 );
257
 
        LOG.debug( e, e );
258
 
      }
259
 
      db.commit( );
260
 
    } catch (Throwable t ) {
261
 
      db.commit();
262
 
    }
263
 
    return reply;
264
 
  }
265
 
 
266
 
  public AttachVolumeResponseType AttachVolume( AttachVolumeType request ) throws EucalyptusCloudException {
267
 
    AttachVolumeResponseType reply = ( AttachVolumeResponseType ) request.getReply();
268
 
 
269
 
    if( request.getDevice( ) == null || request.getDevice().endsWith( "sda" ) ) {
270
 
      throw new EucalyptusCloudException( "Invalid device name specified: " + request.getDevice( ) );
271
 
    }
272
 
    VmInstance vm = null;
273
 
    try {
274
 
      vm = VmInstances.getInstance().lookup( request.getInstanceId() );
275
 
    } catch ( NoSuchElementException e ) {
276
 
      LOG.debug( e, e );
277
 
      throw new EucalyptusCloudException( "Instance does not exist: " + request.getInstanceId() );
278
 
    }
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() );
282
 
      }
283
 
    }
284
 
    Cluster cluster = null;
285
 
    try {
286
 
      cluster = Clusters.getInstance().lookup( vm.getPlacement() );
287
 
    } catch ( NoSuchElementException e ) {
288
 
      LOG.debug( e, e );
289
 
      throw new EucalyptusCloudException( "Cluster does not exist: " + vm.getPlacement() );
290
 
    }
291
 
 
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() );
296
 
        }
297
 
      }
298
 
    }
299
 
 
300
 
    EntityWrapper<Volume> db = VolumeManager.getEntityWrapper();
301
 
    String userName = request.isAdministrator() ? null : request.getUserId();
302
 
    Volume volume = null;
303
 
    try {
304
 
      volume = db.getUnique( Volume.named( userName, request.getVolumeId() ) );
305
 
      db.commit();
306
 
    } catch ( EucalyptusCloudException e ) {
307
 
      LOG.debug( e, e );
308
 
      db.rollback();
309
 
      throw new EucalyptusCloudException( "Volume does not exist: " + request.getVolumeId() );
310
 
    }
311
 
 
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());
316
 
    }
317
 
    request.setRemoteDevice( volume.getRemoteDevice() );
318
 
    new VolumeAttachCallback( request ).dispatch( cluster );
319
 
 
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 );
325
 
    return reply;
326
 
  }
327
 
 
328
 
  public DetachVolumeResponseType detach( DetachVolumeType request ) throws EucalyptusCloudException {
329
 
    DetachVolumeResponseType reply = ( DetachVolumeResponseType ) request.getReply();
330
 
 
331
 
    EntityWrapper<Volume> db = VolumeManager.getEntityWrapper();
332
 
    String userName = request.isAdministrator() ? null : request.getUserId();
333
 
    try {
334
 
      db.getUnique( Volume.named( userName, request.getVolumeId() ) );
335
 
    } catch ( EucalyptusCloudException e ) {
336
 
      LOG.debug( e, e );
337
 
      db.rollback();
338
 
      throw new EucalyptusCloudException( "Volume does not exist: " + request.getVolumeId() );
339
 
    }
340
 
    db.commit();
341
 
 
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() ) ) {
347
 
          volume = vol;
348
 
          vm = v;
349
 
        }
350
 
      }
351
 
    }
352
 
    if ( volume == null) {
353
 
      throw new EucalyptusCloudException( "Volume is not attached: " + request.getVolumeId() );
354
 
    }
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() );
357
 
    }
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() );
360
 
    }
361
 
 
362
 
    Cluster cluster = null;
363
 
    try {
364
 
      cluster = Clusters.getInstance().lookup( vm.getPlacement() );
365
 
    } catch ( NoSuchElementException e ) {
366
 
      LOG.debug( e, e );
367
 
      throw new EucalyptusCloudException( "Cluster does not exist: " + vm.getPlacement() );
368
 
    }
369
 
 
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 );
377
 
    return reply;
378
 
  }
379
 
 
380
 
}
381