mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Merge release branch 4.8 to master
* 4.8: Add ability to download templates in Swift
This commit is contained in:
commit
f7c3957af3
@ -18,10 +18,15 @@
|
||||
*/
|
||||
package org.apache.cloudstack.storage.datastore.driver;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.configuration.Config;
|
||||
import com.cloud.utils.SwiftUtil;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
@ -43,7 +48,6 @@ import com.cloud.agent.api.storage.DownloadAnswer;
|
||||
import com.cloud.agent.api.to.DataObjectType;
|
||||
import com.cloud.agent.api.to.DataStoreTO;
|
||||
import com.cloud.agent.api.to.SwiftTO;
|
||||
import com.cloud.exception.UnsupportedServiceException;
|
||||
import com.cloud.storage.Storage.ImageFormat;
|
||||
import com.cloud.template.VirtualMachineTemplate;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
@ -57,6 +61,8 @@ public class SwiftImageStoreDriverImpl extends BaseImageStoreDriverImpl {
|
||||
EndPointSelector _epSelector;
|
||||
@Inject
|
||||
StorageCacheManager cacheManager;
|
||||
@Inject
|
||||
ConfigurationDao _configDao;
|
||||
|
||||
@Override
|
||||
public DataStoreTO getStoreTO(DataStore store) {
|
||||
@ -67,7 +73,29 @@ public class SwiftImageStoreDriverImpl extends BaseImageStoreDriverImpl {
|
||||
|
||||
@Override
|
||||
public String createEntityExtractUrl(DataStore store, String installPath, ImageFormat format, DataObject dataObject) {
|
||||
throw new UnsupportedServiceException("Extract entity url is not yet supported for Swift image store provider");
|
||||
|
||||
SwiftTO swiftTO = (SwiftTO)store.getTO();
|
||||
String tempKey = UUID.randomUUID().toString();
|
||||
boolean result = SwiftUtil.setTempKey(swiftTO, tempKey);
|
||||
|
||||
if (!result) {
|
||||
String errMsg = "Unable to set Temp-Key: " + tempKey;
|
||||
s_logger.error(errMsg);
|
||||
throw new CloudRuntimeException(errMsg);
|
||||
}
|
||||
|
||||
String containerName = SwiftUtil.getContainerName(dataObject.getType().toString(), dataObject.getId());
|
||||
String objectName = installPath.split("\\/")[1];
|
||||
// Get extract url expiration interval set in global configuration (in seconds)
|
||||
int urlExpirationInterval = Integer.parseInt(_configDao.getValue(Config.ExtractURLExpirationInterval.toString()));
|
||||
|
||||
URL swiftUrl = SwiftUtil.generateTempUrl(swiftTO, containerName, objectName, tempKey, urlExpirationInterval);
|
||||
if (swiftUrl != null) {
|
||||
s_logger.debug("Swift temp-url: " + swiftUrl.toString());
|
||||
return swiftUrl.toString();
|
||||
}
|
||||
|
||||
throw new CloudRuntimeException("Unable to create extraction URL");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -1520,12 +1520,6 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
|
||||
}
|
||||
|
||||
} finally {
|
||||
/*if (snapshot != null && snapshot.getSwiftId() != null
|
||||
&& secondaryStorageURL != null && zoneId != null
|
||||
&& accountId != null && volumeId != null) {
|
||||
_snapshotMgr.deleteSnapshotsForVolume(secondaryStorageURL,
|
||||
zoneId, accountId, volumeId);
|
||||
}*/
|
||||
if (privateTemplate == null) {
|
||||
final VolumeVO volumeFinal = volume;
|
||||
final SnapshotVO snapshotFinal = snapshot;
|
||||
|
||||
@ -20,8 +20,14 @@
|
||||
package com.cloud.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.net.URL;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SignatureException;
|
||||
import java.util.Map;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Formatter;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
@ -29,9 +35,15 @@ import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.script.OutputInterpreter;
|
||||
import com.cloud.utils.script.Script;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
public class SwiftUtil {
|
||||
private static Logger logger = Logger.getLogger(SwiftUtil.class);
|
||||
private static final long SWIFT_MAX_SIZE = 5L * 1024L * 1024L * 1024L;
|
||||
private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
|
||||
|
||||
|
||||
|
||||
public interface SwiftClientCfg {
|
||||
String getAccount();
|
||||
@ -143,8 +155,7 @@ public class SwiftUtil {
|
||||
OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
|
||||
String result = command.execute(parser);
|
||||
if (result == null && parser.getLines() != null && !parser.getLines().equalsIgnoreCase("")) {
|
||||
String[] lines = parser.getLines().split("\\n");
|
||||
return lines;
|
||||
return parser.getLines().split("\\n");
|
||||
} else {
|
||||
if (result != null) {
|
||||
String errMsg = "swiftList failed , err=" + result;
|
||||
@ -161,7 +172,7 @@ public class SwiftUtil {
|
||||
int firstIndexOfSeparator = swiftPath.indexOf(File.separator);
|
||||
String container = swiftPath.substring(0, firstIndexOfSeparator);
|
||||
String srcPath = swiftPath.substring(firstIndexOfSeparator + 1);
|
||||
String destFilePath = null;
|
||||
String destFilePath;
|
||||
if (destDirectory.isDirectory()) {
|
||||
destFilePath = destDirectory.getAbsolutePath() + File.separator + srcPath;
|
||||
} else {
|
||||
@ -171,7 +182,7 @@ public class SwiftUtil {
|
||||
Script command = new Script("/bin/bash", logger);
|
||||
command.add("-c");
|
||||
command.add("/usr/bin/python " + swiftCli + " -A " + cfg.getEndPoint() + " -U " + cfg.getAccount() + ":" + cfg.getUserName() + " -K " + cfg.getKey() +
|
||||
" download " + container + " " + srcPath + " -o " + destFilePath);
|
||||
" download " + container + " " + srcPath + " -o " + destFilePath);
|
||||
OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
|
||||
String result = command.execute(parser);
|
||||
if (result != null) {
|
||||
@ -236,4 +247,59 @@ public class SwiftUtil {
|
||||
command.execute(parser);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean setTempKey(SwiftClientCfg cfg, String tempKey){
|
||||
|
||||
Map<String, String> tempKeyMap = new HashMap<>();
|
||||
tempKeyMap.put("Temp-URL-Key", tempKey);
|
||||
return postMeta(cfg, "", "", tempKeyMap);
|
||||
|
||||
}
|
||||
|
||||
public static URL generateTempUrl(SwiftClientCfg cfg, String container, String object, String tempKey, int urlExpirationInterval) {
|
||||
|
||||
int currentTime = (int) (System.currentTimeMillis() / 1000L);
|
||||
int expirationSeconds = currentTime + urlExpirationInterval;
|
||||
|
||||
try {
|
||||
|
||||
URL endpoint = new URL(cfg.getEndPoint());
|
||||
String method = "GET";
|
||||
String path = String.format("/v1/AUTH_%s/%s/%s", cfg.getAccount(), container, object);
|
||||
|
||||
//sign the request
|
||||
String hmacBody = String.format("%s\n%d\n%s", method, expirationSeconds, path);
|
||||
String signature = calculateRFC2104HMAC(hmacBody, tempKey);
|
||||
path += String.format("?temp_url_sig=%s&temp_url_expires=%d", signature, expirationSeconds);
|
||||
|
||||
//generate the temp url
|
||||
URL tempUrl = new URL(endpoint.getProtocol(), endpoint.getHost(), endpoint.getPort(), path);
|
||||
|
||||
return tempUrl;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage());
|
||||
throw new CloudRuntimeException(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static String calculateRFC2104HMAC(String data, String key)
|
||||
throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
|
||||
|
||||
SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), HMAC_SHA1_ALGORITHM);
|
||||
Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
|
||||
mac.init(signingKey);
|
||||
return toHexString(mac.doFinal(data.getBytes()));
|
||||
|
||||
}
|
||||
|
||||
public static String toHexString(byte[] bytes) {
|
||||
|
||||
Formatter formatter = new Formatter();
|
||||
for (byte b : bytes) {
|
||||
formatter.format("%02x", b);
|
||||
}
|
||||
return formatter.toString();
|
||||
}
|
||||
}
|
||||
|
||||
92
utils/src/test/java/com/cloud/utils/SwiftUtilTest.java
Normal file
92
utils/src/test/java/com/cloud/utils/SwiftUtilTest.java
Normal file
@ -0,0 +1,92 @@
|
||||
//
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
//
|
||||
|
||||
package com.cloud.utils;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.net.URL;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SignatureException;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
||||
public class SwiftUtilTest {
|
||||
|
||||
@Test
|
||||
public void testCalculateRFC2104HMAC() throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
|
||||
String inputData = "testData";
|
||||
String inputKey = "testKey";
|
||||
String expected = "1d541ecb5cdb2d850716bfd55585e20a1cd8984b";
|
||||
String output = SwiftUtil.calculateRFC2104HMAC(inputData, inputKey);
|
||||
|
||||
assertEquals(expected, output);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToHexString(){
|
||||
final byte[] input = "testing".getBytes();
|
||||
final String expected = "74657374696e67";
|
||||
final String result = SwiftUtil.toHexString(input);
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateTempUrl() {
|
||||
|
||||
SwiftUtil.SwiftClientCfg cfg = Mockito.mock(SwiftUtil.SwiftClientCfg.class);
|
||||
when(cfg.getEndPoint()).thenReturn("http://localhost:8080/v1/");
|
||||
when(cfg.getAccount()).thenReturn("test");
|
||||
|
||||
String container = "testContainer";
|
||||
String object = "testObject";
|
||||
String tempKey = "testKey";
|
||||
int urlExpirationInterval = 3600;
|
||||
String expected = "http://localhost:8080/v1/AUTH_test/testContainer/testObject";
|
||||
URL output = SwiftUtil.generateTempUrl(cfg, container, object, tempKey, urlExpirationInterval);
|
||||
|
||||
assertTrue(output.toString().contains(expected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSplitSwiftPath(){
|
||||
String input = "container/object";
|
||||
String[] output = SwiftUtil.splitSwiftPath(input);
|
||||
String[] expected = {"container", "object"};
|
||||
|
||||
assertArrayEquals(expected, output);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetContainerName(){
|
||||
|
||||
String inputType = "Template";
|
||||
long inputId = 45;
|
||||
String output = SwiftUtil.getContainerName(inputType, inputId);
|
||||
String expected = "T-45";
|
||||
|
||||
assertEquals(expected, output);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user