80
80
import org.apache.log4j.Logger;
81
81
import org.bouncycastle.util.encoders.Base64;
83
import com.eucalyptus.auth.ClusterCredentials;
84
import com.eucalyptus.auth.Authentication;
85
import com.eucalyptus.auth.SystemCredentialProvider;
86
import com.eucalyptus.auth.X509Cert;
87
83
import com.eucalyptus.auth.util.Hashes;
84
import com.eucalyptus.auth.util.X509CertHelper;
85
import com.eucalyptus.component.Partitions;
86
import com.eucalyptus.component.ServiceConfiguration;
87
import com.eucalyptus.component.ServiceConfigurations;
88
import com.eucalyptus.component.id.ClusterController;
88
89
import com.eucalyptus.config.StorageControllerBuilder;
89
90
import com.eucalyptus.configurable.ConfigurableClass;
90
91
import com.eucalyptus.configurable.ConfigurableProperty;
91
92
import com.eucalyptus.configurable.PropertyDirectory;
92
93
import com.eucalyptus.entities.EntityWrapper;
93
94
import com.eucalyptus.util.EucalyptusCloudException;
94
import com.eucalyptus.util.ExecutionException;
95
95
import com.eucalyptus.util.StorageProperties;
96
96
import com.eucalyptus.util.WalrusProperties;
147
147
exportManager = new ISCSIManager();
149
149
exportManager.checkPreconditions();
150
} catch(ExecutionException ex) {
150
} catch(EucalyptusCloudException ex) {
151
151
String error = "Unable to run command: " + ex.getMessage();
152
152
LOG.error(error);
153
153
throw new EucalyptusCloudException(error);
157
private String getLvmVersion() throws ExecutionException {
157
private String getLvmVersion() throws EucalyptusCloudException {
158
158
return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "lvm", "version"});
161
private String findFreeLoopback() throws ExecutionException {
161
private String findFreeLoopback() throws EucalyptusCloudException {
162
162
return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "losetup", "-f"}).replaceAll("\n", "");
165
private String getLoopback(String loDevName) throws ExecutionException {
165
private String getLoopback(String loDevName) throws EucalyptusCloudException {
166
166
return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "losetup", loDevName});
169
private String createPhysicalVolume(String loDevName) throws ExecutionException {
169
private String createPhysicalVolume(String loDevName) throws EucalyptusCloudException {
170
170
return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "pvcreate", loDevName});
173
private String createVolumeGroup(String pvName, String vgName) throws ExecutionException {
173
private String createVolumeGroup(String pvName, String vgName) throws EucalyptusCloudException {
174
174
return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "vgcreate", vgName, pvName});
177
private String extendVolumeGroup(String pvName, String vgName) throws ExecutionException {
177
private String extendVolumeGroup(String pvName, String vgName) throws EucalyptusCloudException {
178
178
return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "vgextend", vgName, pvName});
181
private String scanVolumeGroups() throws ExecutionException {
181
private String scanVolumeGroups() throws EucalyptusCloudException {
182
182
return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "vgscan"});
185
private String createLogicalVolume(String vgName, String lvName) throws ExecutionException {
185
private String createLogicalVolume(String vgName, String lvName) throws EucalyptusCloudException {
186
186
return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "lvcreate", "-n", lvName, "-l", "100%FREE", vgName});
189
private String createSnapshotLogicalVolume(String lvName, String snapLvName) throws ExecutionException {
189
private String createSnapshotLogicalVolume(String lvName, String snapLvName) throws EucalyptusCloudException {
190
190
return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "lvcreate", "-n", snapLvName, "-s", "-l", "100%FREE", lvName});
193
private String removeLogicalVolume(String lvName) throws ExecutionException {
193
private String removeLogicalVolume(String lvName) throws EucalyptusCloudException {
194
194
return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "lvremove", "-f", lvName});
197
private String removeVolumeGroup(String vgName) throws ExecutionException {
197
private String removeVolumeGroup(String vgName) throws EucalyptusCloudException {
198
198
return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "vgremove", vgName});
201
private String removePhysicalVolume(String loDevName) throws ExecutionException {
201
private String removePhysicalVolume(String loDevName) throws EucalyptusCloudException {
202
202
return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "pvremove", loDevName});
205
private String removeLoopback(String loDevName) throws ExecutionException {
205
private String removeLoopback(String loDevName) throws EucalyptusCloudException {
206
206
return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "losetup", "-d", loDevName});
209
private String reduceVolumeGroup(String vgName, String pvName) throws ExecutionException {
209
private String reduceVolumeGroup(String vgName, String pvName) throws EucalyptusCloudException {
210
210
return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "vgreduce", vgName, pvName});
213
private String enableLogicalVolume(String lvName) throws ExecutionException {
213
private String enableLogicalVolume(String lvName) throws EucalyptusCloudException {
214
214
return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "lvchange", "-ay", lvName});
217
private boolean logicalVolumeExists(String lvName) {
218
boolean success = false;
219
String returnValue = SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "lvdisplay", lvName});
220
if(returnValue.length() > 0) {
226
private boolean volumeGroupExists(String vgName) {
227
boolean success = false;
228
String returnValue = SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "vgdisplay", vgName});
229
if(returnValue.length() > 0) {
235
private boolean physicalVolumeExists(String pvName) {
236
boolean success = false;
237
String returnValue = SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "pvdisplay", pvName});
238
if(returnValue.length() > 0) {
217
244
private int losetup(String absoluteFileName, String loDevName) {
229
256
LOG.info(output.getReturnValue());
230
257
LOG.info(error.getReturnValue());
231
258
return errorCode;
232
} catch (Throwable t) {
259
} catch (Exception t) {
238
private String duplicateLogicalVolume(String oldLvName, String newLvName) throws ExecutionException {
265
private String duplicateLogicalVolume(String oldLvName, String newLvName) throws EucalyptusCloudException {
239
266
return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "dd", "if=" + oldLvName, "of=" + newLvName, "bs=" + StorageProperties.blockSize});
242
private String createFile(String fileName, long size) throws ExecutionException {
269
private String createFile(String fileName, long size) throws EucalyptusCloudException {
243
270
if(!DirectStorageInfo.getStorageInfo().getZeroFillVolumes())
244
271
return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "dd", "if=/dev/zero", "of=" + fileName, "count=1", "bs=" + StorageProperties.blockSize, "seek=" + (size -1)});
246
273
return SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER, "dd", "if=/dev/zero", "of=" + fileName, "count=" + size, "bs=" + StorageProperties.blockSize});
249
private String createEmptyFile(String fileName, int size) throws ExecutionException {
276
private String createEmptyFile(String fileName, int size) throws EucalyptusCloudException {
250
277
long fileSize = size * 1024;
251
278
return createFile(fileName, fileSize);
254
public String createAbsoluteEmptyFile(String fileName, long size) throws ExecutionException {
281
public String createAbsoluteEmptyFile(String fileName, long size) throws EucalyptusCloudException {
255
282
size = size / WalrusProperties.M;
256
283
return createFile(fileName, size);
260
public void initialize() {
287
public void initialize() throws EucalyptusCloudException {
288
File storageRootDir = new File(getStorageRootDirectory());
289
if(!storageRootDir.exists()) {
290
if(!storageRootDir.mkdirs()) {
291
throw new EucalyptusCloudException("Unable to make volume root directory: " + getStorageRootDirectory());
294
//The following should be executed only once during the entire lifetime of the VM.
261
295
if(!initialized) {
262
296
System.loadLibrary("lvm2control");
263
297
registerSignals();
264
File storageRootDir = new File(getStorageRootDirectory());
265
if(!storageRootDir.exists()) {
266
if(!storageRootDir.mkdirs()) {
267
LOG.fatal("Unable to make volume root directory: " + getStorageRootDirectory());
270
298
initialized = true;
274
public void configure() {
302
public void configure() throws EucalyptusCloudException {
275
303
exportManager.configure();
276
304
//First call to StorageInfo.getStorageInfo will add entity if it does not exist
277
LOG.info(StorageInfo.getStorageInfo().getName());
305
LOG.info(""+StorageInfo.getStorageInfo().getName());
278
306
checkVolumesDir();
377
public String createDuplicateLoopback(String oldRawFileName, String rawFileName) throws EucalyptusCloudException, ExecutionException {
411
public String createDuplicateLoopback(String oldRawFileName, String rawFileName) throws EucalyptusCloudException {
378
412
dupFile(oldRawFileName, rawFileName);
379
413
return createLoopback(rawFileName);
382
public String createLoopback(String fileName, int size) throws EucalyptusCloudException, ExecutionException {
416
public String createLoopback(String fileName, int size) throws EucalyptusCloudException {
383
417
createEmptyFile(fileName, size);
384
418
if(!(new File(fileName).exists()))
385
419
throw new EucalyptusCloudException("Unable to create file " + fileName);
386
420
return createLoopback(fileName);
389
public synchronized String createLoopback(String fileName) throws EucalyptusCloudException, ExecutionException {
423
public synchronized String createLoopback(String fileName) throws EucalyptusCloudException {
390
424
int number_of_retries = 0;
392
426
String loDevName;
598
public void cloneVolume(String volumeId, String parentVolumeId)
599
throws EucalyptusCloudException {
600
VolumeEntityWrapperManager volumeManager = new VolumeEntityWrapperManager();
601
LVMVolumeInfo foundVolumeInfo = volumeManager.getVolumeInfo(parentVolumeId);
602
if(foundVolumeInfo != null) {
603
String vgName = "vg-" + Hashes.getRandom(4);
604
String lvName = "lv-" + Hashes.getRandom(4);
605
String parentVgName = foundVolumeInfo.getVgName();
606
String parentLvName = foundVolumeInfo.getLvName();
607
LVMVolumeInfo lvmVolumeInfo = volumeManager.getVolumeInfo();
608
int size = foundVolumeInfo.getSize();
609
volumeManager.finish();
611
String rawFileName = DirectStorageInfo.getStorageInfo().getVolumesDir() + "/" + volumeId;
612
//create file and attach to loopback device
613
File parentVolumeFile = new File(DirectStorageInfo.getStorageInfo().getVolumesDir() + PATH_SEPARATOR + parentVolumeId);
614
assert(parentVolumeFile.exists());
615
long absoluteSize = parentVolumeFile.length();
617
String loDevName = createLoopback(rawFileName, absoluteSize);
618
//create physical volume, volume group and logical volume
619
createLogicalVolume(loDevName, vgName, lvName);
620
//duplicate snapshot volume
621
String absoluteLVName = lvmRootDirectory + PATH_SEPARATOR + vgName + PATH_SEPARATOR + lvName;
622
String absoluteParentLVName = lvmRootDirectory + PATH_SEPARATOR + parentVgName + PATH_SEPARATOR + parentLvName;
623
duplicateLogicalVolume(absoluteParentLVName, absoluteLVName);
624
//export logical volume
626
volumeManager.exportVolume(lvmVolumeInfo, vgName, lvName);
627
} catch(EucalyptusCloudException ex) {
628
String returnValue = removeLogicalVolume(absoluteLVName);
629
returnValue = removeVolumeGroup(vgName);
630
returnValue = removePhysicalVolume(loDevName);
631
removeLoopback(loDevName);
634
lvmVolumeInfo.setVolumeId(volumeId);
635
lvmVolumeInfo.setLoDevName(loDevName);
636
lvmVolumeInfo.setPvName(loDevName);
637
lvmVolumeInfo.setVgName(vgName);
638
lvmVolumeInfo.setLvName(lvName);
639
lvmVolumeInfo.setStatus(StorageProperties.Status.available.toString());
640
lvmVolumeInfo.setSize(size);
641
volumeManager = new VolumeEntityWrapperManager();
642
volumeManager.add(lvmVolumeInfo);
643
volumeManager.finish();
644
} catch(EucalyptusCloudException ex) {
645
volumeManager.abort();
646
String error = "Unable to run command: " + ex.getMessage();
648
throw new EucalyptusCloudException(error);
651
volumeManager.abort();
652
throw new EucalyptusCloudException("Unable to find volume: " + parentVolumeId);
564
656
public void addSnapshot(String snapshotId) throws EucalyptusCloudException {
565
657
String snapshotRawFileName = DirectStorageInfo.getStorageInfo().getVolumesDir() + "/" + snapshotId;
566
658
File snapshotFile = new File(snapshotRawFileName);
582
674
VolumeEntityWrapperManager volumeManager = new VolumeEntityWrapperManager();
583
675
LVMVolumeInfo foundLVMVolumeInfo = volumeManager.getVolumeInfo(volumeId);
584
676
if(foundLVMVolumeInfo != null) {
586
677
String loDevName = foundLVMVolumeInfo.getLoDevName();
587
678
String vgName = foundLVMVolumeInfo.getVgName();
588
679
String lvName = foundLVMVolumeInfo.getLvName();
589
680
String absoluteLVName = lvmRootDirectory + PATH_SEPARATOR + vgName + PATH_SEPARATOR + lvName;
590
681
volumeManager.unexportVolume(foundLVMVolumeInfo);
592
String returnValue = removeLogicalVolume(absoluteLVName);
593
if(returnValue.length() == 0) {
594
throw new EucalyptusCloudException("Unable to remove logical volume " + absoluteLVName);
596
returnValue = removeVolumeGroup(vgName);
597
if(returnValue.length() == 0) {
598
throw new EucalyptusCloudException("Unable to remove volume group " + vgName);
600
returnValue = removePhysicalVolume(loDevName);
601
if(returnValue.length() == 0) {
602
throw new EucalyptusCloudException("Unable to remove physical volume " + loDevName);
604
returnValue = removeLoopback(loDevName);
605
File rawFile = new File(DirectStorageInfo.getStorageInfo().getVolumesDir() + "/" + volumeId);
606
if (rawFile.exists()) {
607
if(!rawFile.delete()) {
608
throw new EucalyptusCloudException("Unable to delete: " + rawFile.getAbsolutePath());
684
deleteLogicalVolume(loDevName, vgName, absoluteLVName);
685
removeLoopback(loDevName);
686
if(getLoopback(loDevName).length() != 0) {
687
throw new EucalyptusCloudException("Unable to remove loopback device: " + loDevName);
689
LOG.info(loDevName + "was removed.");
691
volumeManager.remove(foundLVMVolumeInfo);
692
volumeManager.finish();
693
File volFile = new File (DirectStorageInfo.getStorageInfo().getVolumesDir() + File.separator + volumeId);
694
if (volFile.exists()) {
695
if(!volFile.delete()) {
696
LOG.error("Unable to delete: " + volFile.getAbsolutePath());
611
volumeManager.remove(foundLVMVolumeInfo);
612
volumeManager.finish();
613
} catch(ExecutionException ex) {
700
} catch(EucalyptusCloudException ex) {
614
701
volumeManager.abort();
615
702
String error = "Unable to run command: " + ex.getMessage();
616
703
LOG.error(error);
712
/*LVM is flaky when there are a large number of concurrent removal requests. This workaround serializes lvm cleanup*/
713
private synchronized void deleteLogicalVolume(String loDevName, String vgName,
714
String absoluteLVName) throws EucalyptusCloudException,
715
EucalyptusCloudException {
716
if(logicalVolumeExists(absoluteLVName)) {
717
String returnValue = removeLogicalVolume(absoluteLVName);
718
if(returnValue.length() == 0) {
719
throw new EucalyptusCloudException("Unable to remove logical volume " + absoluteLVName + " " + returnValue);
722
if(volumeGroupExists(vgName)) {
723
String returnValue = removeVolumeGroup(vgName);
724
if(returnValue.length() == 0) {
725
throw new EucalyptusCloudException("Unable to remove volume group " + vgName + " " + returnValue);
728
if(physicalVolumeExists(loDevName)) {
729
String returnValue = removePhysicalVolume(loDevName);
730
if(returnValue.length() == 0) {
731
throw new EucalyptusCloudException("Unable to remove physical volume " + loDevName + " " + returnValue);
626
public List<String> createSnapshot(String volumeId, String snapshotId) throws EucalyptusCloudException {
736
public List<String> createSnapshot(String volumeId, String snapshotId, Boolean shouldTransferSnapshot) throws EucalyptusCloudException {
627
737
VolumeEntityWrapperManager volumeManager = new VolumeEntityWrapperManager();
628
738
LVMVolumeInfo foundLVMVolumeInfo = volumeManager.getVolumeInfo(volumeId);
629
739
ArrayList<String> returnValues = new ArrayList<String>();
903
1015
if(lvmVolumeInfo instanceof ISCSIVolumeInfo) {
904
1016
ISCSIVolumeInfo iscsiVolumeInfo = (ISCSIVolumeInfo) lvmVolumeInfo;
905
1017
String absoluteLVName = lvmRootDirectory + PATH_SEPARATOR + iscsiVolumeInfo.getVgName() + PATH_SEPARATOR + iscsiVolumeInfo.getLvName();
1019
enableLogicalVolume(absoluteLVName);
1020
} catch(EucalyptusCloudException ex) {
1021
String error = "Unable to run command: " + ex.getMessage();
1023
throw new EucalyptusCloudException(ex);
906
1025
((ISCSIManager)exportManager).exportTarget(iscsiVolumeInfo.getTid(), iscsiVolumeInfo.getStoreName(), iscsiVolumeInfo.getLun(), absoluteLVName, iscsiVolumeInfo.getStoreUser());
908
ISCSIVolumeInfo volumeInfo = new ISCSIVolumeInfo();
909
convertVolumeInfo(lvmVolumeInfo, volumeInfo);
911
unexportVolume(lvmVolumeInfo);
912
exportVolume(volumeInfo, volumeInfo.getVgName(), volumeInfo.getLvName());
914
remove(lvmVolumeInfo);
915
} catch(EucalyptusCloudException ex) {
1028
ISCSIVolumeInfo volumeInfo = new ISCSIVolumeInfo();
1029
convertVolumeInfo(lvmVolumeInfo, volumeInfo);
1031
unexportVolume(lvmVolumeInfo);
1032
exportVolume(volumeInfo, volumeInfo.getVgName(), volumeInfo.getLvName());
1034
remove(lvmVolumeInfo);
1035
} catch(EucalyptusCloudException ex) {
1040
1172
private String encryptTargetPassword(String password) throws EucalyptusCloudException {
1041
EntityWrapper<ClusterCredentials> credDb = Authentication.getEntityWrapper( );
1043
ClusterCredentials credentials = credDb.getUnique( new ClusterCredentials( StorageProperties.NAME ) );
1044
PublicKey ncPublicKey = X509Cert.toCertificate(credentials.getNodeCertificate()).getPublicKey();
1174
List<ServiceConfiguration> partitionConfigs = ServiceConfigurations.listPartition( ClusterController.class, StorageProperties.NAME );
1175
ServiceConfiguration clusterConfig = partitionConfigs.get( 0 );
1176
PublicKey ncPublicKey = Partitions.lookup( clusterConfig ).getNodeCertificate( ).getPublicKey();
1046
1177
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
1047
1178
cipher.init(Cipher.ENCRYPT_MODE, ncPublicKey);
1048
1179
return new String(Base64.encode(cipher.doFinal(password.getBytes())));
1049
1180
} catch ( Exception e ) {
1050
1181
LOG.error( "Unable to encrypt storage target password" );
1052
1182
throw new EucalyptusCloudException(e.getMessage(), e);
1056
private int exportVolume(LVMVolumeInfo lvmVolumeInfo, String vgName, String lvName) throws EucalyptusCloudException {
1186
private void exportVolume(LVMVolumeInfo lvmVolumeInfo, String vgName, String lvName) throws EucalyptusCloudException {
1057
1187
if(exportManager instanceof AOEManager) {
1058
1188
AOEVolumeInfo aoeVolumeInfo = (AOEVolumeInfo) lvmVolumeInfo;
1059
1189
exportManager.allocateTarget(aoeVolumeInfo);
1192
1331
volumeManager = new VolumeEntityWrapperManager();
1193
1332
LVMVolumeInfo volumeInfo = volumeManager.getVolumeInfo(volumeId);
1194
1333
if(volumeInfo != null) {
1196
SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER,
1197
"dd", "if=" + volumePath,
1198
"of=" + lvmRootDirectory + File.separator + volumeInfo.getVgName() +
1199
File.separator + volumeInfo.getLvName(), "bs=" + StorageProperties.blockSize});
1200
} catch (ExecutionException e) {
1202
throw new EucalyptusCloudException(e);
1334
SystemUtil.run(new String[]{eucaHome + StorageProperties.EUCA_ROOT_WRAPPER,
1335
"dd", "if=" + volumePath,
1336
"of=" + lvmRootDirectory + File.separator + volumeInfo.getVgName() +
1337
File.separator + volumeInfo.getLvName(), "bs=" + StorageProperties.blockSize});
1205
1339
volumeManager.abort();
1206
1340
throw new EucalyptusCloudException("Unable to find volume with id: " + volumeId);
1250
1379
volumeManager.add(snapshotInfo);
1251
1380
volumeManager.finish();
1384
public String attachVolume(String volumeId, List<String> nodeIqns)
1385
throws EucalyptusCloudException {
1386
return getVolumeProperty(volumeId);
1390
public void detachVolume(String volumeId, String nodeIqn)
1391
throws EucalyptusCloudException {
1395
public void checkReady() throws EucalyptusCloudException {
1396
//check if binaries exist, commands can be executed, etc.
1397
String eucaHomeDir = System.getProperty("euca.home");
1398
if(eucaHomeDir == null) {
1399
throw new EucalyptusCloudException("euca.home not set");
1401
eucaHome = eucaHomeDir;
1402
if(!new File(eucaHome + StorageProperties.EUCA_ROOT_WRAPPER).exists()) {
1403
throw new EucalyptusCloudException("root wrapper (euca_rootwrap) does not exist in " + eucaHome + StorageProperties.EUCA_ROOT_WRAPPER);
1405
File varDir = new File(eucaHome + EUCA_VAR_RUN_PATH);
1406
if(!varDir.exists()) {
1412
public void stop() throws EucalyptusCloudException {
1413
// TODO Auto-generated method stub
1418
public void disable() throws EucalyptusCloudException {
1419
// TODO Auto-generated method stub
1424
public void enable() throws EucalyptusCloudException {
1425
// TODO Auto-generated method stub