mirror of
https://github.com/apache/cloudstack.git
synced 2025-11-03 04:12:31 +01:00
Backs NFS-based secondary storage with an S3-compatible object store. Periodically, a reaper thread synchronizes templates and ISOs stored on a NFS secondary storage mount with a configured S3 object store. It also pushes snapshots to the object store when they are created and downloads them in other zones on-demand. In addition to permitting the use of commodity or IaaS storage solutions for static assets, it provides a means of automatically synchronizing template and ISO assets across multiple zones.
This commit is contained in:
parent
0ba355fbc2
commit
b70c1a5a84
1
.gitignore
vendored
1
.gitignore
vendored
@ -67,6 +67,7 @@ awsapi/modules/*
|
||||
.settings/
|
||||
db.properties.override
|
||||
awsapi/overlays/
|
||||
tools/marvin/marvin/cloudstackAPI/*
|
||||
*.egg-info/
|
||||
docs/tmp
|
||||
docs/publish
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
package com.cloud.agent.api;
|
||||
|
||||
import com.cloud.agent.api.LogLevel.Log4jLevel;
|
||||
import com.cloud.agent.api.to.S3TO;
|
||||
import com.cloud.agent.api.to.StorageFilerTO;
|
||||
import com.cloud.agent.api.to.SwiftTO;
|
||||
import com.cloud.storage.StoragePool;
|
||||
@ -32,6 +33,7 @@ public class BackupSnapshotCommand extends SnapshotCommand {
|
||||
private Long snapshotId;
|
||||
@LogLevel(Log4jLevel.Off)
|
||||
private SwiftTO swift;
|
||||
private S3TO s3;
|
||||
StorageFilerTO pool;
|
||||
|
||||
protected BackupSnapshotCommand() {
|
||||
@ -88,7 +90,7 @@ public class BackupSnapshotCommand extends SnapshotCommand {
|
||||
}
|
||||
|
||||
public String getVmName() {
|
||||
return vmName;
|
||||
return vmName;
|
||||
}
|
||||
|
||||
public SwiftTO getSwift() {
|
||||
@ -99,6 +101,14 @@ public class BackupSnapshotCommand extends SnapshotCommand {
|
||||
this.swift = swift;
|
||||
}
|
||||
|
||||
public S3TO getS3() {
|
||||
return s3;
|
||||
}
|
||||
|
||||
public void setS3(S3TO s3) {
|
||||
this.s3 = s3;
|
||||
}
|
||||
|
||||
public Long getSnapshotId() {
|
||||
return snapshotId;
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
package com.cloud.agent.api;
|
||||
|
||||
import com.cloud.agent.api.LogLevel.Log4jLevel;
|
||||
import com.cloud.agent.api.to.S3TO;
|
||||
import com.cloud.agent.api.to.SwiftTO;
|
||||
|
||||
/**
|
||||
@ -26,6 +27,7 @@ import com.cloud.agent.api.to.SwiftTO;
|
||||
public class DeleteSnapshotBackupCommand extends SnapshotCommand {
|
||||
@LogLevel(Log4jLevel.Off)
|
||||
private SwiftTO swift;
|
||||
private S3TO s3;
|
||||
private Boolean all;
|
||||
|
||||
public SwiftTO getSwift() {
|
||||
@ -44,6 +46,10 @@ public class DeleteSnapshotBackupCommand extends SnapshotCommand {
|
||||
this.swift = swift;
|
||||
}
|
||||
|
||||
public S3TO getS3() {
|
||||
return s3;
|
||||
}
|
||||
|
||||
protected DeleteSnapshotBackupCommand() {
|
||||
}
|
||||
|
||||
@ -73,6 +79,7 @@ public class DeleteSnapshotBackupCommand extends SnapshotCommand {
|
||||
* @param childUUID The child VHD file of the backup whose parent is reset to its grandparent.
|
||||
*/
|
||||
public DeleteSnapshotBackupCommand(SwiftTO swift,
|
||||
S3TO s3,
|
||||
String secondaryStoragePoolURL,
|
||||
Long dcId,
|
||||
Long accountId,
|
||||
@ -81,6 +88,7 @@ public class DeleteSnapshotBackupCommand extends SnapshotCommand {
|
||||
{
|
||||
super(null, secondaryStoragePoolURL, backupUUID, null, dcId, accountId, volumeId);
|
||||
setSwift(swift);
|
||||
this.s3 = s3;
|
||||
setAll(all);
|
||||
}
|
||||
}
|
||||
106
api/src/com/cloud/agent/api/DeleteTemplateFromS3Command.java
Normal file
106
api/src/com/cloud/agent/api/DeleteTemplateFromS3Command.java
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.agent.api;
|
||||
|
||||
import com.cloud.agent.api.to.S3TO;
|
||||
|
||||
public class DeleteTemplateFromS3Command extends Command {
|
||||
|
||||
private S3TO s3;
|
||||
private Long templateId;
|
||||
private Long accountId;
|
||||
|
||||
protected DeleteTemplateFromS3Command() {
|
||||
super();
|
||||
}
|
||||
|
||||
public DeleteTemplateFromS3Command(final S3TO s3, final Long accountId,
|
||||
final Long templateId) {
|
||||
|
||||
super();
|
||||
|
||||
this.s3 = s3;
|
||||
this.accountId = accountId;
|
||||
this.templateId = templateId;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result
|
||||
+ ((accountId == null) ? 0 : accountId.hashCode());
|
||||
result = prime * result + ((s3 == null) ? 0 : s3.hashCode());
|
||||
result = prime * result
|
||||
+ ((templateId == null) ? 0 : templateId.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object thatObject) {
|
||||
|
||||
if (this == thatObject) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (thatObject == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getClass() != thatObject.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final DeleteTemplateFromS3Command thatCommand = (DeleteTemplateFromS3Command) thatObject;
|
||||
|
||||
if (!(accountId == thatCommand.accountId)
|
||||
|| (this.accountId != null && this.accountId
|
||||
.equals(thatCommand.accountId))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(templateId == thatCommand.templateId)
|
||||
|| (this.templateId != null && this.templateId
|
||||
.equals(thatCommand.templateId))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public S3TO getS3() {
|
||||
return s3;
|
||||
}
|
||||
|
||||
public Long getTemplateId() {
|
||||
return templateId;
|
||||
}
|
||||
|
||||
public Long getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.agent.api;
|
||||
|
||||
import com.cloud.agent.api.to.S3TO;
|
||||
|
||||
public class DownloadSnapshotFromS3Command extends SnapshotCommand {
|
||||
|
||||
private S3TO s3;
|
||||
private String parent;
|
||||
|
||||
protected DownloadSnapshotFromS3Command() {
|
||||
super();
|
||||
}
|
||||
|
||||
public DownloadSnapshotFromS3Command(S3TO s3, String parent,
|
||||
String secondaryStorageUrl, Long dcId, Long accountId,
|
||||
Long volumeId, String backupUuid, int wait) {
|
||||
|
||||
super("", secondaryStorageUrl, backupUuid, "", dcId, accountId,
|
||||
volumeId);
|
||||
|
||||
this.s3 = s3;
|
||||
this.parent = parent;
|
||||
setWait(wait);
|
||||
|
||||
}
|
||||
|
||||
public S3TO getS3() {
|
||||
return s3;
|
||||
}
|
||||
|
||||
public void setS3(S3TO s3) {
|
||||
this.s3 = s3;
|
||||
}
|
||||
|
||||
public String getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(String parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.agent.api;
|
||||
|
||||
import com.cloud.agent.api.to.S3TO;
|
||||
|
||||
public final class DownloadTemplateFromS3ToSecondaryStorageCommand extends Command {
|
||||
|
||||
private final S3TO s3;
|
||||
private final Long accountId;
|
||||
private final Long templateId;
|
||||
private final String storagePath;
|
||||
|
||||
public DownloadTemplateFromS3ToSecondaryStorageCommand(final S3TO s3,
|
||||
final Long accountId, final Long templateId,
|
||||
final String storagePath, final int wait) {
|
||||
|
||||
super();
|
||||
|
||||
this.s3 = s3;
|
||||
this.accountId = accountId;
|
||||
this.templateId = templateId;
|
||||
this.storagePath = storagePath;
|
||||
|
||||
setWait(wait);
|
||||
|
||||
}
|
||||
|
||||
public S3TO getS3() {
|
||||
return this.s3;
|
||||
}
|
||||
|
||||
public Long getAccountId() {
|
||||
return this.accountId;
|
||||
}
|
||||
|
||||
public Long getTemplateId() {
|
||||
return this.templateId;
|
||||
}
|
||||
|
||||
public String getStoragePath() {
|
||||
return this.storagePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.agent.api;
|
||||
|
||||
import com.cloud.agent.api.to.S3TO;
|
||||
|
||||
public class UploadTemplateToS3FromSecondaryStorageCommand extends Command {
|
||||
|
||||
private final S3TO s3;
|
||||
private final String storagePath;
|
||||
private final Long dataCenterId;
|
||||
private final Long accountId;
|
||||
private final Long templateId;
|
||||
|
||||
public UploadTemplateToS3FromSecondaryStorageCommand(final S3TO s3,
|
||||
final String storagePath, final Long dataCenterId, final Long accountId,
|
||||
final Long templateId) {
|
||||
|
||||
super();
|
||||
|
||||
this.s3 = s3;
|
||||
this.storagePath = storagePath;
|
||||
this.dataCenterId = dataCenterId;
|
||||
this.accountId = accountId;
|
||||
this.templateId = templateId;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object thatObject) {
|
||||
|
||||
if (this == thatObject) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (thatObject == null || getClass() != thatObject.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final UploadTemplateToS3FromSecondaryStorageCommand thatCommand =
|
||||
(UploadTemplateToS3FromSecondaryStorageCommand) thatObject;
|
||||
|
||||
if (this.accountId != null ? !this.accountId.equals(thatCommand
|
||||
.accountId) : thatCommand.accountId != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.dataCenterId != null ? !this.dataCenterId.equals(thatCommand
|
||||
.dataCenterId) : thatCommand.dataCenterId != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.s3 != null ? !this.s3.equals(thatCommand.s3) : thatCommand.s3 != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.storagePath != null ? !this.storagePath.equals(thatCommand
|
||||
.storagePath) : thatCommand.storagePath != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.templateId != null ? !this.templateId.equals(thatCommand.templateId) :
|
||||
thatCommand.templateId != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = this.s3 != null ? this.s3.hashCode() : 0;
|
||||
result = 31 * result + (this.storagePath != null ? this.storagePath.hashCode() : 0);
|
||||
result = 31 * result + (this.dataCenterId != null ? this.dataCenterId.hashCode() : 0);
|
||||
result = 31 * result + (this.accountId != null ? this.accountId.hashCode() : 0);
|
||||
result = 31 * result + (this.templateId != null ? this.templateId.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
public S3TO getS3() {
|
||||
return this.s3;
|
||||
}
|
||||
|
||||
public String getStoragePath() {
|
||||
return this.storagePath;
|
||||
}
|
||||
|
||||
public Long getDataCenterId() {
|
||||
return this.dataCenterId;
|
||||
}
|
||||
|
||||
public Long getAccountId() {
|
||||
return this.accountId;
|
||||
}
|
||||
|
||||
public Long getTemplateId() {
|
||||
return this.templateId;
|
||||
}
|
||||
|
||||
}
|
||||
252
api/src/com/cloud/agent/api/to/S3TO.java
Normal file
252
api/src/com/cloud/agent/api/to/S3TO.java
Normal file
@ -0,0 +1,252 @@
|
||||
// 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.agent.api.to;
|
||||
|
||||
import com.cloud.utils.S3Utils;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public final class S3TO implements S3Utils.ClientOptions {
|
||||
|
||||
private Long id;
|
||||
private String uuid;
|
||||
private String accessKey;
|
||||
private String secretKey;
|
||||
private String endPoint;
|
||||
private String bucketName;
|
||||
private Boolean httpsFlag;
|
||||
private Integer connectionTimeout;
|
||||
private Integer maxErrorRetry;
|
||||
private Integer socketTimeout;
|
||||
private Date created;
|
||||
|
||||
public S3TO() {
|
||||
|
||||
super();
|
||||
|
||||
}
|
||||
|
||||
public S3TO(final Long id, final String uuid, final String accessKey,
|
||||
final String secretKey, final String endPoint,
|
||||
final String bucketName, final Boolean httpsFlag,
|
||||
final Integer connectionTimeout, final Integer maxErrorRetry,
|
||||
final Integer socketTimeout, final Date created) {
|
||||
|
||||
super();
|
||||
|
||||
this.id = id;
|
||||
this.uuid = uuid;
|
||||
this.accessKey = accessKey;
|
||||
this.secretKey = secretKey;
|
||||
this.endPoint = endPoint;
|
||||
this.bucketName = bucketName;
|
||||
this.httpsFlag = httpsFlag;
|
||||
this.connectionTimeout = connectionTimeout;
|
||||
this.maxErrorRetry = maxErrorRetry;
|
||||
this.socketTimeout = socketTimeout;
|
||||
this.created = created;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object thatObject) {
|
||||
|
||||
if (this == thatObject)
|
||||
return true;
|
||||
if (thatObject == null || getClass() != thatObject.getClass())
|
||||
return false;
|
||||
|
||||
final S3TO thatS3TO = (S3TO) thatObject;
|
||||
|
||||
if (httpsFlag != null ? !httpsFlag.equals(thatS3TO.httpsFlag)
|
||||
: thatS3TO.httpsFlag != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (accessKey != null ? !accessKey.equals(thatS3TO.accessKey)
|
||||
: thatS3TO.accessKey != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (connectionTimeout != null ? !connectionTimeout
|
||||
.equals(thatS3TO.connectionTimeout)
|
||||
: thatS3TO.connectionTimeout != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (endPoint != null ? !endPoint.equals(thatS3TO.endPoint)
|
||||
: thatS3TO.endPoint != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (id != null ? !id.equals(thatS3TO.id) : thatS3TO.id != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (uuid != null ? !uuid.equals(thatS3TO.uuid) : thatS3TO.uuid != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (maxErrorRetry != null ? !maxErrorRetry
|
||||
.equals(thatS3TO.maxErrorRetry)
|
||||
: thatS3TO.maxErrorRetry != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (secretKey != null ? !secretKey.equals(thatS3TO.secretKey)
|
||||
: thatS3TO.secretKey != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (socketTimeout != null ? !socketTimeout
|
||||
.equals(thatS3TO.socketTimeout)
|
||||
: thatS3TO.socketTimeout != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bucketName != null ? !bucketName.equals(thatS3TO.bucketName)
|
||||
: thatS3TO.bucketName != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (created != null ? !created.equals(thatS3TO.created)
|
||||
: thatS3TO.created != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = id != null ? id.hashCode() : 0;
|
||||
|
||||
result = 31 * result + (accessKey != null ? accessKey.hashCode() : 0);
|
||||
result = 31 * result + (secretKey != null ? secretKey.hashCode() : 0);
|
||||
result = 31 * result + (endPoint != null ? endPoint.hashCode() : 0);
|
||||
result = 31 * result + (bucketName != null ? bucketName.hashCode() : 0);
|
||||
result = 31 * result + (httpsFlag ? 1 : 0);
|
||||
result = 31
|
||||
* result
|
||||
+ (connectionTimeout != null ? connectionTimeout.hashCode() : 0);
|
||||
result = 31 * result
|
||||
+ (maxErrorRetry != null ? maxErrorRetry.hashCode() : 0);
|
||||
result = 31 * result
|
||||
+ (socketTimeout != null ? socketTimeout.hashCode() : 0);
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(final Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return this.uuid;
|
||||
}
|
||||
|
||||
public void setUuid(final String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccessKey() {
|
||||
return this.accessKey;
|
||||
}
|
||||
|
||||
public void setAccessKey(final String accessKey) {
|
||||
this.accessKey = accessKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSecretKey() {
|
||||
return this.secretKey;
|
||||
}
|
||||
|
||||
public void setSecretKey(final String secretKey) {
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEndPoint() {
|
||||
return this.endPoint;
|
||||
}
|
||||
|
||||
public void setEndPoint(final String endPoint) {
|
||||
this.endPoint = endPoint;
|
||||
}
|
||||
|
||||
public String getBucketName() {
|
||||
return this.bucketName;
|
||||
}
|
||||
|
||||
public void setBucketName(final String bucketName) {
|
||||
this.bucketName = bucketName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isHttps() {
|
||||
return this.httpsFlag;
|
||||
}
|
||||
|
||||
public void setHttps(final Boolean httpsFlag) {
|
||||
this.httpsFlag = httpsFlag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getConnectionTimeout() {
|
||||
return connectionTimeout;
|
||||
}
|
||||
|
||||
public void setConnectionTimeout(final Integer connectionTimeout) {
|
||||
this.connectionTimeout = connectionTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getMaxErrorRetry() {
|
||||
return maxErrorRetry;
|
||||
}
|
||||
|
||||
public void setMaxErrorRetry(final Integer maxErrorRetry) {
|
||||
this.maxErrorRetry = maxErrorRetry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getSocketTimeout() {
|
||||
return socketTimeout;
|
||||
}
|
||||
|
||||
public void setSocketTimeout(final Integer socketTimeout) {
|
||||
this.socketTimeout = socketTimeout;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return this.created;
|
||||
}
|
||||
|
||||
public void setCreated(final Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
}
|
||||
@ -16,6 +16,8 @@
|
||||
// under the License.
|
||||
package com.cloud.api;
|
||||
|
||||
import org.omg.CORBA.PUBLIC_MEMBER;
|
||||
|
||||
public class ApiConstants {
|
||||
public static final String ACCOUNT = "account";
|
||||
public static final String ACCOUNTS = "accounts";
|
||||
@ -383,6 +385,14 @@ public class ApiConstants {
|
||||
public static final String NICIRA_NVP_TRANSPORT_ZONE_UUID = "transportzoneuuid";
|
||||
public static final String NICIRA_NVP_DEVICE_NAME = "niciradevicename";
|
||||
public static final String NICIRA_NVP_GATEWAYSERVICE_UUID = "l3gatewayserviceuuid";
|
||||
public static final String S3_ACCESS_KEY = "accesskey";
|
||||
public static final String S3_SECRET_KEY = "secretkey";
|
||||
public static final String S3_END_POINT = "endpoint";
|
||||
public static final String S3_BUCKET_NAME = "bucket";
|
||||
public static final String S3_HTTPS_FLAG = "usehttps";
|
||||
public static final String S3_CONNECTION_TIMEOUT = "connectiontimeout";
|
||||
public static final String S3_MAX_ERROR_RETRY = "maxerrorretry";
|
||||
public static final String S3_SOCKET_TIMEOUT = "sockettimeout";
|
||||
|
||||
public static final String SOURCE = "source";
|
||||
public static final String COUNTER_ID = "counterid";
|
||||
|
||||
@ -64,6 +64,7 @@ import com.cloud.api.response.RemoteAccessVpnResponse;
|
||||
import com.cloud.api.response.ResourceCountResponse;
|
||||
import com.cloud.api.response.ResourceLimitResponse;
|
||||
import com.cloud.api.response.ResourceTagResponse;
|
||||
import com.cloud.api.response.S3Response;
|
||||
import com.cloud.api.response.SecurityGroupResponse;
|
||||
import com.cloud.api.response.ServiceOfferingResponse;
|
||||
import com.cloud.api.response.ServiceResponse;
|
||||
@ -141,6 +142,7 @@ import com.cloud.projects.Project;
|
||||
import com.cloud.projects.ProjectAccount;
|
||||
import com.cloud.projects.ProjectInvitation;
|
||||
import com.cloud.server.ResourceTag;
|
||||
import com.cloud.storage.S3;
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.storage.StoragePool;
|
||||
import com.cloud.storage.Swift;
|
||||
@ -287,6 +289,8 @@ public interface ResponseGenerator {
|
||||
|
||||
SwiftResponse createSwiftResponse(Swift swift);
|
||||
|
||||
S3Response createS3Response(S3 result);
|
||||
|
||||
PhysicalNetworkResponse createPhysicalNetworkResponse(PhysicalNetwork result);
|
||||
|
||||
ServiceResponse createNetworkServiceResponse(Service service);
|
||||
@ -361,4 +365,5 @@ public interface ResponseGenerator {
|
||||
AutoScaleVmProfileResponse createAutoScaleVmProfileResponse(AutoScaleVmProfile profile);
|
||||
|
||||
AutoScaleVmGroupResponse createAutoScaleVmGroupResponse(AutoScaleVmGroup vmGroup);
|
||||
|
||||
}
|
||||
|
||||
218
api/src/com/cloud/api/commands/AddS3Cmd.java
Normal file
218
api/src/com/cloud/api/commands/AddS3Cmd.java
Normal file
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* 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.api.commands;
|
||||
|
||||
import static com.cloud.api.ApiConstants.S3_ACCESS_KEY;
|
||||
import static com.cloud.api.ApiConstants.S3_CONNECTION_TIMEOUT;
|
||||
import static com.cloud.api.ApiConstants.S3_END_POINT;
|
||||
import static com.cloud.api.ApiConstants.S3_HTTPS_FLAG;
|
||||
import static com.cloud.api.ApiConstants.S3_MAX_ERROR_RETRY;
|
||||
import static com.cloud.api.ApiConstants.S3_SECRET_KEY;
|
||||
import static com.cloud.api.ApiConstants.S3_SOCKET_TIMEOUT;
|
||||
import static com.cloud.api.ApiConstants.S3_BUCKET_NAME;
|
||||
import static com.cloud.api.BaseCmd.CommandType.INTEGER;
|
||||
import static com.cloud.api.BaseCmd.CommandType.STRING;
|
||||
import static com.cloud.api.BaseCmd.CommandType.BOOLEAN;
|
||||
import static com.cloud.user.Account.ACCOUNT_ID_SYSTEM;
|
||||
|
||||
import com.cloud.api.BaseCmd;
|
||||
import com.cloud.api.Implementation;
|
||||
import com.cloud.api.Parameter;
|
||||
import com.cloud.api.ServerApiException;
|
||||
import com.cloud.api.response.S3Response;
|
||||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.exception.DiscoveryException;
|
||||
import com.cloud.exception.InsufficientCapacityException;
|
||||
import com.cloud.exception.NetworkRuleConflictException;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.storage.S3;
|
||||
|
||||
@Implementation(description = "Adds S3", responseObject = S3Response.class, since = "4.0.0")
|
||||
public final class AddS3Cmd extends BaseCmd {
|
||||
|
||||
private static String COMMAND_NAME = "adds3response";
|
||||
|
||||
@Parameter(name = S3_ACCESS_KEY, type = STRING, required = true,
|
||||
description = "S3 access key")
|
||||
private String accessKey;
|
||||
|
||||
@Parameter(name = S3_SECRET_KEY, type = STRING, required = true,
|
||||
description = "S3 secret key")
|
||||
private String secretKey;
|
||||
|
||||
@Parameter(name = S3_END_POINT, type = STRING, required = false,
|
||||
description = "S3 host name")
|
||||
private String endPoint = null;
|
||||
|
||||
@Parameter(name = S3_BUCKET_NAME, type = STRING, required = true,
|
||||
description = "name of the template storage bucket")
|
||||
private String bucketName;
|
||||
|
||||
@Parameter(name = S3_HTTPS_FLAG, type = BOOLEAN, required = false,
|
||||
description = "connect to the S3 endpoint via HTTPS?")
|
||||
private Boolean httpsFlag = null;
|
||||
|
||||
@Parameter(name = S3_CONNECTION_TIMEOUT, type = INTEGER, required = false,
|
||||
description = "connection timeout (milliseconds)")
|
||||
private Integer connectionTimeout = null;
|
||||
|
||||
@Parameter(name = S3_MAX_ERROR_RETRY, type = INTEGER, required = false,
|
||||
description = "maximum number of times to retry on error")
|
||||
private Integer maxErrorRetry = null;
|
||||
|
||||
@Parameter(name = S3_SOCKET_TIMEOUT, type = INTEGER, required = false,
|
||||
description = "socket timeout (milliseconds)")
|
||||
private Integer socketTimeout = null;
|
||||
|
||||
@Override
|
||||
public void execute() throws ResourceUnavailableException, InsufficientCapacityException,
|
||||
ServerApiException, ConcurrentOperationException, ResourceAllocationException,
|
||||
NetworkRuleConflictException {
|
||||
|
||||
final S3 result;
|
||||
|
||||
try {
|
||||
|
||||
result = _resourceService.discoverS3(this);
|
||||
|
||||
if (result == null) {
|
||||
throw new ServerApiException(INTERNAL_ERROR, "Failed to add S3.");
|
||||
}
|
||||
|
||||
} catch (DiscoveryException e) {
|
||||
|
||||
throw new ServerApiException(INTERNAL_ERROR, "Failed to add S3 due to " + e.getMessage());
|
||||
|
||||
}
|
||||
|
||||
final S3Response response = _responseGenerator.createS3Response(result);
|
||||
response.setResponseName(this.getCommandName());
|
||||
this.setResponseObject(response);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object thatObject) {
|
||||
|
||||
if (this == thatObject) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (thatObject == null || this.getClass() != thatObject.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final AddS3Cmd thatAddS3Cmd = (AddS3Cmd) thatObject;
|
||||
|
||||
if (this.httpsFlag != null ? !this.httpsFlag.equals(thatAddS3Cmd.httpsFlag) : thatAddS3Cmd.httpsFlag != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.accessKey != null ? !this.accessKey.equals(thatAddS3Cmd.accessKey) : thatAddS3Cmd.accessKey != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.connectionTimeout != null ? !this.connectionTimeout.equals(thatAddS3Cmd.connectionTimeout) : thatAddS3Cmd.connectionTimeout != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.endPoint != null ? !this.endPoint.equals(thatAddS3Cmd.endPoint) : thatAddS3Cmd.endPoint != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.maxErrorRetry != null ? !this.maxErrorRetry.equals(thatAddS3Cmd.maxErrorRetry) : thatAddS3Cmd.maxErrorRetry != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.secretKey != null ? !this.secretKey.equals(thatAddS3Cmd.secretKey) : thatAddS3Cmd.secretKey != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.socketTimeout != null ? !this.socketTimeout.equals(thatAddS3Cmd.socketTimeout) : thatAddS3Cmd.socketTimeout != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.bucketName != null ? !this.bucketName.equals(thatAddS3Cmd.bucketName) : thatAddS3Cmd.bucketName != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = this.accessKey != null ? this.accessKey.hashCode() : 0;
|
||||
result = 31 * result + (this.secretKey != null ? this.secretKey.hashCode() : 0);
|
||||
result = 31 * result + (this.endPoint != null ? this.endPoint.hashCode() : 0);
|
||||
result = 31 * result + (this.bucketName != null ? this.bucketName.hashCode() : 0);
|
||||
result = 31 * result + (this.httpsFlag != null && this.httpsFlag == true ? 1 : 0);
|
||||
result = 31 * result + (this.connectionTimeout != null ? this.connectionTimeout.hashCode() : 0);
|
||||
result = 31 * result + (this.maxErrorRetry != null ? this.maxErrorRetry.hashCode() : 0);
|
||||
result = 31 * result + (this.socketTimeout != null ? this.socketTimeout.hashCode() : 0);
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return COMMAND_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityOwnerId() {
|
||||
return ACCOUNT_ID_SYSTEM;
|
||||
}
|
||||
|
||||
public String getAccessKey() {
|
||||
return this.accessKey;
|
||||
}
|
||||
|
||||
public String getSecretKey() {
|
||||
return this.secretKey;
|
||||
}
|
||||
|
||||
public String getEndPoint() {
|
||||
return this.endPoint;
|
||||
}
|
||||
|
||||
public String getBucketName() {
|
||||
return this.bucketName;
|
||||
}
|
||||
|
||||
public Boolean getHttpsFlag() {
|
||||
return this.httpsFlag;
|
||||
}
|
||||
|
||||
public Integer getConnectionTimeout() {
|
||||
return this.connectionTimeout;
|
||||
}
|
||||
|
||||
public Integer getMaxErrorRetry() {
|
||||
return this.maxErrorRetry;
|
||||
}
|
||||
|
||||
public Integer getSocketTimeout() {
|
||||
return this.socketTimeout;
|
||||
}
|
||||
|
||||
}
|
||||
112
api/src/com/cloud/api/commands/ListS3sCmd.java
Normal file
112
api/src/com/cloud/api/commands/ListS3sCmd.java
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.api.commands;
|
||||
|
||||
import static com.cloud.api.ApiConstants.ID;
|
||||
import static com.cloud.api.BaseCmd.CommandType.LONG;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.api.BaseListCmd;
|
||||
import com.cloud.api.Implementation;
|
||||
import com.cloud.api.Parameter;
|
||||
import com.cloud.api.ServerApiException;
|
||||
import com.cloud.api.response.ListResponse;
|
||||
import com.cloud.api.response.S3Response;
|
||||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.exception.InsufficientCapacityException;
|
||||
import com.cloud.exception.NetworkRuleConflictException;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.storage.S3;
|
||||
|
||||
@Implementation(description = "Lists S3s", responseObject = S3Response.class, since = "4.0.0")
|
||||
public final class ListS3sCmd extends BaseListCmd {
|
||||
|
||||
private static final String COMMAND_NAME = "lists3sresponse";
|
||||
|
||||
@Parameter(name = ID, type = LONG, required = true, description = "The ID of the S3")
|
||||
private Long id;
|
||||
|
||||
@Override
|
||||
public void execute() throws ResourceUnavailableException, InsufficientCapacityException,
|
||||
ServerApiException, ConcurrentOperationException, ResourceAllocationException,
|
||||
NetworkRuleConflictException {
|
||||
|
||||
final List<? extends S3> result = _resourceService.listS3s(this);
|
||||
final ListResponse<S3Response> response = new ListResponse<S3Response>();
|
||||
final List<S3Response> s3Responses = new ArrayList<S3Response>();
|
||||
|
||||
if (result != null) {
|
||||
|
||||
for (S3 s3 : result) {
|
||||
|
||||
S3Response s3Response = _responseGenerator.createS3Response(s3);
|
||||
s3Response.setResponseName(this.getCommandName());
|
||||
s3Response.setObjectName("s3");
|
||||
s3Responses.add(s3Response);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
response.setResponses(s3Responses);
|
||||
response.setResponseName(this.getCommandName());
|
||||
|
||||
this.setResponseObject(response);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object thatObject) {
|
||||
|
||||
if (this == thatObject) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (thatObject == null || getClass() != thatObject.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ListS3sCmd thatListS3sCmd = (ListS3sCmd) thatObject;
|
||||
|
||||
if (this.id != null ? !this.id.equals(thatListS3sCmd.id) : thatListS3sCmd.id != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.id != null ? this.id.hashCode() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return COMMAND_NAME;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
}
|
||||
209
api/src/com/cloud/api/response/S3Response.java
Normal file
209
api/src/com/cloud/api/response/S3Response.java
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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.api.response;
|
||||
|
||||
import com.cloud.serializer.Param;
|
||||
import com.cloud.utils.IdentityProxy;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import static com.cloud.api.ApiConstants.*;
|
||||
|
||||
public class S3Response extends BaseResponse {
|
||||
|
||||
@SerializedName(ID)
|
||||
@Param(description = "The ID of the S3 configuration")
|
||||
private IdentityProxy id = new IdentityProxy("s3");
|
||||
|
||||
@SerializedName(S3_ACCESS_KEY)
|
||||
@Param(description = "The S3 access key")
|
||||
private String accessKey;
|
||||
|
||||
@SerializedName(S3_SECRET_KEY)
|
||||
@Param(description = "The S3 secret key")
|
||||
private String secretKey;
|
||||
|
||||
@SerializedName(S3_END_POINT)
|
||||
@Param(description = "The S3 end point")
|
||||
private String endPoint;
|
||||
|
||||
@SerializedName(S3_BUCKET_NAME)
|
||||
@Param(description = "The name of the template storage bucket")
|
||||
private String bucketName;
|
||||
|
||||
@SerializedName(S3_HTTPS_FLAG)
|
||||
@Param(description = "Connect to S3 using HTTPS?")
|
||||
private Integer httpsFlag;
|
||||
|
||||
@SerializedName(S3_CONNECTION_TIMEOUT)
|
||||
@Param(description = "The connection timeout (milliseconds)")
|
||||
private Integer connectionTimeout;
|
||||
|
||||
@SerializedName(S3_MAX_ERROR_RETRY)
|
||||
@Param(description = "The maximum number of time to retry a connection on error.")
|
||||
private Integer maxErrorRetry;
|
||||
|
||||
@SerializedName(S3_SOCKET_TIMEOUT)
|
||||
@Param(description = "The connection socket (milliseconds)")
|
||||
private Integer socketTimeout;
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object thatObject) {
|
||||
|
||||
if (this == thatObject) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (thatObject == null || this.getClass() != thatObject.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final S3Response thatS3Response = (S3Response) thatObject;
|
||||
|
||||
if (this.httpsFlag != null ? !this.httpsFlag.equals(thatS3Response.httpsFlag) : thatS3Response.httpsFlag != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.accessKey != null ? !this.accessKey.equals(thatS3Response.accessKey) : thatS3Response.accessKey != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.connectionTimeout != null ? !this.connectionTimeout.equals(thatS3Response.connectionTimeout) : thatS3Response.connectionTimeout != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.endPoint != null ? !this.endPoint.equals(thatS3Response.endPoint) : thatS3Response.endPoint != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.id != null ? !this.id.equals(thatS3Response.id) : thatS3Response.id != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.maxErrorRetry != null ? !this.maxErrorRetry.equals(thatS3Response.maxErrorRetry) : thatS3Response.maxErrorRetry != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.secretKey != null ? !this.secretKey.equals(thatS3Response.secretKey) : thatS3Response.secretKey != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.socketTimeout != null ? !this.socketTimeout.equals(thatS3Response.socketTimeout) : thatS3Response.socketTimeout != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.bucketName != null ? !this.bucketName.equals(thatS3Response.bucketName) : thatS3Response.bucketName != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = this.id != null ? this.id.hashCode() : 0;
|
||||
result = 31 * result + (this.accessKey != null ? this.accessKey.hashCode() : 0);
|
||||
result = 31 * result + (this.secretKey != null ? this.secretKey.hashCode() : 0);
|
||||
result = 31 * result + (this.endPoint != null ? this.endPoint.hashCode() : 0);
|
||||
result = 31 * result + (this.bucketName != null ? this.bucketName.hashCode() : 0);
|
||||
result = 31 * result + (this.httpsFlag != null ? this.httpsFlag : 0);
|
||||
result = 31 * result + (this.connectionTimeout != null ? this.connectionTimeout.hashCode() : 0);
|
||||
result = 31 * result + (this.maxErrorRetry != null ? this.maxErrorRetry.hashCode() : 0);
|
||||
result = 31 * result + (this.socketTimeout != null ? this.socketTimeout.hashCode() : 0);
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getObjectId() {
|
||||
return this.id.getValue();
|
||||
}
|
||||
|
||||
public void setObjectId(Long id) {
|
||||
this.id.setValue(id);
|
||||
}
|
||||
|
||||
public String getAccessKey() {
|
||||
return this.accessKey;
|
||||
}
|
||||
|
||||
public void setAccessKey(final String accessKey) {
|
||||
this.accessKey = accessKey;
|
||||
}
|
||||
|
||||
public String getSecretKey() {
|
||||
return this.secretKey;
|
||||
}
|
||||
|
||||
public void setSecretKey(final String secretKey) {
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
|
||||
public String getEndPoint() {
|
||||
return this.endPoint;
|
||||
}
|
||||
|
||||
public void setEndPoint(final String endPoint) {
|
||||
this.endPoint = endPoint;
|
||||
}
|
||||
|
||||
|
||||
public String getTemplateBucketName() {
|
||||
return this.bucketName;
|
||||
}
|
||||
|
||||
public void setTemplateBucketName(final String templateBucketName) {
|
||||
this.bucketName = templateBucketName;
|
||||
}
|
||||
|
||||
public Integer getHttpsFlag() {
|
||||
return this.httpsFlag;
|
||||
}
|
||||
|
||||
public void setHttpsFlag(final Integer httpsFlag) {
|
||||
this.httpsFlag = httpsFlag;
|
||||
}
|
||||
|
||||
public Integer getConnectionTimeout() {
|
||||
return this.connectionTimeout;
|
||||
}
|
||||
|
||||
public void setConnectionTimeout(final Integer connectionTimeout) {
|
||||
this.connectionTimeout = connectionTimeout;
|
||||
}
|
||||
|
||||
public Integer getMaxErrorRetry() {
|
||||
return this.maxErrorRetry;
|
||||
}
|
||||
|
||||
public void setMaxErrorRetry(final Integer maxErrorRetry) {
|
||||
this.maxErrorRetry = maxErrorRetry;
|
||||
}
|
||||
|
||||
public Integer getSocketTimeout() {
|
||||
return this.socketTimeout;
|
||||
}
|
||||
|
||||
public void setSocketTimeout(final Integer socketTimeout) {
|
||||
this.socketTimeout = socketTimeout;
|
||||
}
|
||||
|
||||
}
|
||||
@ -20,10 +20,12 @@ import java.util.List;
|
||||
|
||||
import com.cloud.api.commands.AddClusterCmd;
|
||||
import com.cloud.api.commands.AddHostCmd;
|
||||
import com.cloud.api.commands.AddS3Cmd;
|
||||
import com.cloud.api.commands.AddSecondaryStorageCmd;
|
||||
import com.cloud.api.commands.AddSwiftCmd;
|
||||
import com.cloud.api.commands.CancelMaintenanceCmd;
|
||||
import com.cloud.api.commands.DeleteClusterCmd;
|
||||
import com.cloud.api.commands.ListS3sCmd;
|
||||
import com.cloud.api.commands.ListSwiftsCmd;
|
||||
import com.cloud.api.commands.PrepareForMaintenanceCmd;
|
||||
import com.cloud.api.commands.ReconnectHostCmd;
|
||||
@ -35,6 +37,7 @@ import com.cloud.exception.ResourceInUseException;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||
import com.cloud.org.Cluster;
|
||||
import com.cloud.storage.S3;
|
||||
import com.cloud.storage.Swift;
|
||||
import com.cloud.utils.fsm.NoTransitionException;
|
||||
|
||||
@ -93,8 +96,13 @@ public interface ResourceService {
|
||||
Cluster getCluster(Long clusterId);
|
||||
|
||||
Swift discoverSwift(AddSwiftCmd addSwiftCmd) throws DiscoveryException;
|
||||
|
||||
S3 discoverS3(AddS3Cmd cmd) throws DiscoveryException;
|
||||
|
||||
List<HypervisorType> getSupportedHypervisorTypes(long zoneId, boolean forVirtualRouter, Long podId);
|
||||
|
||||
List<? extends Swift> listSwifts(ListSwiftsCmd cmd);
|
||||
|
||||
List<? extends S3> listS3s(ListS3sCmd cmd);
|
||||
|
||||
}
|
||||
|
||||
51
api/src/com/cloud/storage/S3.java
Normal file
51
api/src/com/cloud/storage/S3.java
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.storage;
|
||||
|
||||
import com.cloud.agent.api.to.S3TO;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public interface S3 {
|
||||
|
||||
long getId();
|
||||
|
||||
String getUuid();
|
||||
|
||||
String getAccessKey();
|
||||
|
||||
String getSecretKey();
|
||||
|
||||
String getEndPoint();
|
||||
|
||||
String getBucketName();
|
||||
|
||||
Integer getHttpsFlag();
|
||||
|
||||
Integer getConnectionTimeout();
|
||||
|
||||
Integer getMaxErrorRetry();
|
||||
|
||||
Integer getSocketTimeout();
|
||||
|
||||
Date getCreated();
|
||||
|
||||
S3TO toS3TO();
|
||||
|
||||
}
|
||||
@ -32,6 +32,17 @@ label.destroy=Destroy
|
||||
label.restore=Restore
|
||||
label.isolation.uri=Isolation URI
|
||||
label.broadcast.uri=Broadcast URI
|
||||
label.enable.s3=Enable S3-backed Secondary Storage
|
||||
confirm.enable.s3=Please fill in the following information to enable support for S3-backed Secondary Storage
|
||||
message.after.enable.s3=S3-backed Secondary Storage configured. Note: When you leave this page, you will not be able to re-configure S3 again.
|
||||
label.s3.access_key=Access Key
|
||||
label.s3.secret_key=Secret Key
|
||||
label.s3.bucket=Bucket
|
||||
label.s3.endpoint=Endpoint
|
||||
label.s3.use_https=Use HTTPS
|
||||
label.s3.connection_timeout=Connection Timeout
|
||||
label.s3.max_error_retry=Max Error Retry
|
||||
label.s3.socket_timeout=Socket Timeout
|
||||
#new labels (end) ************************************************************************************************
|
||||
|
||||
|
||||
|
||||
@ -227,6 +227,9 @@ listCapacity=com.cloud.api.commands.ListCapacityCmd;3
|
||||
addSwift=com.cloud.api.commands.AddSwiftCmd;1
|
||||
listSwifts=com.cloud.api.commands.ListSwiftsCmd;1
|
||||
|
||||
#### s3 commands
|
||||
addS3=com.cloud.api.commands.AddS3Cmd;1
|
||||
listS3s=com.cloud.api.commands.ListS3sCmd;1
|
||||
|
||||
#### host commands
|
||||
addHost=com.cloud.api.commands.AddHostCmd;3
|
||||
|
||||
@ -43,6 +43,11 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>${cs.codec.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<defaultGoal>install</defaultGoal>
|
||||
|
||||
208
core/src/com/cloud/storage/S3VO.java
Normal file
208
core/src/com/cloud/storage/S3VO.java
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* 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.storage;
|
||||
|
||||
import com.cloud.agent.api.to.S3TO;
|
||||
import com.cloud.api.Identity;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import java.util.Date;
|
||||
|
||||
@Entity
|
||||
@Table(name = "s3")
|
||||
public class S3VO implements S3, Identity {
|
||||
|
||||
public static final String ID_COLUMN_NAME = "id";
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = ID_COLUMN_NAME)
|
||||
private long id;
|
||||
|
||||
@Column(name = "uuid")
|
||||
private String uuid;
|
||||
|
||||
@Column(name = "access_key")
|
||||
private String accessKey;
|
||||
|
||||
@Column(name = "secret_key")
|
||||
private String secretKey;
|
||||
|
||||
@Column(name = "end_point")
|
||||
private String endPoint;
|
||||
|
||||
@Column(name = "bucket")
|
||||
private String bucketName;
|
||||
|
||||
@Column(name = "https")
|
||||
private Integer httpsFlag;
|
||||
|
||||
@Column(name = "connection_timeout")
|
||||
private Integer connectionTimeout;
|
||||
|
||||
@Column(name = "max_error_retry")
|
||||
private Integer maxErrorRetry;
|
||||
|
||||
@Column(name = "socket_timeout")
|
||||
private Integer socketTimeout;
|
||||
|
||||
@Column(name = GenericDao.CREATED_COLUMN)
|
||||
private Date created;
|
||||
|
||||
public S3VO() {
|
||||
super();
|
||||
}
|
||||
|
||||
public S3VO(final String uuid, final String accessKey,
|
||||
final String secretKey, final String endPoint,
|
||||
final String bucketName, final Boolean httpsFlag,
|
||||
final Integer connectionTimeout, final Integer maxErrorRetry,
|
||||
final Integer socketTimeout, final Date created) {
|
||||
|
||||
super();
|
||||
|
||||
this.uuid = uuid;
|
||||
this.accessKey = accessKey;
|
||||
this.secretKey = secretKey;
|
||||
this.endPoint = endPoint;
|
||||
this.bucketName = bucketName;
|
||||
|
||||
Integer value = null;
|
||||
if (httpsFlag != null) {
|
||||
value = httpsFlag == false ? 0 : 1;
|
||||
}
|
||||
this.httpsFlag = value;
|
||||
|
||||
this.connectionTimeout = connectionTimeout;
|
||||
this.maxErrorRetry = maxErrorRetry;
|
||||
this.socketTimeout = socketTimeout;
|
||||
this.created = created;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public S3TO toS3TO() {
|
||||
|
||||
Boolean httpsFlag = null;
|
||||
if (this.httpsFlag != null) {
|
||||
httpsFlag = this.httpsFlag == 0 ? false : true;
|
||||
}
|
||||
|
||||
return new S3TO(this.id, this.uuid, this.accessKey, this.secretKey,
|
||||
this.endPoint, this.bucketName, httpsFlag,
|
||||
this.connectionTimeout, this.maxErrorRetry, this.socketTimeout,
|
||||
this.created);
|
||||
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(final long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return this.uuid;
|
||||
}
|
||||
|
||||
public void setUuid(final String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public String getAccessKey() {
|
||||
return this.accessKey;
|
||||
}
|
||||
|
||||
public void setAccessKey(final String accessKey) {
|
||||
this.accessKey = accessKey;
|
||||
}
|
||||
|
||||
public String getSecretKey() {
|
||||
return this.secretKey;
|
||||
}
|
||||
|
||||
public void setSecretKey(final String secretKey) {
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
|
||||
public String getEndPoint() {
|
||||
return this.endPoint;
|
||||
}
|
||||
|
||||
public void setEndPoint(final String endPoint) {
|
||||
this.endPoint = endPoint;
|
||||
}
|
||||
|
||||
public String getBucketName() {
|
||||
return this.bucketName;
|
||||
}
|
||||
|
||||
public void setBucketName(final String bucketName) {
|
||||
this.bucketName = bucketName;
|
||||
}
|
||||
|
||||
public Integer getHttpsFlag() {
|
||||
return this.httpsFlag;
|
||||
}
|
||||
|
||||
public void setHttpsFlag(final Integer httpsFlag) {
|
||||
this.httpsFlag = httpsFlag;
|
||||
}
|
||||
|
||||
public Integer getConnectionTimeout() {
|
||||
return this.connectionTimeout;
|
||||
}
|
||||
|
||||
public void setConnectionTimeout(final int connectionTimeout) {
|
||||
this.connectionTimeout = connectionTimeout;
|
||||
}
|
||||
|
||||
public Integer getMaxErrorRetry() {
|
||||
return this.maxErrorRetry;
|
||||
}
|
||||
|
||||
public void setMaxErrorRetry(final int maxErrorRetry) {
|
||||
this.maxErrorRetry = maxErrorRetry;
|
||||
}
|
||||
|
||||
public Integer getSocketTimeout() {
|
||||
return this.socketTimeout;
|
||||
}
|
||||
|
||||
public void setSocketTimeout(final int socketTimeout) {
|
||||
this.socketTimeout = socketTimeout;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return this.created;
|
||||
}
|
||||
|
||||
public void setCreated(final Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
}
|
||||
@ -91,6 +91,9 @@ public class SnapshotVO implements Snapshot, Identity {
|
||||
@Column(name="swift_id")
|
||||
Long swiftId;
|
||||
|
||||
@Column(name="s3_id")
|
||||
Long s3Id;
|
||||
|
||||
@Column(name="sechost_id")
|
||||
Long secHostId;
|
||||
|
||||
@ -289,4 +292,13 @@ public class SnapshotVO implements Snapshot, Identity {
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public Long getS3Id() {
|
||||
return s3Id;
|
||||
}
|
||||
|
||||
public void setS3Id(Long s3Id) {
|
||||
this.s3Id = s3Id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
203
core/src/com/cloud/storage/VMTemplateS3VO.java
Normal file
203
core/src/com/cloud/storage/VMTemplateS3VO.java
Normal file
@ -0,0 +1,203 @@
|
||||
/*
|
||||
* 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.storage;
|
||||
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
@Entity
|
||||
@Table(name = "template_s3_ref")
|
||||
public class VMTemplateS3VO {
|
||||
|
||||
public static final String S3_ID_COLUMN_NAME = "s3_id";
|
||||
|
||||
public static final String TEMPLATE_ID_COLUMN_NAME = "template_id";
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
|
||||
@Column(name = S3_ID_COLUMN_NAME)
|
||||
private long s3Id;
|
||||
|
||||
@Column(name = TEMPLATE_ID_COLUMN_NAME)
|
||||
private long templateId;
|
||||
|
||||
@Column(name = GenericDaoBase.CREATED_COLUMN)
|
||||
private Date created;
|
||||
|
||||
@Column(name = "size")
|
||||
private Long size;
|
||||
|
||||
@Column(name = "physical_size")
|
||||
private Long physicalSize;
|
||||
|
||||
public VMTemplateS3VO() {
|
||||
super();
|
||||
}
|
||||
|
||||
public VMTemplateS3VO(final long s3Id, final long templateId,
|
||||
final Date created, final Long size, final Long physicalSize) {
|
||||
|
||||
super();
|
||||
|
||||
this.s3Id = s3Id;
|
||||
this.templateId = templateId;
|
||||
this.created = created;
|
||||
this.size = size;
|
||||
this.physicalSize = physicalSize;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object thatObject) {
|
||||
|
||||
if (this == thatObject) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (thatObject == null || getClass() != thatObject.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final VMTemplateS3VO thatVMTemplateS3VO = (VMTemplateS3VO) thatObject;
|
||||
|
||||
if (this.id != thatVMTemplateS3VO.id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.s3Id != thatVMTemplateS3VO.s3Id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.templateId != thatVMTemplateS3VO.templateId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.created != null ? !created.equals(thatVMTemplateS3VO.created)
|
||||
: thatVMTemplateS3VO.created != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.physicalSize != null ? !physicalSize
|
||||
.equals(thatVMTemplateS3VO.physicalSize)
|
||||
: thatVMTemplateS3VO.physicalSize != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.size != null ? !size.equals(thatVMTemplateS3VO.size)
|
||||
: thatVMTemplateS3VO.size != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = (int) (this.id ^ (this.id >>> 32));
|
||||
|
||||
result = 31 * result + (int) (this.s3Id ^ (this.s3Id >>> 32));
|
||||
result = 31 * result
|
||||
+ (int) (this.templateId ^ (this.templateId >>> 32));
|
||||
result = 31 * result
|
||||
+ (this.created != null ? this.created.hashCode() : 0);
|
||||
result = 31 * result + (this.size != null ? this.size.hashCode() : 0);
|
||||
result = 31
|
||||
* result
|
||||
+ (this.physicalSize != null ? this.physicalSize.hashCode() : 0);
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(final long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public long getS3Id() {
|
||||
return this.s3Id;
|
||||
}
|
||||
|
||||
public void setS3Id(final long s3Id) {
|
||||
this.s3Id = s3Id;
|
||||
}
|
||||
|
||||
public long getTemplateId() {
|
||||
return this.templateId;
|
||||
}
|
||||
|
||||
public void setTemplateId(final long templateId) {
|
||||
this.templateId = templateId;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return this.created;
|
||||
}
|
||||
|
||||
public void setCreated(final Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public Long getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public void setSize(final Long size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public Long getPhysicalSize() {
|
||||
return this.physicalSize;
|
||||
}
|
||||
|
||||
public void setPhysicalSize(final Long physicalSize) {
|
||||
this.physicalSize = physicalSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
final StringBuilder stringBuilder = new StringBuilder(
|
||||
"VMTemplateS3VO [ id: ").append(id).append(", created: ")
|
||||
.append(DateFormat.getDateTimeInstance().format(created))
|
||||
.append(", physicalSize: ").append(physicalSize)
|
||||
.append(", size: ").append(size).append(", templateId: ")
|
||||
.append(templateId).append(", s3Id: ").append(s3Id)
|
||||
.append(" ]");
|
||||
|
||||
return stringBuilder.toString();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -16,10 +16,20 @@
|
||||
// under the License.
|
||||
package com.cloud.storage.resource;
|
||||
|
||||
import static com.cloud.utils.S3Utils.deleteDirectory;
|
||||
import static com.cloud.utils.S3Utils.getDirectory;
|
||||
import static com.cloud.utils.S3Utils.putDirectory;
|
||||
import static com.cloud.utils.StringUtils.join;
|
||||
import static com.cloud.utils.db.GlobalLock.executeWithNoWaitLock;
|
||||
import static java.lang.String.format;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.apache.commons.lang.StringUtils.substringAfterLast;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
@ -32,6 +42,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
@ -46,6 +57,9 @@ import com.cloud.agent.api.ComputeChecksumCommand;
|
||||
import com.cloud.agent.api.DeleteObjectFromSwiftCommand;
|
||||
import com.cloud.agent.api.DeleteSnapshotBackupCommand;
|
||||
import com.cloud.agent.api.DeleteSnapshotsDirCommand;
|
||||
import com.cloud.agent.api.DeleteTemplateFromS3Command;
|
||||
import com.cloud.agent.api.DownloadSnapshotFromS3Command;
|
||||
import com.cloud.agent.api.DownloadTemplateFromS3ToSecondaryStorageCommand;
|
||||
import com.cloud.agent.api.GetStorageStatsAnswer;
|
||||
import com.cloud.agent.api.GetStorageStatsCommand;
|
||||
import com.cloud.agent.api.PingCommand;
|
||||
@ -60,6 +74,8 @@ import com.cloud.agent.api.SecStorageSetupCommand.Certificates;
|
||||
import com.cloud.agent.api.StartupSecondaryStorageCommand;
|
||||
import com.cloud.agent.api.SecStorageVMSetupCommand;
|
||||
import com.cloud.agent.api.StartupCommand;
|
||||
import com.cloud.agent.api.StartupSecondaryStorageCommand;
|
||||
import com.cloud.agent.api.UploadTemplateToS3FromSecondaryStorageCommand;
|
||||
import com.cloud.agent.api.downloadSnapshotFromSwiftCommand;
|
||||
import com.cloud.agent.api.downloadTemplateFromSwiftToSecondaryStorageCommand;
|
||||
import com.cloud.agent.api.uploadTemplateToSwiftFromSecondaryStorageCommand;
|
||||
@ -75,6 +91,7 @@ import com.cloud.agent.api.storage.ListVolumeAnswer;
|
||||
import com.cloud.agent.api.storage.ListVolumeCommand;
|
||||
import com.cloud.agent.api.storage.UploadCommand;
|
||||
import com.cloud.agent.api.storage.ssCommand;
|
||||
import com.cloud.agent.api.to.S3TO;
|
||||
import com.cloud.agent.api.to.SwiftTO;
|
||||
import com.cloud.api.commands.DeleteVolumeCmd;
|
||||
import com.cloud.exception.InternalErrorException;
|
||||
@ -90,6 +107,9 @@ import com.cloud.storage.template.TemplateLocation;
|
||||
import com.cloud.storage.template.UploadManager;
|
||||
import com.cloud.storage.template.UploadManagerImpl;
|
||||
import com.cloud.utils.NumbersUtil;
|
||||
import com.cloud.utils.S3Utils;
|
||||
import com.cloud.utils.S3Utils.FileNamingStrategy;
|
||||
import com.cloud.utils.S3Utils.ObjectNamingStrategy;
|
||||
import com.cloud.utils.component.ComponentLocator;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.net.NetUtils;
|
||||
@ -97,8 +117,15 @@ import com.cloud.utils.script.OutputInterpreter;
|
||||
import com.cloud.utils.script.Script;
|
||||
import com.cloud.vm.SecondaryStorageVm;
|
||||
|
||||
public class NfsSecondaryStorageResource extends ServerResourceBase implements SecondaryStorageResource {
|
||||
private static final Logger s_logger = Logger.getLogger(NfsSecondaryStorageResource.class);
|
||||
public class NfsSecondaryStorageResource extends ServerResourceBase implements
|
||||
SecondaryStorageResource {
|
||||
|
||||
private static final Logger s_logger = Logger
|
||||
.getLogger(NfsSecondaryStorageResource.class);
|
||||
|
||||
private static final String TEMPLATE_ROOT_DIR = "template/tmpl";
|
||||
private static final String SNAPSHOT_ROOT_DIR = "snapshots";
|
||||
|
||||
int _timeout;
|
||||
|
||||
String _instance;
|
||||
@ -168,16 +195,24 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
|
||||
return execute((ListVolumeCommand)cmd);
|
||||
}else if (cmd instanceof downloadSnapshotFromSwiftCommand){
|
||||
return execute((downloadSnapshotFromSwiftCommand)cmd);
|
||||
} else if (cmd instanceof DownloadSnapshotFromS3Command) {
|
||||
return execute((DownloadSnapshotFromS3Command) cmd);
|
||||
} else if (cmd instanceof DeleteSnapshotBackupCommand){
|
||||
return execute((DeleteSnapshotBackupCommand)cmd);
|
||||
} else if (cmd instanceof DeleteSnapshotsDirCommand){
|
||||
return execute((DeleteSnapshotsDirCommand)cmd);
|
||||
} else if (cmd instanceof downloadTemplateFromSwiftToSecondaryStorageCommand) {
|
||||
return execute((downloadTemplateFromSwiftToSecondaryStorageCommand) cmd);
|
||||
} else if (cmd instanceof DownloadTemplateFromS3ToSecondaryStorageCommand) {
|
||||
return execute((DownloadTemplateFromS3ToSecondaryStorageCommand) cmd);
|
||||
} else if (cmd instanceof uploadTemplateToSwiftFromSecondaryStorageCommand) {
|
||||
return execute((uploadTemplateToSwiftFromSecondaryStorageCommand) cmd);
|
||||
} else if (cmd instanceof UploadTemplateToS3FromSecondaryStorageCommand) {
|
||||
return execute((UploadTemplateToS3FromSecondaryStorageCommand) cmd);
|
||||
} else if (cmd instanceof DeleteObjectFromSwiftCommand) {
|
||||
return execute((DeleteObjectFromSwiftCommand) cmd);
|
||||
} else if (cmd instanceof DeleteTemplateFromS3Command) {
|
||||
return execute((DeleteTemplateFromS3Command) cmd);
|
||||
} else if (cmd instanceof CleanupSnapshotBackupCommand){
|
||||
return execute((CleanupSnapshotBackupCommand)cmd);
|
||||
} else {
|
||||
@ -185,6 +220,69 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private String determineS3TemplateDirectory(final Long accountId,
|
||||
final Long templateId) {
|
||||
return join(asList(TEMPLATE_ROOT_DIR, accountId, templateId),
|
||||
S3Utils.SEPARATOR);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private String determineStorageTemplatePath(final String storagePath,
|
||||
final Long accountId, final Long templateId) {
|
||||
return join(
|
||||
asList(getRootDir(storagePath), TEMPLATE_ROOT_DIR, accountId,
|
||||
templateId), File.separator);
|
||||
}
|
||||
|
||||
private Answer execute(
|
||||
final DownloadTemplateFromS3ToSecondaryStorageCommand cmd) {
|
||||
|
||||
final S3TO s3 = cmd.getS3();
|
||||
final String storagePath = cmd.getStoragePath();
|
||||
final Long accountId = cmd.getAccountId();
|
||||
final Long templateId = cmd.getTemplateId();
|
||||
|
||||
try {
|
||||
|
||||
final File downloadDirectory = _storage
|
||||
.getFile(determineStorageTemplatePath(storagePath,
|
||||
accountId, templateId));
|
||||
downloadDirectory.mkdirs();
|
||||
|
||||
if (!downloadDirectory.exists()) {
|
||||
final String errMsg = format(
|
||||
"Unable to create directory "
|
||||
+ "download directory %1$s for download of template id "
|
||||
+ "%2$s from S3.", downloadDirectory.getName(),
|
||||
templateId);
|
||||
s_logger.error(errMsg);
|
||||
return new Answer(cmd, false, errMsg);
|
||||
}
|
||||
|
||||
getDirectory(s3, s3.getBucketName(),
|
||||
determineS3TemplateDirectory(accountId, templateId),
|
||||
downloadDirectory, new FileNamingStrategy() {
|
||||
@Override
|
||||
public String determineFileName(final String key) {
|
||||
return substringAfterLast(key, S3Utils.SEPARATOR);
|
||||
}
|
||||
});
|
||||
|
||||
return new Answer(cmd, true, format("Successfully downloaded "
|
||||
+ "template id %1$s from S3 to directory %2$s", templateId,
|
||||
downloadDirectory.getName()));
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
final String errMsg = format("Failed to upload template id %1$s "
|
||||
+ "due to $2%s", templateId, e.getMessage());
|
||||
s_logger.error(errMsg, e);
|
||||
return new Answer(cmd, false, errMsg);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Answer execute(downloadTemplateFromSwiftToSecondaryStorageCommand cmd) {
|
||||
SwiftTO swift = cmd.getSwift();
|
||||
@ -256,6 +354,83 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
|
||||
}
|
||||
}
|
||||
|
||||
private Answer execute(UploadTemplateToS3FromSecondaryStorageCommand cmd) {
|
||||
|
||||
final S3TO s3 = cmd.getS3();
|
||||
final Long accountId = cmd.getAccountId();
|
||||
final Long templateId = cmd.getTemplateId();
|
||||
|
||||
try {
|
||||
|
||||
final String templatePath = determineStorageTemplatePath(
|
||||
cmd.getStoragePath(), accountId, templateId);
|
||||
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug("Found template id " + templateId
|
||||
+ " account id " + accountId + " from directory "
|
||||
+ templatePath + " to upload to S3.");
|
||||
}
|
||||
|
||||
if (!_storage.isDirectory(templatePath)) {
|
||||
final String errMsg = format("S3 Sync Failure: Directory %1$s"
|
||||
+ "for template id %2$s does not exist.", templatePath,
|
||||
templateId);
|
||||
s_logger.error(errMsg);
|
||||
return new Answer(cmd, false, errMsg);
|
||||
}
|
||||
|
||||
if (!_storage.isFile(templatePath + "/template.properties")) {
|
||||
final String errMsg = format("S3 Sync Failure: Template id "
|
||||
+ "%1$s does not exist on the file system.",
|
||||
templatePath);
|
||||
s_logger.error(errMsg);
|
||||
return new Answer(cmd, false, errMsg);
|
||||
}
|
||||
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug(format(
|
||||
"Pushing template id %1$s from %2$s to S3...",
|
||||
templateId, templatePath));
|
||||
}
|
||||
|
||||
final String bucket = s3.getBucketName();
|
||||
putDirectory(s3, bucket, _storage.getFile(templatePath),
|
||||
new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(final File directory,
|
||||
final String fileName) {
|
||||
return !fileName.startsWith(".");
|
||||
}
|
||||
}, new ObjectNamingStrategy() {
|
||||
@Override
|
||||
public String determineKey(final File file) {
|
||||
s_logger.debug(String
|
||||
.format("Determining key using account id %1$s and template id %2$s",
|
||||
accountId, templateId));
|
||||
return join(
|
||||
asList(determineS3TemplateDirectory(
|
||||
accountId, templateId), file
|
||||
.getName()), S3Utils.SEPARATOR);
|
||||
}
|
||||
});
|
||||
|
||||
return new Answer(
|
||||
cmd,
|
||||
true,
|
||||
format("Uploaded the contents of directory %1$s for template id %2$s to S3 bucket %3$s",
|
||||
templatePath, templateId, bucket));
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
final String errMsg = format("Failed to upload template id %1$s",
|
||||
templateId);
|
||||
s_logger.error(errMsg, e);
|
||||
return new Answer(cmd, false, errMsg);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Answer execute(DeleteObjectFromSwiftCommand cmd) {
|
||||
SwiftTO swift = cmd.getSwift();
|
||||
String container = cmd.getContainer();
|
||||
@ -279,6 +454,47 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
|
||||
|
||||
}
|
||||
|
||||
private Answer execute(final DeleteTemplateFromS3Command cmd) {
|
||||
|
||||
final S3TO s3 = cmd.getS3();
|
||||
final Long accountId = cmd.getAccountId();
|
||||
final Long templateId = cmd.getTemplateId();
|
||||
|
||||
if (accountId == null || (accountId != null && accountId <= 0)) {
|
||||
final String errorMessage = "No account id specified for S3 template deletion.";
|
||||
s_logger.error(errorMessage);
|
||||
return new Answer(cmd, false, errorMessage);
|
||||
}
|
||||
|
||||
if (templateId == null || (templateId != null && templateId <= 0)) {
|
||||
final String errorMessage = "No template id specified for S3 template deletion.";
|
||||
s_logger.error(errorMessage);
|
||||
return new Answer(cmd, false, errorMessage);
|
||||
}
|
||||
|
||||
if (s3 == null) {
|
||||
final String errorMessge = "No S3 client options provided";
|
||||
s_logger.error(errorMessge);
|
||||
return new Answer(cmd, false, errorMessge);
|
||||
}
|
||||
|
||||
final String bucket = s3.getBucketName();
|
||||
try {
|
||||
deleteDirectory(s3, bucket,
|
||||
determineS3TemplateDirectory(templateId, accountId));
|
||||
return new Answer(cmd, true, String.format(
|
||||
"Deleted template %1%s from bucket %2$s.", templateId,
|
||||
bucket));
|
||||
} catch (Exception e) {
|
||||
final String errorMessage = String
|
||||
.format("Failed to delete templaet id %1$s from bucket %2$s due to the following error: %3$s",
|
||||
templateId, bucket, e.getMessage());
|
||||
s_logger.error(errorMessage, e);
|
||||
return new Answer(cmd, false, errorMessage);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
String swiftDownload(SwiftTO swift, String container, String rfilename, String lFullPath) {
|
||||
Script command = new Script("/bin/bash", s_logger);
|
||||
command.add("-c");
|
||||
@ -451,6 +667,110 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
|
||||
}
|
||||
}
|
||||
|
||||
public Answer execute(final DownloadSnapshotFromS3Command cmd) {
|
||||
|
||||
final S3TO s3 = cmd.getS3();
|
||||
final String secondaryStorageUrl = cmd.getSecondaryStorageUrl();
|
||||
final Long accountId = cmd.getAccountId();
|
||||
final Long volumeId = cmd.getVolumeId();
|
||||
|
||||
try {
|
||||
|
||||
executeWithNoWaitLock(determineSnapshotLockId(accountId, volumeId),
|
||||
new Callable<Void>() {
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
|
||||
final String directoryName = determineSnapshotLocalDirectory(
|
||||
secondaryStorageUrl, accountId, volumeId);
|
||||
|
||||
String result = createLocalDir(directoryName);
|
||||
if (result != null) {
|
||||
throw new InternalErrorException(
|
||||
format("Failed to create directory %1$s during S3 snapshot download.",
|
||||
directoryName));
|
||||
}
|
||||
|
||||
final String snapshotFileName = determineSnapshotBackupFilename(cmd
|
||||
.getSnapshotUuid());
|
||||
final String key = determineSnapshotS3Key(
|
||||
accountId, volumeId, snapshotFileName);
|
||||
final File targetFile = S3Utils.getFile(s3,
|
||||
s3.getBucketName(), key,
|
||||
_storage.getFile(directoryName),
|
||||
new FileNamingStrategy() {
|
||||
|
||||
@Override
|
||||
public String determineFileName(
|
||||
String key) {
|
||||
return snapshotFileName;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if (cmd.getParent() != null) {
|
||||
|
||||
final String parentPath = join(
|
||||
File.pathSeparator, directoryName,
|
||||
determineSnapshotBackupFilename(cmd
|
||||
.getParent()));
|
||||
result = setVhdParent(
|
||||
targetFile.getAbsolutePath(),
|
||||
parentPath);
|
||||
if (result != null) {
|
||||
throw new InternalErrorException(
|
||||
format("Failed to set the parent for backup %1$s to %2$s due to %3$s.",
|
||||
targetFile
|
||||
.getAbsolutePath(),
|
||||
parentPath, result));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return new Answer(
|
||||
cmd,
|
||||
true,
|
||||
format("Succesfully retrieved volume id %1$s for account id %2$s to %3$s from S3.",
|
||||
volumeId, accountId, secondaryStorageUrl));
|
||||
|
||||
} catch (Exception e) {
|
||||
final String errMsg = format(
|
||||
"Failed to retrieve volume id %1$s for account id %2$s to %3$s from S3 due to exception %4$s",
|
||||
volumeId, accountId, secondaryStorageUrl, e.getMessage());
|
||||
s_logger.error(errMsg);
|
||||
return new Answer(cmd, false, errMsg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String determineSnapshotS3Directory(final Long accountId,
|
||||
final Long volumeId) {
|
||||
return join(S3Utils.SEPARATOR, SNAPSHOT_ROOT_DIR, accountId, volumeId);
|
||||
}
|
||||
|
||||
private String determineSnapshotS3Key(final Long accountId,
|
||||
final Long volumeId, final String snapshotFileName) {
|
||||
|
||||
final String directoryName = determineSnapshotS3Directory(accountId,
|
||||
volumeId);
|
||||
return join(S3Utils.SEPARATOR, directoryName, snapshotFileName);
|
||||
|
||||
}
|
||||
|
||||
private String determineSnapshotLocalDirectory(
|
||||
final String secondaryStorageUrl, final Long accountId,
|
||||
final Long volumeId) {
|
||||
return join(File.pathSeparator, getRootDir(secondaryStorageUrl),
|
||||
SNAPSHOT_ROOT_DIR, accountId, volumeId);
|
||||
}
|
||||
|
||||
public Answer execute(downloadSnapshotFromSwiftCommand cmd){
|
||||
SwiftTO swift = cmd.getSwift();
|
||||
String secondaryStorageUrl = cmd.getSecondaryStorageUrl();
|
||||
@ -622,6 +942,92 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
|
||||
}
|
||||
}
|
||||
|
||||
private String deleteSnapshotBackupFromLocalFileSystem(
|
||||
final String secondaryStorageUrl, final Long accountId,
|
||||
final Long volumeId, final String name, final Boolean deleteAllFlag) {
|
||||
|
||||
final String lPath = determineSnapshotLocalDirectory(
|
||||
secondaryStorageUrl, accountId, volumeId)
|
||||
+ File.pathSeparator
|
||||
+ (deleteAllFlag ? "*" : "*" + name + "*");
|
||||
|
||||
final String result = deleteLocalFile(lPath);
|
||||
|
||||
if (result != null) {
|
||||
return "failed to delete snapshot " + lPath + " , err=" + result;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
private String deleteSnapshotBackupfromS3(final S3TO s3,
|
||||
final String secondaryStorageUrl, final Long accountId,
|
||||
final Long volumeId, final String name, final Boolean deleteAllFlag) {
|
||||
|
||||
try {
|
||||
|
||||
final String bucket = s3.getBucketName();
|
||||
|
||||
final String result = executeWithNoWaitLock(
|
||||
determineSnapshotLockId(accountId, volumeId),
|
||||
new Callable<String>() {
|
||||
|
||||
@Override
|
||||
public String call() throws Exception {
|
||||
|
||||
final String innerResult = deleteSnapshotBackupFromLocalFileSystem(
|
||||
secondaryStorageUrl, accountId, volumeId,
|
||||
name, deleteAllFlag);
|
||||
if (innerResult != null) {
|
||||
return innerResult;
|
||||
}
|
||||
|
||||
if (deleteAllFlag) {
|
||||
S3Utils.deleteDirectory(
|
||||
s3,
|
||||
bucket,
|
||||
determineSnapshotS3Directory(accountId,
|
||||
volumeId));
|
||||
} else {
|
||||
S3Utils.deleteObject(
|
||||
s3,
|
||||
bucket,
|
||||
determineSnapshotS3Key(
|
||||
accountId,
|
||||
volumeId,
|
||||
determineSnapshotBackupFilename(name)));
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
s_logger.error(
|
||||
String.format(
|
||||
"Failed to delete snapshot backup for account id %1$s volume id %2$sfrom S3.",
|
||||
accountId, volumeId), e);
|
||||
return e.getMessage();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String determineSnapshotBackupFilename(final String snapshotUuid) {
|
||||
return snapshotUuid + ".vhd";
|
||||
}
|
||||
|
||||
private String determineSnapshotLockId(final Long accountId,
|
||||
final Long volumeId) {
|
||||
return join("_", "SNAPSHOT", accountId, volumeId);
|
||||
}
|
||||
|
||||
protected Answer execute(final DeleteSnapshotBackupCommand cmd) {
|
||||
String secondaryStorageUrl = cmd.getSecondaryStorageUrl();
|
||||
Long accountId = cmd.getAccountId();
|
||||
@ -629,21 +1035,22 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
|
||||
String name = cmd.getSnapshotUuid();
|
||||
try {
|
||||
SwiftTO swift = cmd.getSwift();
|
||||
S3TO s3 = cmd.getS3();
|
||||
if (swift == null) {
|
||||
String parent = getRootDir(secondaryStorageUrl);
|
||||
String filename;
|
||||
if (cmd.isAll()) {
|
||||
filename = "*";
|
||||
|
||||
} else {
|
||||
filename = "*" + name + "*";
|
||||
}
|
||||
String lPath = parent + "/snapshots/" + String.valueOf(accountId) + "/" + String.valueOf(volumeId) + "/" + filename;
|
||||
String result = deleteLocalFile(lPath);
|
||||
final String result = deleteSnapshotBackupFromLocalFileSystem(
|
||||
secondaryStorageUrl, accountId, volumeId, name,
|
||||
cmd.isAll());
|
||||
if (result != null) {
|
||||
String errMsg = "failed to delete snapshot " + lPath + " , err=" + result;
|
||||
s_logger.warn(errMsg);
|
||||
return new Answer(cmd, false, errMsg);
|
||||
s_logger.warn(result);
|
||||
return new Answer(cmd, false, result);
|
||||
}
|
||||
} else if (s3 != null) {
|
||||
final String result = deleteSnapshotBackupfromS3(s3,
|
||||
secondaryStorageUrl, accountId, volumeId, name,
|
||||
cmd.isAll());
|
||||
if (result != null) {
|
||||
s_logger.warn(result);
|
||||
return new Answer(cmd, false, result);
|
||||
}
|
||||
} else {
|
||||
String filename;
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
net.ipv4.ip_forward = 1
|
||||
|
||||
# Controls source route verification
|
||||
net.ipv4.conf.default.rp_filter = 1
|
||||
net.ipv4.conf.default.rp_filter = 0
|
||||
|
||||
# Do not accept source routing
|
||||
net.ipv4.conf.default.accept_source_route = 0
|
||||
|
||||
@ -17,6 +17,10 @@
|
||||
package com.cloud.hypervisor.xen.resource;
|
||||
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@ -24,11 +28,13 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringReader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
@ -179,6 +185,7 @@ import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
|
||||
import com.cloud.agent.api.to.IpAddressTO;
|
||||
import com.cloud.agent.api.to.NicTO;
|
||||
import com.cloud.agent.api.to.PortForwardingRuleTO;
|
||||
import com.cloud.agent.api.to.S3TO;
|
||||
import com.cloud.agent.api.to.StaticNatRuleTO;
|
||||
import com.cloud.agent.api.to.StorageFilerTO;
|
||||
import com.cloud.agent.api.to.SwiftTO;
|
||||
@ -217,6 +224,8 @@ import com.cloud.storage.template.TemplateInfo;
|
||||
import com.cloud.template.VirtualMachineTemplate.BootloaderType;
|
||||
import com.cloud.utils.NumbersUtil;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.S3Utils;
|
||||
import com.cloud.utils.StringUtils;
|
||||
import com.cloud.utils.Ternary;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.net.NetUtils;
|
||||
@ -6507,7 +6516,14 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
|
||||
} finally {
|
||||
deleteSnapshotBackup(conn, dcId, accountId, volumeId, secondaryStorageMountPath, snapshotBackupUuid);
|
||||
}
|
||||
}
|
||||
} else if (cmd.getS3() != null) {
|
||||
try {
|
||||
backupSnapshotToS3(conn, cmd.getS3(), snapshotSr.getUuid(conn), snapshotBackupUuid, isISCSI, wait);
|
||||
snapshotBackupUuid = snapshotBackupUuid + ".vhd";
|
||||
} finally {
|
||||
deleteSnapshotBackup(conn, dcId, accountId, volumeId, secondaryStorageMountPath, snapshotBackupUuid);
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
} finally {
|
||||
if( snapshotSr != null) {
|
||||
@ -6524,6 +6540,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
|
||||
snapshotBackupUuid = snapshotPaUuid + ".vhd";
|
||||
}
|
||||
success = true;
|
||||
} else if (cmd.getS3() != null) {
|
||||
backupSnapshotToS3(conn, cmd.getS3(), primaryStorageSRUuid, snapshotPaUuid, isISCSI, wait);
|
||||
} else {
|
||||
snapshotBackupUuid = backupSnapshot(conn, primaryStorageSRUuid, dcId, accountId, volumeId, secondaryStorageMountPath, snapshotUuid, prevBackupUuid, isISCSI, wait);
|
||||
success = (snapshotBackupUuid != null);
|
||||
@ -6546,6 +6564,88 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
|
||||
return new BackupSnapshotAnswer(cmd, success, details, snapshotBackupUuid, fullbackup);
|
||||
}
|
||||
|
||||
private static List<String> serializeProperties(final Object object,
|
||||
final Class<?> propertySet) {
|
||||
|
||||
assert object != null;
|
||||
assert propertySet != null;
|
||||
assert propertySet.isAssignableFrom(object.getClass());
|
||||
|
||||
try {
|
||||
|
||||
final BeanInfo beanInfo = Introspector.getBeanInfo(propertySet);
|
||||
final PropertyDescriptor[] descriptors = beanInfo
|
||||
.getPropertyDescriptors();
|
||||
|
||||
final List<String> serializedProperties = new ArrayList<String>();
|
||||
for (final PropertyDescriptor descriptor : descriptors) {
|
||||
|
||||
serializedProperties.add(descriptor.getName());
|
||||
final Object value = descriptor.getReadMethod().invoke(object);
|
||||
serializedProperties.add(value != null ? value.toString()
|
||||
: "null");
|
||||
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(serializedProperties);
|
||||
|
||||
} catch (IntrospectionException e) {
|
||||
s_logger.warn(
|
||||
"Ignored IntrospectionException when serializing class "
|
||||
+ object.getClass().getCanonicalName(), e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
s_logger.warn(
|
||||
"Ignored IllegalArgumentException when serializing class "
|
||||
+ object.getClass().getCanonicalName(), e);
|
||||
} catch (IllegalAccessException e) {
|
||||
s_logger.warn(
|
||||
"Ignored IllegalAccessException when serializing class "
|
||||
+ object.getClass().getCanonicalName(), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
s_logger.warn(
|
||||
"Ignored InvocationTargetException when serializing class "
|
||||
+ object.getClass().getCanonicalName(), e);
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
|
||||
}
|
||||
|
||||
private boolean backupSnapshotToS3(final Connection connection,
|
||||
final S3TO s3, final String srUuid, final String snapshotUuid,
|
||||
final Boolean iSCSIFlag, final int wait) {
|
||||
|
||||
final String filename = iSCSIFlag ? "VHD-" + snapshotUuid
|
||||
: snapshotUuid + ".vhd";
|
||||
final String dir = (iSCSIFlag ? "/dev/VG_XenStorage-"
|
||||
: "/var/run/sr-mount/") + srUuid;
|
||||
final String key = StringUtils.join("/", "snapshots", snapshotUuid);
|
||||
|
||||
try {
|
||||
|
||||
final List<String> parameters = new ArrayList<String>(
|
||||
serializeProperties(s3, S3Utils.ClientOptions.class));
|
||||
parameters.addAll(Arrays.asList("operation", "put", "directory",
|
||||
dir, "filename", filename, "iSCSIFlag",
|
||||
iSCSIFlag.toString(), "key", key));
|
||||
final String result = callHostPluginAsync(connection, "s3xen",
|
||||
"s3", wait,
|
||||
parameters.toArray(new String[parameters.size()]));
|
||||
|
||||
if (result != null && result.equals("true")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
s_logger.error(String.format(
|
||||
"S3 upload failed of snapshot %1$s due to %2$s.",
|
||||
snapshotUuid, e.toString()), e);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
protected CreateVolumeFromSnapshotAnswer execute(final CreateVolumeFromSnapshotCommand cmd) {
|
||||
Connection conn = getConnection();
|
||||
String primaryStorageNameLabel = cmd.getPrimaryStoragePoolNameLabel();
|
||||
|
||||
6
pom.xml
6
pom.xml
@ -67,7 +67,7 @@
|
||||
<cs.gson.version>1.7.1</cs.gson.version>
|
||||
<cs.xapi.version>5.6.100-1-SNAPSHOT</cs.xapi.version>
|
||||
<cs.httpclient.version>3.1</cs.httpclient.version>
|
||||
<cs.httpcore.version>4.0</cs.httpcore.version>
|
||||
<cs.httpcore.version>4.1</cs.httpcore.version>
|
||||
<cs.mysql.version>5.1.21</cs.mysql.version>
|
||||
<cs.xstream.version>1.3.1</cs.xstream.version>
|
||||
<cs.xmlrpc.version>3.1.3</cs.xmlrpc.version>
|
||||
@ -82,7 +82,9 @@
|
||||
<cs.selenium.server.version>1.0-20081010.060147</cs.selenium.server.version>
|
||||
<cs.vmware.api.version>4.1</cs.vmware.api.version>
|
||||
<cs.mockito.version>1.9.5</cs.mockito.version>
|
||||
|
||||
<cs.aws.sdk.version>1.3.21.1</cs.aws.sdk.version>
|
||||
<cs.lang.version>2.6</cs.lang.version>
|
||||
<cs.commons-io.version>1.4</cs.commons-io.version>
|
||||
</properties>
|
||||
|
||||
<distributionManagement>
|
||||
|
||||
297
scripts/vm/hypervisor/xenserver/s3xen
Normal file
297
scripts/vm/hypervisor/xenserver/s3xen
Normal file
@ -0,0 +1,297 @@
|
||||
#!/usr/bin/python
|
||||
# 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.
|
||||
|
||||
# Version @VERSION@
|
||||
#
|
||||
# A plugin for executing script needed by cloud stack
|
||||
from __future__ import with_statement
|
||||
|
||||
from copy import copy
|
||||
from datetime import datetime
|
||||
from httplib import *
|
||||
from string import join
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import hashlib
|
||||
import base64
|
||||
import hmac
|
||||
import traceback
|
||||
import urllib2
|
||||
|
||||
import XenAPIPlugin
|
||||
sys.path.extend(["/opt/xensource/sm/"])
|
||||
import util
|
||||
|
||||
NULL = 'null'
|
||||
|
||||
# Value conversion utility functions ...
|
||||
|
||||
|
||||
def to_none(value):
|
||||
return value if value is not None and value.strip() != NULL else None
|
||||
|
||||
|
||||
def to_bool(value):
|
||||
return True if to_none(value) in ['true', 'True', None] else False
|
||||
|
||||
|
||||
def to_integer(value, default):
|
||||
return int(value) if to_none(value) is not None else default
|
||||
|
||||
|
||||
def optional_str_value(value, default):
|
||||
return value if is_not_blank(value) else default
|
||||
|
||||
|
||||
def is_not_blank(value):
|
||||
return True if to_none(value) is not None and value.strip != '' else False
|
||||
|
||||
|
||||
def get_optional_key(map, key, default=''):
|
||||
return map[key] if key in map else default
|
||||
|
||||
|
||||
def log(message):
|
||||
util.SMlog('#### VMOPS %s ####' % message)
|
||||
|
||||
|
||||
def echo(fn):
|
||||
def wrapped(*v, **k):
|
||||
name = fn.__name__
|
||||
log("enter %s ####" % name)
|
||||
res = fn(*v, **k)
|
||||
log("exit %s with result %s" % name, res)
|
||||
return res
|
||||
return wrapped
|
||||
|
||||
|
||||
def require_str_value(value, error_message):
|
||||
|
||||
if is_not_blank(value):
|
||||
return value
|
||||
|
||||
raise ValueError(error_message)
|
||||
|
||||
|
||||
def retry(max_attempts, fn):
|
||||
|
||||
attempts = 1
|
||||
while attempts <= max_attempts:
|
||||
log("Attempting execution {0}/{1} of {2}".
|
||||
format(attempts, max_attempts, fn.__name__))
|
||||
try:
|
||||
return fn()
|
||||
except:
|
||||
if (attempts >= max_attempts):
|
||||
raise
|
||||
attempts = attempts + 1
|
||||
|
||||
|
||||
def compute_md5(filename, buffer_size=8192):
|
||||
|
||||
hasher = hashlib.md5()
|
||||
|
||||
with open(filename, 'rb') as file:
|
||||
data = file.read(buffer_size)
|
||||
while data != "":
|
||||
hasher.update(data)
|
||||
data = file.read(buffer_size)
|
||||
|
||||
return base64.encodestring(hasher.digest())[:-1]
|
||||
|
||||
|
||||
class S3Client(object):
|
||||
|
||||
DEFAULT_END_POINT = 's3.amazonaws.com'
|
||||
DEFAULT_CONNECTION_TIMEOUT = 50000
|
||||
DEFAULT_SOCKET_TIMEOUT = 50000
|
||||
DEFAULT_MAX_ERROR_RETRY = 3
|
||||
|
||||
HEADER_CONTENT_MD5 = 'Content-MD5'
|
||||
HEADER_CONTENT_TYPE = 'Content-Type'
|
||||
HEADER_CONTENT_LENGTH = 'Content-Length'
|
||||
|
||||
def __init__(self, access_key, secret_key, end_point=None,
|
||||
https_flag=None, connection_timeout=None, socket_timeout=None,
|
||||
max_error_retry=None):
|
||||
|
||||
self.access_key = require_str_value(
|
||||
access_key, 'An access key must be specified.')
|
||||
self.secret_key = require_str_value(
|
||||
secret_key, 'A secret key must be specified.')
|
||||
self.end_point = optional_str_value(end_point, self.DEFAULT_END_POINT)
|
||||
self.https_flag = to_bool(https_flag)
|
||||
self.connection_timeout = to_integer(
|
||||
connection_timeout, self.DEFAULT_CONNECTION_TIMEOUT)
|
||||
self.socket_timeout = to_integer(
|
||||
socket_timeout, self.DEFAULT_SOCKET_TIMEOUT)
|
||||
self.max_error_retry = to_integer(
|
||||
max_error_retry, self.DEFAULT_MAX_ERROR_RETRY)
|
||||
|
||||
def build_canocialized_resource(self, bucket, key):
|
||||
|
||||
return '/{bucket}/{key}'.format(bucket=bucket, key=key)
|
||||
|
||||
def noop_send_body():
|
||||
pass
|
||||
|
||||
def noop_read(response):
|
||||
return response.read()
|
||||
|
||||
def do_operation(
|
||||
self, method, bucket, key, input_headers={},
|
||||
fn_send_body=noop_send_body, fn_read=noop_read):
|
||||
|
||||
headers = copy(input_headers)
|
||||
headers['Expect'] = '100-continue'
|
||||
|
||||
uri = self.build_canocialized_resource(bucket, key)
|
||||
signature, request_date = self.sign_request(method, uri, headers)
|
||||
headers['Authorization'] = "AWS {0}:{1}".format(
|
||||
self.access_key, signature)
|
||||
headers['Date'] = request_date
|
||||
|
||||
connection = HTTPSConnection(self.end_point) \
|
||||
if self.https_flag else HTTPConnection(self.end_point)
|
||||
connection.timeout = self.socket_timeout
|
||||
|
||||
def perform_request():
|
||||
|
||||
connection.request(method, uri, fn_send_body(), headers)
|
||||
response = connection.getresponse()
|
||||
log("Sent {0} request to {1} {2} with headers {3}. \
|
||||
Got response status {4}: {5}".
|
||||
format(method, self.end_point, uri, headers,
|
||||
response.status, response.reason))
|
||||
return fn_read(response)
|
||||
|
||||
try:
|
||||
return retry(self.max_error_retry, perform_request)
|
||||
finally:
|
||||
connection.close()
|
||||
|
||||
'''
|
||||
See http://bit.ly/MMC5de for more information regarding the creation of
|
||||
AWS authorization tokens and header signing
|
||||
'''
|
||||
def sign_request(self, operation, canocialized_resource, headers):
|
||||
|
||||
request_date = datetime.utcnow(
|
||||
).strftime('%a, %d %b %Y %H:%M:%S +0000')
|
||||
|
||||
content_hash = get_optional_key(headers, self.HEADER_CONTENT_MD5)
|
||||
content_type = get_optional_key(headers, self.HEADER_CONTENT_TYPE)
|
||||
|
||||
string_to_sign = join(
|
||||
[operation, content_hash, content_type, request_date,
|
||||
canocialized_resource], '\n')
|
||||
|
||||
signature = base64.encodestring(
|
||||
hmac.new(self.secret_key, string_to_sign.encode('utf8'),
|
||||
hashlib.sha1).digest())[:-1]
|
||||
|
||||
return signature, request_date
|
||||
|
||||
def put(self, bucket, key, src_filename):
|
||||
|
||||
headers = {
|
||||
self.HEADER_CONTENT_MD5: compute_md5(src_filename),
|
||||
self.HEADER_CONTENT_TYPE: 'application/octet-stream',
|
||||
self.HEADER_CONTENT_LENGTH: os.stat(src_filename).st_size,
|
||||
}
|
||||
|
||||
def send_body():
|
||||
return open(src_filename, 'rb')
|
||||
|
||||
self.do_operation('PUT', bucket, key, headers, send_body)
|
||||
|
||||
def get(self, bucket, key, target_filename):
|
||||
|
||||
def read(response):
|
||||
|
||||
with open(target_filename, 'wb') as file:
|
||||
while True:
|
||||
block = response.read(8192)
|
||||
if not block:
|
||||
break
|
||||
file.write(block)
|
||||
|
||||
return self.do_operation('GET', bucket, key, fn_read=read)
|
||||
|
||||
def delete(self, bucket, key):
|
||||
|
||||
return self.do_operation('DELETE', bucket, key)
|
||||
|
||||
|
||||
def parseArguments(args):
|
||||
|
||||
# The keys in the args map will correspond to the properties defined on
|
||||
# the com.cloud.utils.S3Utils#ClientOptions interface
|
||||
client = S3Client(
|
||||
args['accessKey'], args['secretKey'], args['endPoint'],
|
||||
args['isHttps'], args['connectionTimeout'], args['socketTimeout'])
|
||||
|
||||
operation = args['operation']
|
||||
bucket = args['bucket']
|
||||
key = args['key']
|
||||
filename = args['filename']
|
||||
|
||||
if is_blank(operation):
|
||||
raise ValueError('An operation must be specified.')
|
||||
|
||||
if is_blank(bucket):
|
||||
raise ValueError('A bucket must be specified.')
|
||||
|
||||
if is_blank(key):
|
||||
raise ValueError('A value must be specified.')
|
||||
|
||||
if is_blank(filename):
|
||||
raise ValueError('A filename must be specified.')
|
||||
|
||||
return client, operation, bucket, key, filename
|
||||
|
||||
|
||||
@echo
|
||||
def s3(session, args):
|
||||
|
||||
client, operation, bucket, key, filename = parseArguments(args)
|
||||
|
||||
try:
|
||||
|
||||
if operation == 'put':
|
||||
client.put(bucket, key, filename)
|
||||
elif operation == 'get':
|
||||
client.get(bucket, key, filename)
|
||||
elif operation == 'delete':
|
||||
client.delete(bucket, key, filename)
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"S3 plugin does not support operation {0}.".format(operation))
|
||||
|
||||
return 'true'
|
||||
|
||||
except:
|
||||
log("Operation {0} on file {1} from/in bucket {2} key {3}.".format(
|
||||
operation, filename, bucket, key))
|
||||
log(traceback.format_exc())
|
||||
return 'false'
|
||||
|
||||
if __name__ == "__main__":
|
||||
XenAPIPlugin.dispatch({"s3": s3})
|
||||
@ -62,3 +62,5 @@ cloud-prepare-upgrade.sh=..,0755,/opt/xensource/bin
|
||||
bumpUpPriority.sh=../../../../network/domr/,0755,/opt/xensource/bin
|
||||
swift=..,0755,/opt/xensource/bin
|
||||
swiftxen=..,0755,/etc/xapi.d/plugins
|
||||
s3xen=..,0755,/etc/xapi.d/plugins
|
||||
|
||||
|
||||
@ -61,3 +61,5 @@ cloud-prepare-upgrade.sh=..,0755,/opt/xensource/bin
|
||||
bumpUpPriority.sh=../../../../network/domr/,0755,/opt/xensource/bin
|
||||
swift=..,0755,/opt/xensource/bin
|
||||
swiftxen=..,0755,/etc/xapi.d/plugins
|
||||
s3xen=..,0755,/etc/xapi.d/plugins
|
||||
|
||||
|
||||
@ -66,3 +66,5 @@ cloud-prepare-upgrade.sh=..,0755,/opt/xensource/bin
|
||||
bumpUpPriority.sh=../../../../network/domr/,0755,/opt/xensource/bin
|
||||
swift=..,0755,/opt/xensource/bin
|
||||
swiftxen=..,0755,/etc/xapi.d/plugins
|
||||
s3xen=..,0755,/etc/xapi.d/plugins
|
||||
|
||||
|
||||
@ -62,6 +62,11 @@
|
||||
<artifactId>jstl</artifactId>
|
||||
<version>${cs.jstl.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>${cs.codec.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-utils</artifactId>
|
||||
@ -69,7 +74,6 @@
|
||||
<classifier>tests</classifier>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
<defaultGoal>install</defaultGoal>
|
||||
|
||||
@ -115,6 +115,7 @@ import com.cloud.storage.StoragePoolVO;
|
||||
import com.cloud.storage.StorageStats;
|
||||
import com.cloud.storage.UploadVO;
|
||||
import com.cloud.storage.VMTemplateHostVO;
|
||||
import com.cloud.storage.VMTemplateS3VO;
|
||||
import com.cloud.storage.VMTemplateSwiftVO;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.storage.Volume.Type;
|
||||
@ -129,6 +130,7 @@ import com.cloud.storage.dao.UploadDao;
|
||||
import com.cloud.storage.dao.VMTemplateDao;
|
||||
import com.cloud.storage.dao.VMTemplateDetailsDao;
|
||||
import com.cloud.storage.dao.VMTemplateHostDao;
|
||||
import com.cloud.storage.dao.VMTemplateS3Dao;
|
||||
import com.cloud.storage.dao.VMTemplateSwiftDao;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.storage.dao.VolumeHostDao;
|
||||
@ -196,6 +198,7 @@ public class ApiDBUtils {
|
||||
private static VMTemplateDetailsDao _templateDetailsDao;
|
||||
private static VMTemplateHostDao _templateHostDao;
|
||||
private static VMTemplateSwiftDao _templateSwiftDao;
|
||||
private static VMTemplateS3Dao _templateS3Dao;
|
||||
private static UploadDao _uploadDao;
|
||||
private static UserDao _userDao;
|
||||
private static UserStatisticsDao _userStatsDao;
|
||||
@ -260,6 +263,7 @@ public class ApiDBUtils {
|
||||
_templateDetailsDao = locator.getDao(VMTemplateDetailsDao.class);
|
||||
_templateHostDao = locator.getDao(VMTemplateHostDao.class);
|
||||
_templateSwiftDao = locator.getDao(VMTemplateSwiftDao.class);
|
||||
_templateS3Dao = locator.getDao(VMTemplateS3Dao.class);
|
||||
_uploadDao = locator.getDao(UploadDao.class);
|
||||
_userDao = locator.getDao(UserDao.class);
|
||||
_userStatsDao = locator.getDao(UserStatisticsDao.class);
|
||||
@ -575,6 +579,10 @@ public class ApiDBUtils {
|
||||
return _templateSwiftDao.findOneByTemplateId(templateId);
|
||||
}
|
||||
|
||||
public static VMTemplateS3VO findTemplateS3Ref(long templateId) {
|
||||
return _templateS3Dao.findOneByTemplateId(templateId);
|
||||
}
|
||||
|
||||
public static UploadVO findUploadById(Long id) {
|
||||
return _uploadDao.findById(id);
|
||||
}
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
// under the License.
|
||||
package com.cloud.api;
|
||||
|
||||
import static org.apache.commons.lang.StringUtils.isNotBlank;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
@ -41,7 +43,6 @@ import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.utils.IdentityProxy;
|
||||
import com.cloud.server.ManagementServer;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.UserContext;
|
||||
@ -417,10 +418,20 @@ public class ApiDispatcher {
|
||||
}
|
||||
break;
|
||||
case FLOAT:
|
||||
field.set(cmdObj, Float.valueOf(paramObj.toString()));
|
||||
// Assuming that the parameters have been checked for required before now,
|
||||
// we ignore blank or null values and defer to the command to set a default
|
||||
// value for optional parameters ...
|
||||
if (paramObj != null && isNotBlank(paramObj.toString())) {
|
||||
field.set(cmdObj, Float.valueOf(paramObj.toString()));
|
||||
}
|
||||
break;
|
||||
case INTEGER:
|
||||
field.set(cmdObj, Integer.valueOf(paramObj.toString()));
|
||||
// Assuming that the parameters have been checked for required before now,
|
||||
// we ignore blank or null values and defer to the command to set a default
|
||||
// value for optional parameters ...
|
||||
if (paramObj != null && isNotBlank(paramObj.toString())) {
|
||||
field.set(cmdObj, Integer.valueOf(paramObj.toString()));
|
||||
}
|
||||
break;
|
||||
case LIST:
|
||||
List listParam = new ArrayList();
|
||||
|
||||
@ -16,6 +16,9 @@
|
||||
// under the License.
|
||||
package com.cloud.api;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
@ -82,6 +85,7 @@ import com.cloud.api.response.RemoteAccessVpnResponse;
|
||||
import com.cloud.api.response.ResourceCountResponse;
|
||||
import com.cloud.api.response.ResourceLimitResponse;
|
||||
import com.cloud.api.response.ResourceTagResponse;
|
||||
import com.cloud.api.response.S3Response;
|
||||
import com.cloud.api.response.SecurityGroupResponse;
|
||||
import com.cloud.api.response.SecurityGroupResultObject;
|
||||
import com.cloud.api.response.SecurityGroupRuleResponse;
|
||||
@ -131,7 +135,6 @@ import com.cloud.dc.Vlan.VlanType;
|
||||
import com.cloud.dc.VlanVO;
|
||||
import com.cloud.domain.Domain;
|
||||
import com.cloud.event.Event;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.host.HostStats;
|
||||
import com.cloud.host.HostVO;
|
||||
@ -189,6 +192,7 @@ import com.cloud.server.ResourceTag.TaggedResourceType;
|
||||
import com.cloud.storage.DiskOfferingVO;
|
||||
import com.cloud.storage.GuestOS;
|
||||
import com.cloud.storage.GuestOSCategoryVO;
|
||||
import com.cloud.storage.S3;
|
||||
import com.cloud.storage.Snapshot;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.storage.Storage.ImageFormat;
|
||||
@ -200,6 +204,7 @@ import com.cloud.storage.StorageStats;
|
||||
import com.cloud.storage.Swift;
|
||||
import com.cloud.storage.UploadVO;
|
||||
import com.cloud.storage.VMTemplateHostVO;
|
||||
import com.cloud.storage.VMTemplateS3VO;
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
|
||||
import com.cloud.storage.VMTemplateSwiftVO;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
@ -743,6 +748,25 @@ public class ApiResponseHelper implements ResponseGenerator {
|
||||
return swiftResponse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public S3Response createS3Response(final S3 result) {
|
||||
|
||||
final S3Response response = new S3Response();
|
||||
|
||||
response.setAccessKey(result.getAccessKey());
|
||||
response.setConnectionTimeout(result.getConnectionTimeout());
|
||||
response.setEndPoint(result.getEndPoint());
|
||||
response.setHttpsFlag(result.getHttpsFlag());
|
||||
response.setMaxErrorRetry(result.getMaxErrorRetry());
|
||||
response.setObjectId(result.getId());
|
||||
response.setSecretKey(result.getSecretKey());
|
||||
response.setSocketTimeout(result.getSocketTimeout());
|
||||
response.setTemplateBucketName(result.getBucketName());
|
||||
|
||||
return response;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public VlanIpRangeResponse createVlanIpRangeResponse(Vlan vlan) {
|
||||
Long podId = ApiDBUtils.getPodIdForVlan(vlan.getId());
|
||||
@ -2153,7 +2177,7 @@ public class ApiResponseHelper implements ResponseGenerator {
|
||||
@Override
|
||||
public List<TemplateResponse> createIsoResponses(long isoId, Long zoneId, boolean readyOnly) {
|
||||
|
||||
List<TemplateResponse> isoResponses = new ArrayList<TemplateResponse>();
|
||||
final List<TemplateResponse> isoResponses = new ArrayList<TemplateResponse>();
|
||||
VirtualMachineTemplate iso = findTemplateById(isoId);
|
||||
if (iso.getTemplateType() == TemplateType.PERHOST) {
|
||||
TemplateResponse isoResponse = new TemplateResponse();
|
||||
@ -2191,11 +2215,17 @@ public class ApiResponseHelper implements ResponseGenerator {
|
||||
return isoResponses;
|
||||
} else {
|
||||
if (zoneId == null || zoneId == -1) {
|
||||
isoResponses = createSwiftIsoResponses(iso);
|
||||
isoResponses.addAll(createSwiftIsoResponses(iso));
|
||||
if (!isoResponses.isEmpty()) {
|
||||
return isoResponses;
|
||||
}
|
||||
List<DataCenterVO> dcs = new ArrayList<DataCenterVO>();
|
||||
|
||||
isoResponses.addAll(createS3IsoResponses(iso));
|
||||
if (!isoResponses.isEmpty()) {
|
||||
return isoResponses;
|
||||
}
|
||||
|
||||
final List<DataCenterVO> dcs = new ArrayList<DataCenterVO>();
|
||||
dcs.addAll(ApiDBUtils.listZones());
|
||||
for (DataCenterVO dc : dcs) {
|
||||
isoResponses.addAll(createIsoResponses(iso, dc.getId(), readyOnly));
|
||||
@ -2207,6 +2237,65 @@ public class ApiResponseHelper implements ResponseGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private List<? extends TemplateResponse> createS3IsoResponses(final VirtualMachineTemplate iso) {
|
||||
|
||||
final VMTemplateS3VO s3Iso = ApiDBUtils.findTemplateS3Ref(iso.getId());
|
||||
|
||||
if (s3Iso == null) {
|
||||
return emptyList();
|
||||
}
|
||||
|
||||
final TemplateResponse templateResponse = new TemplateResponse();
|
||||
|
||||
templateResponse.setId(iso.getId());
|
||||
templateResponse.setName(iso.getName());
|
||||
templateResponse.setDisplayText(iso.getDisplayText());
|
||||
templateResponse.setPublic(iso.isPublicTemplate());
|
||||
templateResponse.setExtractable(iso.isExtractable());
|
||||
templateResponse.setCreated(s3Iso.getCreated());
|
||||
templateResponse.setReady(true);
|
||||
templateResponse.setBootable(iso.isBootable());
|
||||
templateResponse.setFeatured(iso.isFeatured());
|
||||
templateResponse.setCrossZones(iso.isCrossZones());
|
||||
templateResponse.setChecksum(iso.getChecksum());
|
||||
templateResponse.setDetails(iso.getDetails());
|
||||
|
||||
final GuestOS os = ApiDBUtils.findGuestOSById(iso.getGuestOSId());
|
||||
|
||||
if (os != null) {
|
||||
templateResponse.setOsTypeId(os.getId());
|
||||
templateResponse.setOsTypeName(os.getDisplayName());
|
||||
} else {
|
||||
templateResponse.setOsTypeId(-1L);
|
||||
templateResponse.setOsTypeName("");
|
||||
}
|
||||
|
||||
final Account account = ApiDBUtils.findAccountByIdIncludingRemoved(iso.getAccountId());
|
||||
populateAccount(templateResponse, account.getId());
|
||||
populateDomain(templateResponse, account.getDomainId());
|
||||
|
||||
boolean isAdmin = false;
|
||||
if ((account == null) || BaseCmd.isAdmin(account.getType())) {
|
||||
isAdmin = true;
|
||||
}
|
||||
|
||||
// If the user is an admin, add the template download status
|
||||
if (isAdmin || account.getId() == iso.getAccountId()) {
|
||||
// add download status
|
||||
templateResponse.setStatus("Successfully Installed");
|
||||
}
|
||||
|
||||
final Long isoSize = s3Iso.getSize();
|
||||
if (isoSize > 0) {
|
||||
templateResponse.setSize(isoSize);
|
||||
}
|
||||
|
||||
templateResponse.setObjectName("iso");
|
||||
|
||||
return singletonList(templateResponse);
|
||||
|
||||
}
|
||||
|
||||
private List<TemplateResponse> createSwiftIsoResponses(VirtualMachineTemplate iso) {
|
||||
long isoId = iso.getId();
|
||||
List<TemplateResponse> isoResponses = new ArrayList<TemplateResponse>();
|
||||
|
||||
@ -314,6 +314,11 @@ public class ApiXmlDocWriter {
|
||||
impl = clas.getSuperclass().getAnnotation(Implementation.class);
|
||||
}
|
||||
|
||||
if (impl == null) {
|
||||
throw new IllegalStateException(String.format("An %1$s annotation is required for class %2$s.",
|
||||
Implementation.class.getCanonicalName(), clas.getCanonicalName()));
|
||||
}
|
||||
|
||||
if (impl.includeInApiDoc()) {
|
||||
String commandDescription = impl.description();
|
||||
if (commandDescription != null && !commandDescription.isEmpty()) {
|
||||
|
||||
@ -140,8 +140,8 @@ public enum Config {
|
||||
JobExpireMinutes("Advanced", ManagementServer.class, String.class, "job.expire.minutes", "1440", "Time (in minutes) for async-jobs to be kept in system", null),
|
||||
JobCancelThresholdMinutes("Advanced", ManagementServer.class, String.class, "job.cancel.threshold.minutes", "60", "Time (in minutes) for async-jobs to be forcely cancelled if it has been in process for long", null),
|
||||
SwiftEnable("Advanced", ManagementServer.class, Boolean.class, "swift.enable", "false", "enable swift ", null),
|
||||
|
||||
EventPurgeInterval("Advanced", ManagementServer.class, Integer.class, "event.purge.interval", "86400", "The interval (in seconds) to wait before running the event purge thread", null),
|
||||
S3Enable("Advanced", ManagementServer.class, Boolean.class, "s3.enable", "false", "enable s3 ", null),
|
||||
EventPurgeInterval("Advanced", ManagementServer.class, Integer.class, "event.purge.interval", "86400", "The interval (in seconds) to wait before running the event purge thread", null),
|
||||
AccountCleanupInterval("Advanced", ManagementServer.class, Integer.class, "account.cleanup.interval", "86400", "The interval (in seconds) between cleanup for removed accounts", null),
|
||||
AllowPublicUserTemplates("Advanced", ManagementServer.class, Integer.class, "allow.public.user.templates", "true", "If false, users will not be able to create public templates.", null),
|
||||
InstanceName("Advanced", AgentManager.class, String.class, "instance.name", "VM", "Name of the deployment instance.", "instanceName"),
|
||||
|
||||
@ -63,7 +63,9 @@ import com.cloud.service.dao.ServiceOfferingDao;
|
||||
import com.cloud.storage.DiskOfferingVO;
|
||||
import com.cloud.storage.SwiftVO;
|
||||
import com.cloud.storage.dao.DiskOfferingDao;
|
||||
import com.cloud.storage.dao.S3Dao;
|
||||
import com.cloud.storage.dao.SwiftDao;
|
||||
import com.cloud.storage.s3.S3Manager;
|
||||
import com.cloud.storage.secondary.SecondaryStorageVmManager;
|
||||
import com.cloud.storage.swift.SwiftManager;
|
||||
import com.cloud.test.IPRangeConfig;
|
||||
@ -118,6 +120,8 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura
|
||||
@Inject
|
||||
SwiftDao _swiftDao;
|
||||
@Inject
|
||||
S3Dao _s3Dao;
|
||||
@Inject
|
||||
ServiceOfferingDao _serviceOfferingDao;
|
||||
@Inject
|
||||
DiskOfferingDao _diskOfferingDao;
|
||||
@ -158,6 +162,8 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura
|
||||
@Inject
|
||||
SwiftManager _swiftMgr;
|
||||
@Inject
|
||||
S3Manager _s3Mgr;
|
||||
@Inject
|
||||
PhysicalNetworkTrafficTypeDao _trafficTypeDao;
|
||||
@Inject
|
||||
NicDao _nicDao;
|
||||
@ -419,6 +425,14 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura
|
||||
if (swift != null) {
|
||||
return " can not change " + Config.SwiftEnable.key() + " after you have added Swift";
|
||||
}
|
||||
if (this._s3Mgr.isS3Enabled()) {
|
||||
return String.format("Swift is not supported when S3 is enabled.");
|
||||
}
|
||||
}
|
||||
if (Config.S3Enable.key().equals(name)) {
|
||||
if (this._swiftMgr.isSwiftEnabled()) {
|
||||
return String.format("S3-backed Secondary Storage is not supported when Swift is enabled.");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -1520,6 +1534,7 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura
|
||||
createDefaultSystemNetworks(zone.getId());
|
||||
|
||||
_swiftMgr.propagateSwiftTmplteOnZone(zone.getId());
|
||||
_s3Mgr.propagateTemplatesToZone(zone);
|
||||
txn.commit();
|
||||
return zone;
|
||||
} catch (Exception ex) {
|
||||
|
||||
@ -147,6 +147,7 @@ import com.cloud.storage.dao.DiskOfferingDaoImpl;
|
||||
import com.cloud.storage.dao.GuestOSCategoryDaoImpl;
|
||||
import com.cloud.storage.dao.GuestOSDaoImpl;
|
||||
import com.cloud.storage.dao.LaunchPermissionDaoImpl;
|
||||
import com.cloud.storage.dao.S3DaoImpl;
|
||||
import com.cloud.storage.dao.SnapshotDaoImpl;
|
||||
import com.cloud.storage.dao.SnapshotPolicyDaoImpl;
|
||||
import com.cloud.storage.dao.SnapshotScheduleDaoImpl;
|
||||
@ -159,11 +160,13 @@ import com.cloud.storage.dao.VMTemplateDaoImpl;
|
||||
import com.cloud.storage.dao.VMTemplateDetailsDaoImpl;
|
||||
import com.cloud.storage.dao.VMTemplateHostDaoImpl;
|
||||
import com.cloud.storage.dao.VMTemplatePoolDaoImpl;
|
||||
import com.cloud.storage.dao.VMTemplateS3DaoImpl;
|
||||
import com.cloud.storage.dao.VMTemplateSwiftDaoImpl;
|
||||
import com.cloud.storage.dao.VMTemplateZoneDaoImpl;
|
||||
import com.cloud.storage.dao.VolumeDaoImpl;
|
||||
import com.cloud.storage.dao.VolumeHostDaoImpl;
|
||||
import com.cloud.storage.download.DownloadMonitorImpl;
|
||||
import com.cloud.storage.s3.S3ManagerImpl;
|
||||
import com.cloud.storage.secondary.SecondaryStorageManagerImpl;
|
||||
import com.cloud.storage.snapshot.SnapshotManagerImpl;
|
||||
import com.cloud.storage.snapshot.SnapshotSchedulerImpl;
|
||||
@ -270,6 +273,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com
|
||||
addDao("VMTemplateHostDao", VMTemplateHostDaoImpl.class);
|
||||
addDao("VolumeHostDao", VolumeHostDaoImpl.class);
|
||||
addDao("VMTemplateSwiftDao", VMTemplateSwiftDaoImpl.class);
|
||||
addDao("VMTemplateS3Dao", VMTemplateS3DaoImpl.class);
|
||||
addDao("UploadDao", UploadDaoImpl.class);
|
||||
addDao("VMTemplatePoolDao", VMTemplatePoolDaoImpl.class);
|
||||
addDao("LaunchPermissionDao", LaunchPermissionDaoImpl.class);
|
||||
@ -318,6 +322,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com
|
||||
addDao("KeystoreDao", KeystoreDaoImpl.class);
|
||||
addDao("DcDetailsDao", DcDetailsDaoImpl.class);
|
||||
addDao("SwiftDao", SwiftDaoImpl.class);
|
||||
addDao("S3Dao", S3DaoImpl.class);
|
||||
addDao("AgentTransferMapDao", HostTransferMapDaoImpl.class);
|
||||
addDao("ProjectDao", ProjectDaoImpl.class);
|
||||
addDao("InlineLoadBalancerNicMapDao", InlineLoadBalancerNicMapDaoImpl.class);
|
||||
@ -403,6 +408,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com
|
||||
info.addParameter("consoleproxy.sslEnabled", "true");
|
||||
addManager("ProjectManager", ProjectManagerImpl.class);
|
||||
addManager("SwiftManager", SwiftManagerImpl.class);
|
||||
addManager("S3Manager", S3ManagerImpl.class);
|
||||
addManager("StorageNetworkManager", StorageNetworkManagerImpl.class);
|
||||
addManager("ExternalLoadBalancerUsageManager", ExternalLoadBalancerUsageManagerImpl.class);
|
||||
addManager("HA Manager", HighAvailabilityManagerImpl.class);
|
||||
|
||||
@ -30,6 +30,11 @@ import java.util.Set;
|
||||
import javax.ejb.Local;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.api.commands.AddS3Cmd;
|
||||
import com.cloud.api.commands.ListS3sCmd;
|
||||
import com.cloud.storage.S3;
|
||||
import com.cloud.storage.S3VO;
|
||||
import com.cloud.storage.s3.S3Manager;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.agent.AgentManager;
|
||||
@ -181,6 +186,8 @@ public class ResourceManagerImpl implements ResourceManager, ResourceService, Ma
|
||||
@Inject
|
||||
protected SwiftManager _swiftMgr;
|
||||
@Inject
|
||||
protected S3Manager _s3Mgr;
|
||||
@Inject
|
||||
protected HostDetailsDao _hostDetailsDao;
|
||||
@Inject
|
||||
protected ConfigurationDao _configDao;
|
||||
@ -561,6 +568,16 @@ public class ResourceManagerImpl implements ResourceManager, ResourceService, Ma
|
||||
return _swiftMgr.listSwifts(cmd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public S3 discoverS3(final AddS3Cmd cmd) throws DiscoveryException {
|
||||
return this._s3Mgr.addS3(cmd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<S3VO> listS3s(final ListS3sCmd cmd) {
|
||||
return this._s3Mgr.listS3s(cmd);
|
||||
}
|
||||
|
||||
private List<HostVO> discoverHostsFull(Long dcId, Long podId, Long clusterId, String clusterName, String url, String username, String password, String hypervisorType, List<String> hostTags,
|
||||
Map<String, String> params) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException {
|
||||
URI uri = null;
|
||||
|
||||
@ -151,11 +151,13 @@ import com.cloud.storage.dao.StoragePoolWorkDao;
|
||||
import com.cloud.storage.dao.VMTemplateDao;
|
||||
import com.cloud.storage.dao.VMTemplateHostDao;
|
||||
import com.cloud.storage.dao.VMTemplatePoolDao;
|
||||
import com.cloud.storage.dao.VMTemplateS3Dao;
|
||||
import com.cloud.storage.dao.VMTemplateSwiftDao;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.storage.dao.VolumeHostDao;
|
||||
import com.cloud.storage.download.DownloadMonitor;
|
||||
import com.cloud.storage.listener.StoragePoolMonitor;
|
||||
import com.cloud.storage.s3.S3Manager;
|
||||
import com.cloud.storage.secondary.SecondaryStorageVmManager;
|
||||
import com.cloud.storage.snapshot.SnapshotManager;
|
||||
import com.cloud.storage.snapshot.SnapshotScheduler;
|
||||
@ -262,6 +264,10 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
|
||||
@Inject
|
||||
protected VMTemplateSwiftDao _vmTemplateSwiftDao = null;
|
||||
@Inject
|
||||
protected VMTemplateS3Dao _vmTemplateS3Dao;
|
||||
@Inject
|
||||
protected S3Manager _s3Mgr;
|
||||
@Inject
|
||||
protected VMTemplateDao _vmTemplateDao = null;
|
||||
@Inject
|
||||
protected StoragePoolHostDao _poolHostDao = null;
|
||||
@ -707,6 +713,8 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
|
||||
try {
|
||||
if (snapshot.getSwiftId() != null && snapshot.getSwiftId() != 0) {
|
||||
_snapshotMgr.downloadSnapshotsFromSwift(snapshot);
|
||||
} else if (snapshot.getS3Id() != null && snapshot.getS3Id() != 0) {
|
||||
_snapshotMgr.downloadSnapshotsFromS3(snapshot);
|
||||
}
|
||||
CreateVolumeFromSnapshotCommand createVolumeFromSnapshotCommand = new CreateVolumeFromSnapshotCommand(primaryStoragePoolNameLabel, secondaryStoragePoolUrl, dcId, accountId, volumeId,
|
||||
backedUpSnapshotUuid, snapshot.getName(), _createVolumeFromSnapshotWait);
|
||||
@ -2983,6 +2991,14 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
|
||||
if (tsvs != null && tsvs.size() > 0) {
|
||||
size = tsvs.get(0).getSize();
|
||||
}
|
||||
|
||||
if (size == null && _s3Mgr.isS3Enabled()) {
|
||||
VMTemplateS3VO vmTemplateS3VO = _vmTemplateS3Dao.findOneByTemplateId(template.getId());
|
||||
if (vmTemplateS3VO != null) {
|
||||
size = vmTemplateS3VO.getSize();
|
||||
}
|
||||
}
|
||||
|
||||
if (size == null) {
|
||||
List<VMTemplateHostVO> sss = _vmTemplateHostDao.search(sc, null);
|
||||
if (sss == null || sss.size() == 0) {
|
||||
|
||||
29
server/src/com/cloud/storage/dao/S3Dao.java
Normal file
29
server/src/com/cloud/storage/dao/S3Dao.java
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.storage.dao;
|
||||
|
||||
import com.cloud.agent.api.to.S3TO;
|
||||
import com.cloud.storage.S3VO;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
public interface S3Dao extends GenericDao<S3VO, Long> {
|
||||
|
||||
S3TO getS3TO(final Long id);
|
||||
|
||||
}
|
||||
47
server/src/com/cloud/storage/dao/S3DaoImpl.java
Normal file
47
server/src/com/cloud/storage/dao/S3DaoImpl.java
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.storage.dao;
|
||||
|
||||
import com.cloud.agent.api.to.S3TO;
|
||||
import com.cloud.storage.S3VO;
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
|
||||
import javax.ejb.Local;
|
||||
|
||||
@Local(S3Dao.class)
|
||||
public class S3DaoImpl extends GenericDaoBase<S3VO, Long> implements S3Dao {
|
||||
|
||||
@Override
|
||||
public S3TO getS3TO(final Long id) {
|
||||
|
||||
if (id != null) {
|
||||
|
||||
final S3VO s3VO = findById(id);
|
||||
if (s3VO != null) {
|
||||
return s3VO.toS3TO();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// NOTE: Excluded listAll / shuffle operation implemented in SwiftDaoImpl ...
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
@ -71,4 +71,6 @@ public interface VMTemplateDao extends GenericDao<VMTemplateVO, Long> {
|
||||
List<Long> listPrivateTemplatesByHost(Long hostId);
|
||||
public Long countTemplatesForAccount(long accountId);
|
||||
|
||||
List<VMTemplateVO> findTemplatesToSyncToS3();
|
||||
|
||||
}
|
||||
|
||||
@ -92,6 +92,14 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
|
||||
|
||||
private final String SELECT_TEMPLATE_SWIFT_REF = "SELECT t.id, t.unique_name, t.name, t.public, t.featured, t.type, t.hvm, t.bits, t.url, t.format, t.created, t.account_id, "
|
||||
+ "t.checksum, t.display_text, t.enable_password, t.guest_os_id, t.bootable, t.prepopulate, t.cross_zones, t.hypervisor_type FROM vm_template t";
|
||||
|
||||
private static final String SELECT_S3_CANDIDATE_TEMPLATES = "SELECT t.id, t.unique_name, t.name, t.public, t.featured, " +
|
||||
"t.type, t.hvm, t.bits, t.url, t.format, t.created, t.account_id, t.checksum, t.display_text, " +
|
||||
"t.enable_password, t.guest_os_id, t.bootable, t.prepopulate, t.cross_zones, t.hypervisor_type " +
|
||||
"FROM vm_template t JOIN template_host_ref r ON t.id=r.template_id JOIN host h ON h.id=r.host_id " +
|
||||
"WHERE t.hypervisor_type IN (SELECT hypervisor_type FROM host) AND r.download_state = 'DOWNLOADED' AND " +
|
||||
"r.template_id NOT IN (SELECT template_id FROM template_s3_ref) AND r.destroyed = 0 AND t.type <> 'PERHOST'";
|
||||
|
||||
protected SearchBuilder<VMTemplateVO> TemplateNameSearch;
|
||||
protected SearchBuilder<VMTemplateVO> UniqueNameSearch;
|
||||
protected SearchBuilder<VMTemplateVO> tmpltTypeSearch;
|
||||
@ -917,5 +925,10 @@ public class VMTemplateDaoImpl extends GenericDaoBase<VMTemplateVO, Long> implem
|
||||
(accountType == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) ||
|
||||
(accountType == Account.ACCOUNT_TYPE_READ_ONLY_ADMIN));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<VMTemplateVO> findTemplatesToSyncToS3() {
|
||||
return executeList(SELECT_S3_CANDIDATE_TEMPLATES, new Object[] {});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
36
server/src/com/cloud/storage/dao/VMTemplateS3Dao.java
Normal file
36
server/src/com/cloud/storage/dao/VMTemplateS3Dao.java
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.storage.dao;
|
||||
|
||||
import com.cloud.storage.VMTemplateS3VO;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface VMTemplateS3Dao extends GenericDao<VMTemplateS3VO, Long> {
|
||||
|
||||
List<VMTemplateS3VO> listByS3Id(long id);
|
||||
|
||||
VMTemplateS3VO findOneByTemplateId(long id);
|
||||
|
||||
VMTemplateS3VO findOneByS3Template(long s3Id, long templateId);
|
||||
|
||||
void expungeAllByTemplateId(long templateId);
|
||||
|
||||
}
|
||||
101
server/src/com/cloud/storage/dao/VMTemplateS3DaoImpl.java
Normal file
101
server/src/com/cloud/storage/dao/VMTemplateS3DaoImpl.java
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.storage.dao;
|
||||
|
||||
import static com.cloud.utils.db.SearchCriteria.Op.*;
|
||||
import static com.cloud.storage.VMTemplateS3VO.*;
|
||||
|
||||
import com.cloud.storage.VMTemplateS3VO;
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
|
||||
import javax.ejb.Local;
|
||||
import java.util.List;
|
||||
|
||||
@Local(VMTemplateS3Dao.class)
|
||||
public class VMTemplateS3DaoImpl extends GenericDaoBase<VMTemplateS3VO, Long>
|
||||
implements VMTemplateS3Dao {
|
||||
|
||||
private final SearchBuilder<VMTemplateS3VO> searchBuilder;
|
||||
|
||||
public VMTemplateS3DaoImpl() {
|
||||
|
||||
super();
|
||||
|
||||
this.searchBuilder = createSearchBuilder();
|
||||
this.searchBuilder
|
||||
.and(S3_ID_COLUMN_NAME, this.searchBuilder.entity().getS3Id(),
|
||||
EQ)
|
||||
.and(TEMPLATE_ID_COLUMN_NAME,
|
||||
this.searchBuilder.entity().getTemplateId(), EQ).done();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VMTemplateS3VO> listByS3Id(final long s3id) {
|
||||
|
||||
final SearchCriteria<VMTemplateS3VO> criteria = this.searchBuilder
|
||||
.create();
|
||||
|
||||
criteria.setParameters(S3_ID_COLUMN_NAME, s3id);
|
||||
|
||||
return this.listBy(criteria);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public VMTemplateS3VO findOneByTemplateId(final long templateId) {
|
||||
|
||||
final SearchCriteria<VMTemplateS3VO> criteria = this.searchBuilder
|
||||
.create();
|
||||
|
||||
criteria.setParameters(TEMPLATE_ID_COLUMN_NAME, templateId);
|
||||
|
||||
return this.findOneBy(criteria);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public VMTemplateS3VO findOneByS3Template(final long s3Id,
|
||||
final long templateId) {
|
||||
|
||||
final SearchCriteria<VMTemplateS3VO> criteria = this.searchBuilder
|
||||
.create();
|
||||
|
||||
criteria.setParameters(S3_ID_COLUMN_NAME, s3Id);
|
||||
criteria.setParameters(TEMPLATE_ID_COLUMN_NAME, templateId);
|
||||
|
||||
return this.findOneBy(criteria);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expungeAllByTemplateId(long templateId) {
|
||||
|
||||
final SearchCriteria<VMTemplateS3VO> criteria = this.searchBuilder
|
||||
.create();
|
||||
|
||||
criteria.setParameters(TEMPLATE_ID_COLUMN_NAME, templateId);
|
||||
|
||||
this.expunge(criteria);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
63
server/src/com/cloud/storage/s3/S3Manager.java
Normal file
63
server/src/com/cloud/storage/s3/S3Manager.java
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.storage.s3;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.agent.api.to.S3TO;
|
||||
import com.cloud.api.commands.AddS3Cmd;
|
||||
import com.cloud.api.commands.ListS3sCmd;
|
||||
import com.cloud.dc.DataCenterVO;
|
||||
import com.cloud.exception.DiscoveryException;
|
||||
import com.cloud.storage.S3;
|
||||
import com.cloud.storage.S3VO;
|
||||
import com.cloud.storage.VMTemplateS3VO;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.utils.component.Manager;
|
||||
|
||||
public interface S3Manager extends Manager {
|
||||
|
||||
S3TO getS3TO();
|
||||
|
||||
S3TO getS3TO(Long s3Id);
|
||||
|
||||
S3 addS3(AddS3Cmd addS3Cmd) throws DiscoveryException;
|
||||
|
||||
Long chooseZoneForTemplateExtract(VMTemplateVO template);
|
||||
|
||||
boolean isS3Enabled();
|
||||
|
||||
boolean isTemplateInstalled(Long templateId);
|
||||
|
||||
void deleteTemplate(final Long accountId, final Long templateId);
|
||||
|
||||
String downloadTemplateFromS3ToSecondaryStorage(final long dcId,
|
||||
final long templateId, final int primaryStorageDownloadWait);
|
||||
|
||||
List<S3VO> listS3s(ListS3sCmd listS3sCmd);
|
||||
|
||||
VMTemplateS3VO findByTemplateId(Long templateId);
|
||||
|
||||
void propagateTemplatesToZone(DataCenterVO zone);
|
||||
|
||||
void propagateTemplateToAllZones(VMTemplateS3VO vmTemplateS3VO);
|
||||
|
||||
void uploadTemplateToS3FromSecondaryStorage(final VMTemplateVO template);
|
||||
|
||||
}
|
||||
669
server/src/com/cloud/storage/s3/S3ManagerImpl.java
Normal file
669
server/src/com/cloud/storage/s3/S3ManagerImpl.java
Normal file
@ -0,0 +1,669 @@
|
||||
/*
|
||||
* 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.storage.s3;
|
||||
|
||||
import static com.cloud.storage.S3VO.ID_COLUMN_NAME;
|
||||
import static com.cloud.utils.DateUtil.now;
|
||||
import static com.cloud.utils.S3Utils.canConnect;
|
||||
import static com.cloud.utils.S3Utils.canReadWriteBucket;
|
||||
import static com.cloud.utils.S3Utils.checkBucketName;
|
||||
import static com.cloud.utils.S3Utils.checkClientOptions;
|
||||
import static com.cloud.utils.S3Utils.doesBucketExist;
|
||||
import static com.cloud.utils.StringUtils.join;
|
||||
import static com.cloud.utils.db.GlobalLock.executeWithNoWaitLock;
|
||||
import static com.cloud.utils.db.SearchCriteria.Op.EQ;
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static java.lang.String.format;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.shuffle;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import javax.ejb.Local;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.agent.AgentManager;
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.DeleteTemplateFromS3Command;
|
||||
import com.cloud.agent.api.DownloadTemplateFromS3ToSecondaryStorageCommand;
|
||||
import com.cloud.agent.api.UploadTemplateToS3FromSecondaryStorageCommand;
|
||||
import com.cloud.agent.api.to.S3TO;
|
||||
import com.cloud.api.commands.AddS3Cmd;
|
||||
import com.cloud.api.commands.ListS3sCmd;
|
||||
import com.cloud.configuration.Config;
|
||||
import com.cloud.configuration.dao.ConfigurationDao;
|
||||
import com.cloud.dc.DataCenterVO;
|
||||
import com.cloud.dc.dao.DataCenterDao;
|
||||
import com.cloud.exception.DiscoveryException;
|
||||
import com.cloud.host.HostVO;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
import com.cloud.storage.S3;
|
||||
import com.cloud.storage.S3VO;
|
||||
import com.cloud.storage.VMTemplateHostVO;
|
||||
import com.cloud.storage.VMTemplateS3VO;
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.storage.VMTemplateZoneVO;
|
||||
import com.cloud.storage.dao.S3Dao;
|
||||
import com.cloud.storage.dao.VMTemplateDao;
|
||||
import com.cloud.storage.dao.VMTemplateHostDao;
|
||||
import com.cloud.storage.dao.VMTemplateS3Dao;
|
||||
import com.cloud.storage.dao.VMTemplateZoneDao;
|
||||
import com.cloud.storage.secondary.SecondaryStorageVmManager;
|
||||
import com.cloud.utils.S3Utils.ClientOptions;
|
||||
import com.cloud.utils.component.Inject;
|
||||
import com.cloud.utils.db.Filter;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
|
||||
@Local(value = { S3Manager.class })
|
||||
public class S3ManagerImpl implements S3Manager {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(S3ManagerImpl.class);
|
||||
|
||||
private String name;
|
||||
|
||||
@Inject
|
||||
private AgentManager agentManager;
|
||||
|
||||
@Inject
|
||||
private S3Dao s3Dao;
|
||||
|
||||
@Inject
|
||||
private VMTemplateZoneDao vmTemplateZoneDao;
|
||||
|
||||
@Inject
|
||||
private VMTemplateS3Dao vmTemplateS3Dao;
|
||||
|
||||
@Inject
|
||||
private VMTemplateHostDao vmTemplateHostDao;
|
||||
|
||||
@Inject
|
||||
private VMTemplateDao vmTemplateDao;
|
||||
|
||||
@Inject
|
||||
private ConfigurationDao configurationDao;
|
||||
|
||||
@Inject
|
||||
private DataCenterDao dataCenterDao;
|
||||
|
||||
@Inject
|
||||
private HostDao hostDao;
|
||||
|
||||
@Inject
|
||||
private SecondaryStorageVmManager secondaryStorageVMManager;
|
||||
|
||||
protected S3ManagerImpl() {
|
||||
super();
|
||||
}
|
||||
|
||||
private void verifyConnection(final S3TO s3) throws DiscoveryException {
|
||||
|
||||
if (!canConnect(s3)) {
|
||||
throw new DiscoveryException(format("Unable to connect to S3 "
|
||||
+ "using access key %1$s, secret key %2$s, and endpoint, "
|
||||
+ "%3$S", s3.getAccessKey(), s3.getSecretKey(),
|
||||
s3.getEndPoint() != null ? s3.getEndPoint() : "default"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void verifyBuckets(S3TO s3) throws DiscoveryException {
|
||||
|
||||
final List<String> errorMessages = new ArrayList<String>();
|
||||
|
||||
errorMessages.addAll(verifyBucket(s3, s3.getBucketName()));
|
||||
|
||||
throwDiscoveryExceptionFromErrorMessages(errorMessages);
|
||||
|
||||
}
|
||||
|
||||
private List<String> verifyBucket(final ClientOptions clientOptions,
|
||||
final String bucketName) {
|
||||
|
||||
if (!doesBucketExist(clientOptions, bucketName)) {
|
||||
return singletonList(format("Bucket %1$s does not exist.",
|
||||
bucketName));
|
||||
}
|
||||
|
||||
if (!canReadWriteBucket(clientOptions, bucketName)) {
|
||||
return singletonList(format("Can read/write from bucket %1$s.",
|
||||
bucketName));
|
||||
}
|
||||
|
||||
return emptyList();
|
||||
}
|
||||
|
||||
private void validateFields(final S3VO s3VO) {
|
||||
|
||||
final List<String> errorMessages = new ArrayList<String>();
|
||||
|
||||
errorMessages.addAll(checkClientOptions(s3VO.toS3TO()));
|
||||
|
||||
errorMessages.addAll(checkBucketName("template", s3VO.getBucketName()));
|
||||
|
||||
throwDiscoveryExceptionFromErrorMessages(errorMessages);
|
||||
|
||||
}
|
||||
|
||||
private void enforceS3PreConditions() throws DiscoveryException {
|
||||
|
||||
if (!this.isS3Enabled()) {
|
||||
throw new DiscoveryException("S3 is not enabled.");
|
||||
}
|
||||
|
||||
if (this.getS3TO() != null) {
|
||||
throw new DiscoveryException("Attempt to define multiple S3 "
|
||||
+ "instances. Only one instance definition is supported.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void throwDiscoveryExceptionFromErrorMessages(
|
||||
final List<String> errorMessages) {
|
||||
|
||||
if (!errorMessages.isEmpty()) {
|
||||
throw new CloudRuntimeException(join(errorMessages, " "));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private String determineLockId(final long accountId, final long templateId) {
|
||||
|
||||
// TBD The lock scope may be too coarse grained. Deletes need to lock
|
||||
// the template across all zones where upload and download could
|
||||
// probably safely scoped to the zone ...
|
||||
return join(asList("S3_TEMPLATE", accountId, templateId), "_");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public S3TO getS3TO(final Long s3Id) {
|
||||
return this.s3Dao.getS3TO(s3Id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public S3TO getS3TO() {
|
||||
|
||||
final List<S3VO> s3s = this.s3Dao.listAll();
|
||||
|
||||
if (s3s == null || (s3s != null && s3s.isEmpty())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (s3s.size() == 1) {
|
||||
return s3s.get(0).toS3TO();
|
||||
}
|
||||
|
||||
throw new CloudRuntimeException("Multiple S3 instances have been "
|
||||
+ "defined. Only one instance configuration is supported.");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public S3 addS3(final AddS3Cmd addS3Cmd) throws DiscoveryException {
|
||||
|
||||
this.enforceS3PreConditions();
|
||||
|
||||
final S3VO s3VO = new S3VO(UUID.randomUUID().toString(),
|
||||
addS3Cmd.getAccessKey(), addS3Cmd.getSecretKey(),
|
||||
addS3Cmd.getEndPoint(), addS3Cmd.getBucketName(),
|
||||
addS3Cmd.getHttpsFlag(), addS3Cmd.getConnectionTimeout(),
|
||||
addS3Cmd.getMaxErrorRetry(), addS3Cmd.getSocketTimeout(), now());
|
||||
|
||||
this.validateFields(s3VO);
|
||||
|
||||
final S3TO s3 = s3VO.toS3TO();
|
||||
this.verifyConnection(s3);
|
||||
this.verifyBuckets(s3);
|
||||
|
||||
return this.s3Dao.persist(s3VO);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isS3Enabled() {
|
||||
return Boolean
|
||||
.valueOf(configurationDao.getValue(Config.S3Enable.key()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTemplateInstalled(final Long templateId) {
|
||||
throw new UnsupportedOperationException(
|
||||
"S3Manager#isTemplateInstalled (DeleteIsoCmd) has not yet "
|
||||
+ "been implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteTemplate(final Long templateId, final Long accountId) {
|
||||
|
||||
final S3TO s3 = getS3TO();
|
||||
|
||||
if (s3 == null) {
|
||||
final String errorMessage = "Delete Template Failed: No S3 configuration defined.";
|
||||
LOGGER.error(errorMessage);
|
||||
throw new CloudRuntimeException(errorMessage);
|
||||
}
|
||||
|
||||
final VMTemplateS3VO vmTemplateS3VO = vmTemplateS3Dao
|
||||
.findOneByS3Template(s3.getId(), templateId);
|
||||
if (vmTemplateS3VO == null) {
|
||||
final String errorMessage = format(
|
||||
"Delete Template Failed: Unable to find Template %1$s in S3.",
|
||||
templateId);
|
||||
LOGGER.error(errorMessage);
|
||||
throw new CloudRuntimeException(errorMessage);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
executeWithNoWaitLock(determineLockId(accountId, templateId),
|
||||
new Callable<Void>() {
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
|
||||
final Answer answer = agentManager.sendToSSVM(null,
|
||||
new DeleteTemplateFromS3Command(s3,
|
||||
accountId, templateId));
|
||||
if (answer == null || !answer.getResult()) {
|
||||
final String errorMessage = format(
|
||||
"Delete Template Failed: Unable to delete template id %1$s from S3 due to following error: %2$s",
|
||||
templateId,
|
||||
((answer == null) ? "answer is null"
|
||||
: answer.getDetails()));
|
||||
LOGGER.error(errorMessage);
|
||||
throw new CloudRuntimeException(errorMessage);
|
||||
}
|
||||
|
||||
vmTemplateS3Dao.remove(vmTemplateS3VO.getId());
|
||||
LOGGER.debug(format(
|
||||
"Deleted template %1$s from S3.",
|
||||
templateId));
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
final String errorMessage = format(
|
||||
"Delete Template Failed: Unable to delete template id %1$s from S3 due to the following error: %2$s.",
|
||||
templateId, e.getMessage());
|
||||
LOGGER.error(errorMessage);
|
||||
throw new CloudRuntimeException(errorMessage, e);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public String downloadTemplateFromS3ToSecondaryStorage(
|
||||
final long dataCenterId, final long templateId,
|
||||
final int primaryStorageDownloadWait) {
|
||||
|
||||
if (!isS3Enabled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final VMTemplateVO template = vmTemplateDao.findById(templateId);
|
||||
if (template == null) {
|
||||
final String errorMessage = String
|
||||
.format("Failed to download template id %1$s from S3 because the template definition was not found.",
|
||||
templateId);
|
||||
LOGGER.error(errorMessage);
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
final VMTemplateS3VO templateS3VO = findByTemplateId(templateId);
|
||||
if (templateS3VO == null) {
|
||||
final String errorMessage = format(
|
||||
"Failed to download template id %1$s from S3 because it does not exist in S3.",
|
||||
templateId);
|
||||
LOGGER.error(errorMessage);
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
final S3TO s3 = getS3TO(templateS3VO.getS3Id());
|
||||
if (s3 == null) {
|
||||
final String errorMessage = format(
|
||||
"Failed to download template id %1$s from S3 because S3 id %2$s does not exist.",
|
||||
templateId, templateS3VO);
|
||||
LOGGER.error(errorMessage);
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
final HostVO secondaryStorageHost = secondaryStorageVMManager
|
||||
.findSecondaryStorageHost(dataCenterId);
|
||||
if (secondaryStorageHost == null) {
|
||||
final String errorMessage = format(
|
||||
"Unable to find secondary storage host for zone id %1$s.",
|
||||
dataCenterId);
|
||||
LOGGER.error(errorMessage);
|
||||
throw new CloudRuntimeException(errorMessage);
|
||||
}
|
||||
|
||||
final long accountId = template.getAccountId();
|
||||
final DownloadTemplateFromS3ToSecondaryStorageCommand cmd = new DownloadTemplateFromS3ToSecondaryStorageCommand(
|
||||
s3, accountId, templateId, secondaryStorageHost.getName(),
|
||||
primaryStorageDownloadWait);
|
||||
|
||||
try {
|
||||
|
||||
executeWithNoWaitLock(determineLockId(accountId, templateId),
|
||||
new Callable<Void>() {
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
|
||||
final Answer answer = agentManager.sendToSSVM(
|
||||
dataCenterId, cmd);
|
||||
|
||||
if (answer == null || !answer.getResult()) {
|
||||
final String errMsg = String
|
||||
.format("Failed to download template from S3 to secondary storage due to %1$s",
|
||||
(answer == null ? "answer is null"
|
||||
: answer.getDetails()));
|
||||
LOGGER.error(errMsg);
|
||||
throw new CloudRuntimeException(errMsg);
|
||||
}
|
||||
|
||||
final String installPath = join(
|
||||
asList("template", "tmpl", accountId,
|
||||
templateId), File.separator);
|
||||
final VMTemplateHostVO tmpltHost = new VMTemplateHostVO(
|
||||
secondaryStorageHost.getId(), templateId,
|
||||
now(), 100, Status.DOWNLOADED, null, null,
|
||||
null, installPath, template.getUrl());
|
||||
tmpltHost.setSize(templateS3VO.getSize());
|
||||
tmpltHost.setPhysicalSize(templateS3VO
|
||||
.getPhysicalSize());
|
||||
vmTemplateHostDao.persist(tmpltHost);
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
final String errMsg = "Failed to download template from S3 to secondary storage due to "
|
||||
+ e.toString();
|
||||
LOGGER.error(errMsg);
|
||||
throw new CloudRuntimeException(errMsg);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<S3VO> listS3s(final ListS3sCmd cmd) {
|
||||
|
||||
final Filter filter = new Filter(S3VO.class, ID_COLUMN_NAME, TRUE,
|
||||
cmd.getStartIndex(), cmd.getPageSizeVal());
|
||||
final SearchCriteria<S3VO> criteria = this.s3Dao.createSearchCriteria();
|
||||
|
||||
if (cmd.getId() != null) {
|
||||
criteria.addAnd(ID_COLUMN_NAME, EQ, cmd.getId());
|
||||
}
|
||||
|
||||
return this.s3Dao.search(criteria, filter);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public VMTemplateS3VO findByTemplateId(final Long templateId) {
|
||||
throw new UnsupportedOperationException(
|
||||
"S3Manager#findByTemplateId(Long) has not yet "
|
||||
+ "been implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propagateTemplatesToZone(final DataCenterVO zone) {
|
||||
|
||||
if (!isS3Enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final List<VMTemplateS3VO> s3VMTemplateRefs = this.vmTemplateS3Dao
|
||||
.listAll();
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
LOGGER.info(format("Propagating %1$s templates to zone %2$s.",
|
||||
s3VMTemplateRefs.size(), zone.getName()));
|
||||
}
|
||||
|
||||
for (final VMTemplateS3VO templateS3VO : s3VMTemplateRefs) {
|
||||
this.vmTemplateZoneDao.persist(new VMTemplateZoneVO(zone.getId(),
|
||||
templateS3VO.getTemplateId(), now()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean configure(final String name, final Map<String, Object> params)
|
||||
throws ConfigurationException {
|
||||
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
LOGGER.info(format("Configuring S3 Manager %1$s", name));
|
||||
}
|
||||
|
||||
this.name = name;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean start() {
|
||||
LOGGER.info("Starting S3 Manager");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stop() {
|
||||
LOGGER.info("Stopping S3 Manager");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propagateTemplateToAllZones(final VMTemplateS3VO vmTemplateS3VO) {
|
||||
|
||||
final long templateId = vmTemplateS3VO.getId();
|
||||
|
||||
if (!isS3Enabled()) {
|
||||
if (LOGGER.isTraceEnabled()) {
|
||||
LOGGER.trace(format(
|
||||
"Attempt to propogate template id %1$s across all zones. However, S3 is not enabled.",
|
||||
templateId));
|
||||
}
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
final S3TO s3 = getS3TO();
|
||||
|
||||
if (s3 == null) {
|
||||
LOGGER.warn(format(
|
||||
"Unable to propagate template id %1$s across all zones because S3 is enabled, but not configured.",
|
||||
templateId));
|
||||
return;
|
||||
}
|
||||
|
||||
if (vmTemplateS3VO != null) {
|
||||
final List<DataCenterVO> dataCenters = dataCenterDao.listAll();
|
||||
for (DataCenterVO dataCenter : dataCenters) {
|
||||
final VMTemplateZoneVO tmpltZoneVO = new VMTemplateZoneVO(
|
||||
dataCenter.getId(), templateId, now());
|
||||
vmTemplateZoneDao.persist(tmpltZoneVO);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long chooseZoneForTemplateExtract(VMTemplateVO template) {
|
||||
|
||||
final S3TO s3 = getS3TO();
|
||||
|
||||
if (s3 == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final List<VMTemplateHostVO> templateHosts = vmTemplateHostDao
|
||||
.listByOnlyTemplateId(template.getId());
|
||||
if (templateHosts != null) {
|
||||
shuffle(templateHosts);
|
||||
for (VMTemplateHostVO vmTemplateHostVO : templateHosts) {
|
||||
final HostVO host = hostDao.findById(vmTemplateHostVO
|
||||
.getHostId());
|
||||
if (host != null) {
|
||||
return host.getDataCenterId();
|
||||
}
|
||||
throw new CloudRuntimeException(
|
||||
format("Unable to find secondary storage host for template id %1$s.",
|
||||
template.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
final List<DataCenterVO> dataCenters = dataCenterDao.listAll();
|
||||
shuffle(dataCenters);
|
||||
return dataCenters.get(0).getId();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uploadTemplateToS3FromSecondaryStorage(
|
||||
final VMTemplateVO template) {
|
||||
|
||||
final Long templateId = template.getId();
|
||||
|
||||
final List<VMTemplateHostVO> templateHostRefs = vmTemplateHostDao
|
||||
.listByTemplateId(templateId);
|
||||
|
||||
if (templateHostRefs == null
|
||||
|| (templateHostRefs != null && templateHostRefs.isEmpty())) {
|
||||
throw new CloudRuntimeException(
|
||||
format("Attempt to sync template id %1$s that is not attached to a host.",
|
||||
templateId));
|
||||
}
|
||||
|
||||
final VMTemplateHostVO templateHostRef = templateHostRefs.get(0);
|
||||
|
||||
if (!isS3Enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final S3TO s3 = getS3TO();
|
||||
if (s3 == null) {
|
||||
LOGGER.warn("S3 Template Sync Failed: Attempt to sync templates with S3, but no S3 instance defined.");
|
||||
return;
|
||||
}
|
||||
|
||||
final HostVO secondaryHost = this.hostDao.findById(templateHostRef
|
||||
.getHostId());
|
||||
if (secondaryHost == null) {
|
||||
throw new CloudRuntimeException(format(
|
||||
"Unable to find secondary storage host id %1$s.",
|
||||
templateHostRef.getHostId()));
|
||||
}
|
||||
|
||||
final Long dataCenterId = secondaryHost.getDataCenterId();
|
||||
final Long accountId = template.getAccountId();
|
||||
|
||||
try {
|
||||
|
||||
executeWithNoWaitLock(determineLockId(accountId, templateId),
|
||||
new Callable<Void>() {
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
|
||||
final UploadTemplateToS3FromSecondaryStorageCommand cmd = new UploadTemplateToS3FromSecondaryStorageCommand(
|
||||
s3, secondaryHost.getStorageUrl(),
|
||||
dataCenterId, accountId, templateId);
|
||||
|
||||
final Answer answer = agentManager.sendToSSVM(
|
||||
dataCenterId, cmd);
|
||||
if (answer == null || !answer.getResult()) {
|
||||
|
||||
final String reason = answer != null ? answer
|
||||
.getDetails()
|
||||
: "S3 template sync failed due to an unspecified error.";
|
||||
throw new CloudRuntimeException(
|
||||
format("Failed to upload template id %1$s to S3 from secondary storage due to %2$s.",
|
||||
templateId, reason));
|
||||
|
||||
}
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(format(
|
||||
"Creating VMTemplateS3VO instance using template id %1s.",
|
||||
templateId));
|
||||
}
|
||||
|
||||
final VMTemplateS3VO vmTemplateS3VO = new VMTemplateS3VO(
|
||||
s3.getId(), templateId, now(),
|
||||
templateHostRef.getSize(), templateHostRef
|
||||
.getPhysicalSize());
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(format("Persisting %1$s",
|
||||
vmTemplateS3VO));
|
||||
}
|
||||
|
||||
vmTemplateS3Dao.persist(vmTemplateS3VO);
|
||||
propagateTemplateToAllZones(vmTemplateS3VO);
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
final String errorMessage = format(
|
||||
"Failed to upload template id %1$s for zone id %2$s to S3.",
|
||||
templateId, dataCenterId);
|
||||
LOGGER.error(errorMessage, e);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -128,6 +128,8 @@ public interface SnapshotManager {
|
||||
|
||||
void downloadSnapshotsFromSwift(SnapshotVO ss);
|
||||
|
||||
void downloadSnapshotsFromS3(SnapshotVO snapshot);
|
||||
|
||||
HostVO getSecondaryStorageHost(SnapshotVO snapshot);
|
||||
|
||||
String getSecondaryStorageURL(SnapshotVO snapshot);
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
package com.cloud.storage.snapshot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -34,9 +35,11 @@ import com.cloud.agent.api.BackupSnapshotCommand;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.cloud.agent.api.DeleteSnapshotBackupCommand;
|
||||
import com.cloud.agent.api.DeleteSnapshotsDirCommand;
|
||||
import com.cloud.agent.api.DownloadSnapshotFromS3Command;
|
||||
import com.cloud.agent.api.ManageSnapshotAnswer;
|
||||
import com.cloud.agent.api.ManageSnapshotCommand;
|
||||
import com.cloud.agent.api.downloadSnapshotFromSwiftCommand;
|
||||
import com.cloud.agent.api.to.S3TO;
|
||||
import com.cloud.agent.api.to.SwiftTO;
|
||||
import com.cloud.alert.AlertManager;
|
||||
import com.cloud.api.commands.CreateSnapshotPolicyCmd;
|
||||
@ -91,6 +94,7 @@ import com.cloud.storage.dao.SnapshotScheduleDao;
|
||||
import com.cloud.storage.dao.StoragePoolDao;
|
||||
import com.cloud.storage.dao.VMTemplateDao;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.storage.s3.S3Manager;
|
||||
import com.cloud.storage.secondary.SecondaryStorageVmManager;
|
||||
import com.cloud.storage.swift.SwiftManager;
|
||||
import com.cloud.tags.ResourceTagVO;
|
||||
@ -171,6 +175,8 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
|
||||
private ResourceLimitService _resourceLimitMgr;
|
||||
@Inject
|
||||
private SwiftManager _swiftMgr;
|
||||
@Inject
|
||||
private S3Manager _s3Mgr;
|
||||
@Inject
|
||||
private SecondaryStorageVmManager _ssvmMgr;
|
||||
@Inject
|
||||
@ -477,11 +483,25 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
|
||||
return createdSnapshot;
|
||||
}
|
||||
|
||||
private static void checkObjectStorageConfiguration(SwiftTO swift, S3TO s3) {
|
||||
|
||||
if (swift != null && s3 != null) {
|
||||
throw new CloudRuntimeException(
|
||||
"Swift and S3 are not simultaneously supported for snapshot backup.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSnapshotsForVolume (String secondaryStoragePoolUrl, Long dcId, Long accountId, Long volumeId ){
|
||||
SwiftTO swift = _swiftMgr.getSwiftTO();
|
||||
DeleteSnapshotBackupCommand cmd = new DeleteSnapshotBackupCommand(swift, secondaryStoragePoolUrl, dcId, accountId, volumeId, null, true);
|
||||
S3TO s3 = _s3Mgr.getS3TO();
|
||||
|
||||
checkObjectStorageConfiguration(swift, s3);
|
||||
|
||||
DeleteSnapshotBackupCommand cmd = new DeleteSnapshotBackupCommand(
|
||||
swift, s3, secondaryStoragePoolUrl, dcId, accountId, volumeId,
|
||||
null, true);
|
||||
try {
|
||||
Answer ans = _agentMgr.sendToSSVM(dcId, cmd);
|
||||
if ( ans == null || !ans.getResult() ) {
|
||||
@ -543,6 +563,54 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
|
||||
|
||||
}
|
||||
|
||||
private List<String> determineBackupUuids(final SnapshotVO snapshot) {
|
||||
|
||||
final List<String> backupUuids = new ArrayList<String>();
|
||||
backupUuids.add(0, snapshot.getBackupSnapshotId());
|
||||
|
||||
SnapshotVO tempSnapshot = snapshot;
|
||||
while (tempSnapshot.getPrevSnapshotId() != 0) {
|
||||
tempSnapshot = _snapshotDao.findById(tempSnapshot
|
||||
.getPrevSnapshotId());
|
||||
backupUuids.add(0, tempSnapshot.getBackupSnapshotId());
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(backupUuids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void downloadSnapshotsFromS3(final SnapshotVO snapshot) {
|
||||
|
||||
final VolumeVO volume = _volsDao.findById(snapshot.getVolumeId());
|
||||
final Long zoneId = volume.getDataCenterId();
|
||||
final HostVO secHost = _storageMgr.getSecondaryStorageHost(zoneId);
|
||||
|
||||
final S3TO s3 = _s3Mgr.getS3TO(snapshot.getS3Id());
|
||||
final List<String> backupUuids = determineBackupUuids(snapshot);
|
||||
|
||||
try {
|
||||
String parent = null;
|
||||
for (final String backupUuid : backupUuids) {
|
||||
final DownloadSnapshotFromS3Command cmd = new DownloadSnapshotFromS3Command(
|
||||
s3, parent, secHost.getStorageUrl(), zoneId,
|
||||
volume.getAccountId(), volume.getId(), backupUuid,
|
||||
_backupsnapshotwait);
|
||||
final Answer answer = _agentMgr.sendToSSVM(zoneId, cmd);
|
||||
if ((answer == null) || !answer.getResult()) {
|
||||
throw new CloudRuntimeException(String.format(
|
||||
"S3 snapshot download failed due to %1$s.",
|
||||
answer != null ? answer.getDetails()
|
||||
: "unspecified error"));
|
||||
}
|
||||
parent = backupUuid;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new CloudRuntimeException(
|
||||
"Snapshot download from S3 failed due to " + e.toString(),
|
||||
e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@DB
|
||||
@ -577,6 +645,9 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
|
||||
|
||||
|
||||
SwiftTO swift = _swiftMgr.getSwiftTO();
|
||||
S3TO s3 = _s3Mgr.getS3TO();
|
||||
|
||||
checkObjectStorageConfiguration(swift, s3);
|
||||
|
||||
long prevSnapshotId = snapshot.getPrevSnapshotId();
|
||||
if (prevSnapshotId > 0) {
|
||||
@ -586,7 +657,8 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
|
||||
prevBackupUuid = prevSnapshot.getBackupSnapshotId();
|
||||
prevSnapshotUuid = prevSnapshot.getPath();
|
||||
}
|
||||
} else if ( prevSnapshot.getSwiftId() != null && swift != null ) {
|
||||
} else if ((prevSnapshot.getSwiftId() != null && swift != null)
|
||||
|| (prevSnapshot.getS3Id() != null && s3 != null)) {
|
||||
prevBackupUuid = prevSnapshot.getBackupSnapshotId();
|
||||
prevSnapshotUuid = prevSnapshot.getPath();
|
||||
}
|
||||
@ -599,8 +671,10 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
|
||||
|
||||
if ( swift != null ) {
|
||||
backupSnapshotCommand.setSwift(swift);
|
||||
} else if (s3 != null) {
|
||||
backupSnapshotCommand.setS3(s3);
|
||||
}
|
||||
|
||||
|
||||
String backedUpSnapshotUuid = null;
|
||||
// By default, assume failed.
|
||||
boolean backedUp = false;
|
||||
@ -621,6 +695,9 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
|
||||
if (backupSnapshotCommand.getSwift() != null ) {
|
||||
snapshot.setSwiftId(swift.getId());
|
||||
snapshot.setBackupSnapshotId(backedUpSnapshotUuid);
|
||||
} else if (backupSnapshotCommand.getS3() != null) {
|
||||
snapshot.setS3Id(s3.getId());
|
||||
snapshot.setBackupSnapshotId(backedUpSnapshotUuid);
|
||||
} else {
|
||||
snapshot.setSecHostId(secHost.getId());
|
||||
snapshot.setBackupSnapshotId(backedUpSnapshotUuid);
|
||||
@ -832,7 +909,13 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
|
||||
return true;
|
||||
}
|
||||
SwiftTO swift = _swiftMgr.getSwiftTO(snapshot.getSwiftId());
|
||||
DeleteSnapshotBackupCommand cmd = new DeleteSnapshotBackupCommand(swift, secondaryStoragePoolUrl, dcId, accountId, volumeId, backupOfSnapshot, false);
|
||||
S3TO s3 = _s3Mgr.getS3TO();
|
||||
|
||||
checkObjectStorageConfiguration(swift, s3);
|
||||
|
||||
DeleteSnapshotBackupCommand cmd = new DeleteSnapshotBackupCommand(
|
||||
swift, s3, secondaryStoragePoolUrl, dcId, accountId, volumeId,
|
||||
backupOfSnapshot, false);
|
||||
Answer answer = _agentMgr.sendToSSVM(dcId, cmd);
|
||||
|
||||
if ((answer != null) && answer.getResult()) {
|
||||
@ -979,9 +1062,15 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
|
||||
}
|
||||
List<HostVO> ssHosts = _ssvmMgr.listSecondaryStorageHostsInOneZone(dcId);
|
||||
SwiftTO swift = _swiftMgr.getSwiftTO();
|
||||
if (swift == null) {
|
||||
S3TO s3 = _s3Mgr.getS3TO();
|
||||
|
||||
checkObjectStorageConfiguration(swift, s3);
|
||||
|
||||
if (swift == null && s3 == null) {
|
||||
for (HostVO ssHost : ssHosts) {
|
||||
DeleteSnapshotBackupCommand cmd = new DeleteSnapshotBackupCommand(null, ssHost.getStorageUrl(), dcId, accountId, volumeId, "", true);
|
||||
DeleteSnapshotBackupCommand cmd = new DeleteSnapshotBackupCommand(
|
||||
null, null, ssHost.getStorageUrl(), dcId,
|
||||
accountId, volumeId, "", true);
|
||||
Answer answer = null;
|
||||
try {
|
||||
answer = _agentMgr.sendToSSVM(dcId, cmd);
|
||||
@ -998,12 +1087,14 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DeleteSnapshotBackupCommand cmd = new DeleteSnapshotBackupCommand(swift, "", dcId, accountId, volumeId, "", true);
|
||||
DeleteSnapshotBackupCommand cmd = new DeleteSnapshotBackupCommand(
|
||||
swift, s3, "", dcId, accountId, volumeId, "", true);
|
||||
Answer answer = null;
|
||||
try {
|
||||
answer = _agentMgr.sendToSSVM(dcId, cmd);
|
||||
} catch (Exception e) {
|
||||
s_logger.warn("Failed to delete all snapshot for volume " + volumeId + " on swift");
|
||||
final String storeType = s3 != null ? "S3" : "swift";
|
||||
s_logger.warn("Failed to delete all snapshot for volume " + volumeId + " on " + storeType);
|
||||
}
|
||||
if ((answer != null) && answer.getResult()) {
|
||||
s_logger.debug("Deleted all snapshots for volume: " + volumeId + " under account: " + accountId);
|
||||
|
||||
94
server/src/com/cloud/template/S3SyncTask.java
Normal file
94
server/src/com/cloud/template/S3SyncTask.java
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.template;
|
||||
|
||||
import static java.lang.String.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.agent.api.to.S3TO;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.storage.dao.VMTemplateDao;
|
||||
import com.cloud.storage.s3.S3Manager;
|
||||
|
||||
final class S3SyncTask implements Runnable {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(S3SyncTask.class);
|
||||
|
||||
private final VMTemplateDao vmTemplateDao;
|
||||
private final S3Manager s3Mgr;
|
||||
|
||||
S3SyncTask(final VMTemplateDao vmTemplateDao, final S3Manager s3Mgr) {
|
||||
|
||||
super();
|
||||
|
||||
assert vmTemplateDao != null;
|
||||
assert s3Mgr != null;
|
||||
|
||||
this.vmTemplateDao = vmTemplateDao;
|
||||
this.s3Mgr = s3Mgr;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
|
||||
final S3TO s3 = s3Mgr.getS3TO();
|
||||
|
||||
if (s3 == null) {
|
||||
LOGGER.warn("S3 sync skipped because no S3 instance is configured.");
|
||||
return;
|
||||
}
|
||||
|
||||
final List<VMTemplateVO> candidateTemplates = vmTemplateDao
|
||||
.findTemplatesToSyncToS3();
|
||||
|
||||
if (candidateTemplates.isEmpty()) {
|
||||
LOGGER.debug("All templates are synced with S3.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (VMTemplateVO candidateTemplate : candidateTemplates) {
|
||||
|
||||
if (LOGGER.isInfoEnabled()) {
|
||||
LOGGER.info(format(
|
||||
"Uploading template %1$s (id: %2$s) to S3.",
|
||||
candidateTemplate.getName(),
|
||||
candidateTemplate.getId()));
|
||||
}
|
||||
|
||||
s3Mgr.uploadTemplateToS3FromSecondaryStorage(candidateTemplate);
|
||||
|
||||
}
|
||||
|
||||
LOGGER.debug("Completed S3 template sync task.");
|
||||
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn(
|
||||
"S3 Sync Task ignored exception, and will continue to execute.",
|
||||
e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -113,10 +113,12 @@ import com.cloud.storage.dao.UploadDao;
|
||||
import com.cloud.storage.dao.VMTemplateDao;
|
||||
import com.cloud.storage.dao.VMTemplateHostDao;
|
||||
import com.cloud.storage.dao.VMTemplatePoolDao;
|
||||
import com.cloud.storage.dao.VMTemplateS3Dao;
|
||||
import com.cloud.storage.dao.VMTemplateSwiftDao;
|
||||
import com.cloud.storage.dao.VMTemplateZoneDao;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.storage.download.DownloadMonitor;
|
||||
import com.cloud.storage.s3.S3Manager;
|
||||
import com.cloud.storage.secondary.SecondaryStorageVmManager;
|
||||
import com.cloud.storage.swift.SwiftManager;
|
||||
import com.cloud.storage.upload.UploadMonitor;
|
||||
@ -179,8 +181,12 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
|
||||
@Inject
|
||||
SwiftManager _swiftMgr;
|
||||
@Inject
|
||||
S3Manager _s3Mgr;
|
||||
@Inject
|
||||
VMTemplateSwiftDao _tmpltSwiftDao;
|
||||
@Inject
|
||||
VMTemplateS3Dao _vmS3TemplateDao;
|
||||
@Inject
|
||||
ConfigurationDao _configDao;
|
||||
@Inject
|
||||
ClusterDao _clusterDao;
|
||||
@ -207,6 +213,7 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
|
||||
ExecutorService _preloadExecutor;
|
||||
ScheduledExecutorService _swiftTemplateSyncExecutor;
|
||||
|
||||
private ScheduledExecutorService _s3TemplateSyncExecutor = null;
|
||||
|
||||
@Inject (adapter=TemplateAdapter.class)
|
||||
protected Adapters<TemplateAdapter> _adapters;
|
||||
@ -344,10 +351,14 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
|
||||
}
|
||||
}
|
||||
|
||||
if (zoneId == null) {
|
||||
if (zoneId == null && _swiftMgr.isSwiftEnabled()) {
|
||||
zoneId = _swiftMgr.chooseZoneForTmpltExtract(templateId);
|
||||
}
|
||||
|
||||
if (zoneId == null && _s3Mgr.isS3Enabled()) {
|
||||
zoneId = _s3Mgr.chooseZoneForTemplateExtract(template);
|
||||
}
|
||||
|
||||
if (_dcDao.findById(zoneId) == null) {
|
||||
throw new IllegalArgumentException("Please specify a valid zone.");
|
||||
}
|
||||
@ -380,7 +391,13 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
|
||||
if (swift != null && sservers != null) {
|
||||
downloadTemplateFromSwiftToSecondaryStorage(zoneId, templateId);
|
||||
}
|
||||
} else if (tmpltHostRef == null && _s3Mgr.isS3Enabled()) {
|
||||
if (sservers != null) {
|
||||
_s3Mgr.downloadTemplateFromS3ToSecondaryStorage(zoneId,
|
||||
templateId, _primaryStorageDownloadWait);
|
||||
}
|
||||
}
|
||||
|
||||
if (tmpltHostRef == null) {
|
||||
throw new InvalidParameterValueException("The " + desc + " has not been downloaded ");
|
||||
}
|
||||
@ -594,6 +611,12 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
|
||||
s_logger.error("Unable to find a secondary storage host who has completely downloaded the template.");
|
||||
return null;
|
||||
}
|
||||
result = _s3Mgr.downloadTemplateFromS3ToSecondaryStorage(dcId,
|
||||
templateId, _primaryStorageDownloadWait);
|
||||
if (result != null) {
|
||||
s_logger.error("Unable to find a secondary storage host who has completely downloaded the template.");
|
||||
return null;
|
||||
}
|
||||
templateHostRef = _storageMgr.findVmTemplateHost(templateId, pool);
|
||||
if (templateHostRef == null || templateHostRef.getDownloadState() != Status.DOWNLOADED) {
|
||||
s_logger.error("Unable to find a secondary storage host who has completely downloaded the template.");
|
||||
@ -708,6 +731,12 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
|
||||
s_logger.error("Unable to find a secondary storage host who has completely downloaded the template.");
|
||||
return null;
|
||||
}
|
||||
result = _s3Mgr.downloadTemplateFromS3ToSecondaryStorage(dcId,
|
||||
templateId, _primaryStorageDownloadWait);
|
||||
if (result != null) {
|
||||
s_logger.error("Unable to find a secondary storage host who has completely downloaded the template.");
|
||||
return null;
|
||||
}
|
||||
templateHostRef = _storageMgr.findVmTemplateHost(templateId, pool);
|
||||
if (templateHostRef == null || templateHostRef.getDownloadState() != Status.DOWNLOADED) {
|
||||
s_logger.error("Unable to find a secondary storage host who has completely downloaded the template.");
|
||||
@ -823,6 +852,12 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
|
||||
if (_swiftMgr.isSwiftEnabled()) {
|
||||
throw new CloudRuntimeException("copytemplate API is disabled in Swift setup, templates in Swift can be accessed by all Zones");
|
||||
}
|
||||
|
||||
if (_s3Mgr.isS3Enabled()) {
|
||||
throw new CloudRuntimeException(
|
||||
"copytemplate API is disabled in S3 setup -- S3 templates are accessible in all zones.");
|
||||
}
|
||||
|
||||
//Verify parameters
|
||||
if (sourceZoneId == destZoneId) {
|
||||
throw new InvalidParameterValueException("Please specify different source and destination zones.");
|
||||
@ -1003,12 +1038,32 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
|
||||
@Override
|
||||
public boolean start() {
|
||||
_swiftTemplateSyncExecutor.scheduleAtFixedRate(getSwiftTemplateSyncTask(), 60, 60, TimeUnit.SECONDS);
|
||||
|
||||
if (_s3TemplateSyncExecutor != null) {
|
||||
|
||||
final int initialDelay = 60;
|
||||
final int period = 60;
|
||||
|
||||
_s3TemplateSyncExecutor.scheduleAtFixedRate(new S3SyncTask(
|
||||
this._tmpltDao, this._s3Mgr), initialDelay, period,
|
||||
TimeUnit.SECONDS);
|
||||
s_logger.info(String.format("Started S3 sync task to execute "
|
||||
+ "execute every %1$s after an initial delay of %2$s.",
|
||||
period, initialDelay));
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stop() {
|
||||
_swiftTemplateSyncExecutor.shutdownNow();
|
||||
|
||||
if (_s3TemplateSyncExecutor != null) {
|
||||
_s3TemplateSyncExecutor.shutdownNow();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1041,7 +1096,16 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
|
||||
_storagePoolMaxWaitSeconds = NumbersUtil.parseInt(_configDao.getValue(Config.StoragePoolMaxWaitSeconds.key()), 3600);
|
||||
_preloadExecutor = Executors.newFixedThreadPool(8, new NamedThreadFactory("Template-Preloader"));
|
||||
_swiftTemplateSyncExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("swift-template-sync-Executor"));
|
||||
return false;
|
||||
|
||||
if (_s3Mgr.isS3Enabled()) {
|
||||
_s3TemplateSyncExecutor = Executors
|
||||
.newSingleThreadScheduledExecutor(new NamedThreadFactory(
|
||||
"s3-template-sync"));
|
||||
} else {
|
||||
s_logger.info("S3 secondary storage synchronization is disabled.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected TemplateManagerImpl() {
|
||||
@ -1195,13 +1259,19 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
|
||||
if (cmd.getZoneId() == null && _swiftMgr.isSwiftEnabled()) {
|
||||
_swiftMgr.deleteTemplate(cmd);
|
||||
}
|
||||
if (cmd.getZoneId() == null && _s3Mgr.isS3Enabled()) {
|
||||
_s3Mgr.deleteTemplate(cmd.getId(), caller.getAccountId());
|
||||
}
|
||||
|
||||
TemplateAdapter adapter = getAdapter(template.getHypervisorType());
|
||||
TemplateProfile profile = adapter.prepareDelete(cmd);
|
||||
boolean result = adapter.delete(profile);
|
||||
|
||||
if (result){
|
||||
if (cmd.getZoneId() == null && _swiftMgr.isSwiftEnabled()) {
|
||||
List<VMTemplateZoneVO> templateZones = _tmpltZoneDao.listByZoneTemplate(null, templateId);
|
||||
if (cmd.getZoneId() == null
|
||||
&& (_swiftMgr.isSwiftEnabled() || _s3Mgr.isS3Enabled())) {
|
||||
List<VMTemplateZoneVO> templateZones = _tmpltZoneDao
|
||||
.listByZoneTemplate(null, templateId);
|
||||
if (templateZones != null) {
|
||||
for (VMTemplateZoneVO templateZone : templateZones) {
|
||||
_tmpltZoneDao.remove(templateZone.getId());
|
||||
@ -1234,6 +1304,10 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
|
||||
if (cmd.getZoneId() == null && _swiftMgr.isSwiftEnabled()) {
|
||||
_swiftMgr.deleteIso(cmd);
|
||||
}
|
||||
if (cmd.getZoneId() == null && _s3Mgr.isS3Enabled()) {
|
||||
_s3Mgr.deleteTemplate(caller.getAccountId(), templateId);
|
||||
}
|
||||
|
||||
if (zoneId != null && (_ssvmMgr.findSecondaryStorageHost(zoneId) == null)) {
|
||||
throw new InvalidParameterValueException("Failed to find a secondary storage host in the specified zone.");
|
||||
}
|
||||
@ -1241,8 +1315,10 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
|
||||
TemplateProfile profile = adapter.prepareDelete(cmd);
|
||||
boolean result = adapter.delete(profile);
|
||||
if (result) {
|
||||
if (cmd.getZoneId() == null && _swiftMgr.isSwiftEnabled()) {
|
||||
List<VMTemplateZoneVO> templateZones = _tmpltZoneDao.listByZoneTemplate(null, templateId);
|
||||
if (cmd.getZoneId() == null
|
||||
&& (_swiftMgr.isSwiftEnabled() || _s3Mgr.isS3Enabled())) {
|
||||
List<VMTemplateZoneVO> templateZones = _tmpltZoneDao
|
||||
.listByZoneTemplate(null, templateId);
|
||||
if (templateZones != null) {
|
||||
for (VMTemplateZoneVO templateZone : templateZones) {
|
||||
_tmpltZoneDao.remove(templateZone.getId());
|
||||
|
||||
@ -146,6 +146,8 @@ DROP TABLE IF EXISTS `cloud`.`s2s_vpn_gateway`;
|
||||
DROP TABLE IF EXISTS `cloud`.`s2s_vpn_connection`;
|
||||
DROP TABLE IF EXISTS `cloud`,`external_nicira_nvp_devices`;
|
||||
DROP TABLE IF EXISTS `cloud`,`nicira_nvp_nic_map`;
|
||||
DROP TABLE IF EXISTS `cloud`,`s3`;
|
||||
DROP TABLE IF EXISTS `cloud`,`template_s3_ref`;
|
||||
DROP TABLE IF EXISTS `cloud`,`nicira_nvp_router_map`;
|
||||
DROP TABLE IF EXISTS `cloud`.`autoscale_vmgroup_policy_map`;
|
||||
DROP TABLE IF EXISTS `cloud`.`autoscale_policy_condition_map`;
|
||||
@ -164,7 +166,7 @@ CREATE TABLE `cloud`.`version` (
|
||||
INDEX `i_version__version`(`version`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
INSERT INTO `version` (`version`, `updated`, `step`) VALUES('@VERSION@', now(), 'Complete');
|
||||
INSERT INTO `version` (`version`, `updated`, `step`) VALUES('4.0.0.2012-09-12T14:47:37Z', now(), 'Complete');
|
||||
|
||||
CREATE TABLE `cloud`.`op_it_work` (
|
||||
`id` char(40) COMMENT 'reservation id',
|
||||
@ -480,12 +482,14 @@ CREATE TABLE `cloud`.`snapshots` (
|
||||
`removed` datetime COMMENT 'Date removed. not null if removed',
|
||||
`backup_snap_id` varchar(255) COMMENT 'Back up uuid of the snapshot',
|
||||
`swift_id` bigint unsigned COMMENT 'which swift',
|
||||
`s3_id` bigint unsigned COMMENT 'S3 to which this snapshot will be stored',
|
||||
`sechost_id` bigint unsigned COMMENT 'secondary storage host id',
|
||||
`prev_snap_id` bigint unsigned COMMENT 'Id of the most recent snapshot',
|
||||
`hypervisor_type` varchar(32) NOT NULL COMMENT 'hypervisor that the snapshot was taken under',
|
||||
`version` varchar(32) COMMENT 'snapshot version',
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `uc_snapshots__uuid` UNIQUE (`uuid`),
|
||||
CONSTRAINT `fk_snapshots__s3_id` FOREIGN KEY `fk_snapshots__s3_id` (`s3_id`) REFERENCES `s3` (`id`),
|
||||
INDEX `i_snapshots__removed`(`removed`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
@ -1850,6 +1854,36 @@ CREATE TABLE `cloud`.`swift` (
|
||||
CONSTRAINT `uc_swift__uuid` UNIQUE (`uuid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE `cloud`.`s3` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||
`uuid` varchar(40),
|
||||
`access_key` varchar(20) NOT NULL COMMENT ' The S3 access key',
|
||||
`secret_key` varchar(40) NOT NULL COMMENT ' The S3 secret key',
|
||||
`end_point` varchar(1024) COMMENT ' The S3 host',
|
||||
`bucket` varchar(63) NOT NULL COMMENT ' The S3 host',
|
||||
`https` tinyint unsigned DEFAULT NULL COMMENT ' Flag indicating whether or not to connect over HTTPS',
|
||||
`connection_timeout` integer COMMENT ' The amount of time to wait (in milliseconds) when initially establishing a connection before giving up and timing out.',
|
||||
`max_error_retry` integer COMMENT ' The maximum number of retry attempts for failed retryable requests (ex: 5xx error responses from services).',
|
||||
`socket_timeout` integer COMMENT ' The amount of time to wait (in milliseconds) for data to be transfered over an established, open connection before the connection times out and is closed.',
|
||||
`created` datetime COMMENT 'date the s3 first signed on',
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `uc_s3__uuid` UNIQUE (`uuid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE `cloud`.`template_s3_ref` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||
`s3_id` bigint unsigned NOT NULL COMMENT ' Associated S3 instance id',
|
||||
`template_id` bigint unsigned NOT NULL COMMENT ' Associated template id',
|
||||
`created` DATETIME NOT NULL COMMENT ' The creation timestamp',
|
||||
`size` bigint unsigned COMMENT ' The size of the object',
|
||||
`physical_size` bigint unsigned DEFAULT 0 COMMENT ' The physical size of the object',
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `uc_template_s3_ref__template_id` UNIQUE (`template_id`),
|
||||
CONSTRAINT `fk_template_s3_ref__s3_id` FOREIGN KEY `fk_template_s3_ref__s3_id` (`s3_id`) REFERENCES `s3` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_template_s3_ref__template_id` FOREIGN KEY `fk_template_s3_ref__template_id` (`template_id`) REFERENCES `vm_template` (`id`),
|
||||
INDEX `i_template_s3_ref__s3_id`(`s3_id`),
|
||||
INDEX `i_template_s3_ref__template_id`(`template_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE `cloud`.`op_host_transfer` (
|
||||
`id` bigint unsigned UNIQUE NOT NULL COMMENT 'Id of the host',
|
||||
|
||||
58
setup/db/db/schema-40to410.sql
Normal file
58
setup/db/db/schema-40to410.sql
Normal file
@ -0,0 +1,58 @@
|
||||
-- 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.
|
||||
|
||||
--;
|
||||
-- Schema upgrade from 4.0.0 to 4.1.0;
|
||||
--;
|
||||
|
||||
CREATE TABLE `cloud`.`s3` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||
`uuid` varchar(40),
|
||||
`access_key` varchar(20) NOT NULL COMMENT ' The S3 access key',
|
||||
`secret_key` varchar(40) NOT NULL COMMENT ' The S3 secret key',
|
||||
`end_point` varchar(1024) COMMENT ' The S3 host',
|
||||
`bucket` varchar(63) NOT NULL COMMENT ' The S3 host',
|
||||
`https` tinyint unsigned DEFAULT NULL COMMENT ' Flag indicating whether or not to connect over HTTPS',
|
||||
`connection_timeout` integer COMMENT ' The amount of time to wait (in milliseconds) when initially establishing a connection before giving up and timing out.',
|
||||
`max_error_retry` integer COMMENT ' The maximum number of retry attempts for failed retryable requests (ex: 5xx error responses from services).',
|
||||
`socket_timeout` integer COMMENT ' The amount of time to wait (in milliseconds) for data to be transfered over an established, open connection before the connection times out and is closed.',
|
||||
`created` datetime COMMENT 'date the s3 first signed on',
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `uc_s3__uuid` UNIQUE (`uuid`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE `cloud`.`template_s3_ref` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
|
||||
`s3_id` bigint unsigned NOT NULL COMMENT ' Associated S3 instance id',
|
||||
`template_id` bigint unsigned NOT NULL COMMENT ' Associated template id',
|
||||
`created` DATETIME NOT NULL COMMENT ' The creation timestamp',
|
||||
`size` bigint unsigned COMMENT ' The size of the object',
|
||||
`physical_size` bigint unsigned DEFAULT 0 COMMENT ' The physical size of the object',
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `uc_template_s3_ref__template_id` UNIQUE (`template_id`),
|
||||
CONSTRAINT `fk_template_s3_ref__s3_id` FOREIGN KEY `fk_template_s3_ref__s3_id` (`s3_id`) REFERENCES `s3` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_template_s3_ref__template_id` FOREIGN KEY `fk_template_s3_ref__template_id` (`template_id`) REFERENCES `vm_template` (`id`),
|
||||
INDEX `i_template_s3_ref__swift_id`(`s3_id`),
|
||||
INDEX `i_template_s3_ref__template_id`(`template_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 's3.enable', 'false', 'enable s3');
|
||||
|
||||
ALTER TABLE `cloud`.`snapshots` ADD COLUMN `s3_id` bigint unsigned COMMENT 'S3 to which this snapshot will be stored';
|
||||
|
||||
ALTER TABLE `cloud`.`snapshots` ADD CONSTRAINT `fk_snapshots__s3_id` FOREIGN KEY `fk_snapshots__s3_id` (`s3_id`) REFERENCES `s3` (`id`);
|
||||
|
||||
@ -116,6 +116,7 @@ known_categories = {
|
||||
'LB': 'Load Balancer',
|
||||
'ldap': 'LDAP',
|
||||
'Swift': 'Swift',
|
||||
'S3' : 'S3',
|
||||
'SecondaryStorage': 'Host',
|
||||
'Project': 'Project',
|
||||
'Lun': 'Storage',
|
||||
|
||||
@ -42,7 +42,7 @@ class cloudConnection(object):
|
||||
else:
|
||||
self.protocol=protocol
|
||||
self.path = path
|
||||
if port == 8096:
|
||||
if port == 8096 or (self.apiKey == None and self.securityKey == None):
|
||||
self.auth = False
|
||||
else:
|
||||
self.auth = True
|
||||
|
||||
@ -399,9 +399,9 @@ class deployDataCenters():
|
||||
logging=self.testClientLogger)
|
||||
|
||||
"""config database"""
|
||||
dbSvr = self.config.dbSvr
|
||||
self.testClient.dbConfigure(dbSvr.dbSvr, dbSvr.port, dbSvr.user, \
|
||||
dbSvr.passwd, dbSvr.db)
|
||||
#dbSvr = self.config.dbSvr
|
||||
#self.testClient.dbConfigure(dbSvr.dbSvr, dbSvr.port, dbSvr.user, \
|
||||
# dbSvr.passwd, dbSvr.db)
|
||||
self.apiClient = self.testClient.getApiClient()
|
||||
|
||||
def updateConfiguration(self, globalCfg):
|
||||
|
||||
@ -25,6 +25,17 @@ under the License.
|
||||
<% long now = System.currentTimeMillis(); %>
|
||||
<script language="javascript">
|
||||
dictionary = {
|
||||
'label.enable.s3': '<fmt:message key="label.enable.s3"/>',
|
||||
'confirm.enable.s3': '<fmt:message key="confirm.enable.s3"/>',
|
||||
'message.after.enable.s3': '<fmt:message key="message.after.enable.s3"/>',
|
||||
'label.s3.access_key': '<fmt:message key="label.s3.access_key"/>',
|
||||
'label.s3.secret_key': '<fmt:message key="label.s3.secret_key"/>',
|
||||
'label.s3.bucket': '<fmt:message key="label.s3.bucket"/>',
|
||||
'label.s3.endpoint': '<fmt:message key="label.s3.endpoint"/>',
|
||||
'label.s3.use_https': '<fmt:message key="label.s3.use_https"/>',
|
||||
'label.s3.connection_timeout': '<fmt:message key="label.s3.connection_timeout"/>',
|
||||
'label.s3.max_error_retry': '<fmt:message key="label.s3.max_error_retry"/>',
|
||||
'label.s3.socket_timeout': '<fmt:message key="label.s3.socket_timeout"/>',
|
||||
'label.egress.rules': '<fmt:message key="label.egress.rules"/>',
|
||||
'message.acquire.new.ip.vpc': '<fmt:message key="message.acquire.new.ip.vpc"/>',
|
||||
'label.quickview': '<fmt:message key="label.quickview"/>',
|
||||
|
||||
@ -178,8 +178,22 @@
|
||||
havingSwift = true;
|
||||
}
|
||||
});
|
||||
if (havingSwift == false) {
|
||||
$.ajax({
|
||||
url: createURL("listS3s"),
|
||||
dataType: "json",
|
||||
async: false,
|
||||
success: function(json) {
|
||||
var items = json.lists3sresponse.s3;
|
||||
if (items != null && items.length > 0) {
|
||||
havingS3 = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
havingSwift = false;
|
||||
havingS3 = false;
|
||||
}
|
||||
|
||||
return userValid ? {
|
||||
@ -296,8 +310,22 @@
|
||||
havingSwift = true;
|
||||
}
|
||||
});
|
||||
if (havingSwift = false) {
|
||||
$.ajax({
|
||||
url: createURL("listS3s"),
|
||||
dataType: "json",
|
||||
async: false,
|
||||
success: function(json) {
|
||||
var items = json.lists3sresponse.s3;
|
||||
if (items != null && items.length > 0) {
|
||||
havingS3 = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
havingSwift = false;
|
||||
havingS3 = false;
|
||||
}
|
||||
|
||||
// Get project configuration
|
||||
|
||||
@ -45,6 +45,7 @@ var pageSize = 20;
|
||||
|
||||
var rootAccountId = 1;
|
||||
var havingSwift = false;
|
||||
var havingS3 = false;
|
||||
|
||||
//async action
|
||||
var pollAsyncJobResult = function(args) {
|
||||
|
||||
@ -4249,6 +4249,84 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
enableS3: {
|
||||
label: 'label.enable.s3',
|
||||
isHeader: true,
|
||||
addRow: false,
|
||||
|
||||
preFilter: function(args) {
|
||||
var s3Enabled = false;
|
||||
$.ajax({
|
||||
url: createURL('listConfigurations'),
|
||||
data: {
|
||||
name: 's3.enable'
|
||||
},
|
||||
async: false,
|
||||
success: function(json) {
|
||||
s3Enabled = json.listconfigurationsresponse.configuration[0].value == 'true' && !havingS3 ?
|
||||
true : false;
|
||||
},
|
||||
error: function(json) {
|
||||
cloudStack.dialog.notice({ message: parseXMLHttpResponse(json) });
|
||||
}
|
||||
});
|
||||
|
||||
return s3Enabled;
|
||||
},
|
||||
|
||||
messages: {
|
||||
notification: function(args) {
|
||||
return 'label.enable.s3';
|
||||
}
|
||||
},
|
||||
|
||||
createForm: {
|
||||
desc: 'confirm.enable.s3',
|
||||
fields: {
|
||||
accesskey: { label: 'label.s3.access_key', validation: { required: true } },
|
||||
secretkey: { label: 'label.s3.secret_key', validation: { required: true} },
|
||||
bucket: { label: 'label.s3.bucket', validation: { required: true} },
|
||||
endpoint: { label: 'label.s3.endpoint' },
|
||||
usehttps: {
|
||||
label: 'label.s3.use_https',
|
||||
isEditable: true,
|
||||
isBoolean: true,
|
||||
isChecked: true,
|
||||
converter:cloudStack.converters.toBooleanText
|
||||
},
|
||||
connectiontimeout: { label: 'label.s3.connection_timeout' },
|
||||
maxerrorretry: { label: 'label.s3.max_error_retry' },
|
||||
sockettimeout: { label: 'label.s3.socket_timeout' }
|
||||
}
|
||||
},
|
||||
action: function(args) {
|
||||
$.ajax({
|
||||
url: createURL('addS3'),
|
||||
data: {
|
||||
accesskey: args.data.accesskey,
|
||||
secretkey: args.data.secretkey,
|
||||
bucket: args.data.bucket,
|
||||
endpoint: args.data.endpoint,
|
||||
usehttps: (args.data.usehttps != null && args.data.usehttps == 'on' ? 'true' : 'false'),
|
||||
connectiontimeout: args.data.connectiontimeout,
|
||||
maxerrorretry: args.data.maxerrorretry,
|
||||
sockettimeout: args.data.sockettimeout
|
||||
},
|
||||
success: function(json) {
|
||||
havingS3 = true;
|
||||
args.response.success();
|
||||
|
||||
cloudStack.dialog.notice({
|
||||
message: 'message.after.enable.s3'
|
||||
});
|
||||
},
|
||||
error: function(json) {
|
||||
args.response.error(parseXMLHttpResponse(json));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -1420,7 +1420,7 @@
|
||||
else {
|
||||
allowedActions.push("edit");
|
||||
|
||||
if(havingSwift == false)
|
||||
if(havingSwift == false && havingS3 == false)
|
||||
allowedActions.push("copyTemplate");
|
||||
|
||||
//allowedActions.push("createVm"); // For Beta2, this simply doesn't work without a network.
|
||||
|
||||
@ -102,6 +102,11 @@
|
||||
<artifactId>trilead-ssh2</artifactId>
|
||||
<version>${cs.trilead.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.amazonaws</groupId>
|
||||
<artifactId>aws-java-sdk</artifactId>
|
||||
<version>${cs.aws.sdk.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>apache-log4j-extras</artifactId>
|
||||
@ -128,7 +133,13 @@
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>${cs.commons-io.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<defaultGoal>install</defaultGoal>
|
||||
|
||||
@ -82,6 +82,10 @@ public class DateUtil {
|
||||
return formattedString;
|
||||
}
|
||||
|
||||
public static Date now() {
|
||||
return new Date(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public enum IntervalType {
|
||||
HOURLY,
|
||||
DAILY,
|
||||
|
||||
495
utils/src/com/cloud/utils/S3Utils.java
Normal file
495
utils/src/com/cloud/utils/S3Utils.java
Normal file
@ -0,0 +1,495 @@
|
||||
/*
|
||||
* 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 static com.amazonaws.Protocol.HTTP;
|
||||
import static com.amazonaws.Protocol.HTTPS;
|
||||
import static com.cloud.utils.StringUtils.join;
|
||||
import static java.io.File.createTempFile;
|
||||
import static java.lang.String.format;
|
||||
import static java.lang.System.currentTimeMillis;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Collections.unmodifiableList;
|
||||
import static org.apache.commons.lang.ArrayUtils.isEmpty;
|
||||
import static org.apache.commons.lang.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang.StringUtils.isNotBlank;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.amazonaws.AmazonClientException;
|
||||
import com.amazonaws.ClientConfiguration;
|
||||
import com.amazonaws.auth.AWSCredentials;
|
||||
import com.amazonaws.auth.BasicAWSCredentials;
|
||||
import com.amazonaws.services.s3.AmazonS3;
|
||||
import com.amazonaws.services.s3.AmazonS3Client;
|
||||
import com.amazonaws.services.s3.model.Bucket;
|
||||
import com.amazonaws.services.s3.model.GetObjectRequest;
|
||||
import com.amazonaws.services.s3.model.ObjectMetadata;
|
||||
import com.amazonaws.services.s3.model.S3ObjectSummary;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
|
||||
public final class S3Utils {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(S3Utils.class);
|
||||
|
||||
public static final String SEPARATOR = "/";
|
||||
|
||||
private static final int MIN_BUCKET_NAME_LENGTH = 3;
|
||||
private static final int MAX_BUCKET_NAME_LENGTH = 63;
|
||||
|
||||
private S3Utils() {
|
||||
super();
|
||||
}
|
||||
|
||||
private static AmazonS3 acquireClient(final ClientOptions clientOptions) {
|
||||
|
||||
final AWSCredentials credentials = new BasicAWSCredentials(
|
||||
clientOptions.getAccessKey(), clientOptions.getSecretKey());
|
||||
|
||||
final ClientConfiguration configuration = new ClientConfiguration();
|
||||
|
||||
if (clientOptions.isHttps() != null) {
|
||||
configuration.setProtocol(clientOptions.isHttps() == true ? HTTPS
|
||||
: HTTP);
|
||||
}
|
||||
|
||||
if (clientOptions.getConnectionTimeout() != null) {
|
||||
configuration.setConnectionTimeout(clientOptions
|
||||
.getConnectionTimeout());
|
||||
}
|
||||
|
||||
if (clientOptions.getMaxErrorRetry() != null) {
|
||||
configuration.setMaxErrorRetry(clientOptions.getMaxErrorRetry());
|
||||
}
|
||||
|
||||
if (clientOptions.getSocketTimeout() != null) {
|
||||
configuration.setSocketTimeout(clientOptions.getSocketTimeout());
|
||||
}
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(format(
|
||||
"Creating S3 client with configuration: [protocol: %1$s, connectionTimeOut: "
|
||||
+ "%2$s, maxErrorRetry: %3$s, socketTimeout: %4$s]",
|
||||
configuration.getProtocol(),
|
||||
configuration.getConnectionTimeout(),
|
||||
configuration.getMaxErrorRetry(),
|
||||
configuration.getSocketTimeout()));
|
||||
}
|
||||
|
||||
final AmazonS3Client client = new AmazonS3Client(credentials,
|
||||
configuration);
|
||||
|
||||
if (isNotBlank(clientOptions.getEndPoint())) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(format(
|
||||
"Setting the end point for S3 client %1$s to %2$s.",
|
||||
client, clientOptions.getEndPoint()));
|
||||
}
|
||||
client.setEndpoint(clientOptions.getEndPoint());
|
||||
}
|
||||
|
||||
return client;
|
||||
|
||||
}
|
||||
|
||||
public static void putFile(final ClientOptions clientOptions,
|
||||
final File sourceFile, final String bucketName, final String key) {
|
||||
|
||||
assert clientOptions != null;
|
||||
assert sourceFile != null;
|
||||
assert !isBlank(bucketName);
|
||||
assert !isBlank(key);
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(format("Sending file %1$s as S3 object %2$s in "
|
||||
+ "bucket %3$s", sourceFile.getName(), key, bucketName));
|
||||
}
|
||||
|
||||
acquireClient(clientOptions).putObject(bucketName, key, sourceFile);
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static File getFile(final ClientOptions clientOptions,
|
||||
final String bucketName, final String key,
|
||||
final File targetDirectory, final FileNamingStrategy namingStrategy) {
|
||||
|
||||
assert clientOptions != null;
|
||||
assert isNotBlank(bucketName);
|
||||
assert isNotBlank(key);
|
||||
assert targetDirectory != null && targetDirectory.isDirectory();
|
||||
assert namingStrategy != null;
|
||||
|
||||
final AmazonS3 connection = acquireClient(clientOptions);
|
||||
|
||||
File tempFile = null;
|
||||
try {
|
||||
|
||||
tempFile = createTempFile(
|
||||
join(asList(targetDirectory.getName(), currentTimeMillis(),
|
||||
"part"), "-"), "tmp", targetDirectory);
|
||||
tempFile.deleteOnExit();
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(format(
|
||||
"Downloading object %1$s from bucket %2$s to temp file %3$s",
|
||||
key, bucketName, tempFile.getName()));
|
||||
}
|
||||
|
||||
connection.getObject(new GetObjectRequest(bucketName, key),
|
||||
tempFile);
|
||||
|
||||
final File targetFile = new File(targetDirectory,
|
||||
namingStrategy.determineFileName(key));
|
||||
tempFile.renameTo(targetFile);
|
||||
|
||||
return targetFile;
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
|
||||
throw new CloudRuntimeException(
|
||||
format("Failed open file %1$s in order to get object %2$s from bucket %3$s.",
|
||||
targetDirectory.getAbsoluteFile(), bucketName, key),
|
||||
e);
|
||||
|
||||
} catch (IOException e) {
|
||||
|
||||
throw new CloudRuntimeException(
|
||||
format("Unable to allocate temporary file in directory %1$s to download %2$s:%3$s from S3",
|
||||
targetDirectory.getAbsolutePath(), bucketName, key),
|
||||
e);
|
||||
|
||||
} finally {
|
||||
|
||||
if (tempFile != null) {
|
||||
tempFile.delete();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static List<File> getDirectory(final ClientOptions clientOptions,
|
||||
final String bucketName, final String sourcePath,
|
||||
final File targetDirectory, final FileNamingStrategy namingStrategy) {
|
||||
|
||||
assert clientOptions != null;
|
||||
assert isNotBlank(bucketName);
|
||||
assert isNotBlank(sourcePath);
|
||||
assert targetDirectory != null;
|
||||
|
||||
final AmazonS3 connection = acquireClient(clientOptions);
|
||||
|
||||
// List the objects in the source directory on S3
|
||||
final List<S3ObjectSummary> objectSummaries = listDirectory(bucketName,
|
||||
sourcePath, connection);
|
||||
final List<File> files = new ArrayList<File>();
|
||||
|
||||
for (final S3ObjectSummary objectSummary : objectSummaries) {
|
||||
|
||||
files.add(getFile(clientOptions, bucketName,
|
||||
objectSummary.getKey(), targetDirectory, namingStrategy));
|
||||
|
||||
}
|
||||
|
||||
return unmodifiableList(files);
|
||||
|
||||
}
|
||||
|
||||
private static List<S3ObjectSummary> listDirectory(final String bucketName,
|
||||
final String directory, final AmazonS3 client) {
|
||||
|
||||
final List<S3ObjectSummary> objects = client.listObjects(bucketName,
|
||||
directory + SEPARATOR).getObjectSummaries();
|
||||
|
||||
if (objects == null) {
|
||||
return emptyList();
|
||||
}
|
||||
|
||||
return unmodifiableList(objects);
|
||||
|
||||
}
|
||||
|
||||
public static void putDirectory(final ClientOptions clientOptions,
|
||||
final String bucketName, final File directory,
|
||||
final FilenameFilter fileNameFilter,
|
||||
final ObjectNamingStrategy namingStrategy) {
|
||||
|
||||
assert clientOptions != null;
|
||||
assert isNotBlank(bucketName);
|
||||
assert directory != null && directory.isDirectory();
|
||||
assert fileNameFilter != null;
|
||||
assert namingStrategy != null;
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(format("Putting directory %1$s in S3 bucket %2$s.",
|
||||
directory.getAbsolutePath(), bucketName));
|
||||
}
|
||||
|
||||
// Determine the list of files to be sent using the passed filter ...
|
||||
final File[] files = directory.listFiles(fileNameFilter);
|
||||
|
||||
if (LOGGER.isTraceEnabled()) {
|
||||
LOGGER.trace(format("Putting files (%1$s) in S3 bucket %2$s.",
|
||||
ArrayUtils.toString(files, "no files found"), bucketName));
|
||||
}
|
||||
|
||||
// Skip spinning up an S3 connection when no files will be sent ...
|
||||
if (isEmpty(files)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final AmazonS3 client = acquireClient(clientOptions);
|
||||
|
||||
// Send the files to S3 using the passed ObjectNaming strategy to
|
||||
// determine the key ...
|
||||
for (final File file : files) {
|
||||
final String key = namingStrategy.determineKey(file);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(format(
|
||||
"Putting file %1$s into bucket %2$s with key %3$s.",
|
||||
file.getAbsolutePath(), bucketName, key));
|
||||
}
|
||||
client.putObject(bucketName, key, file);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void deleteObject(final ClientOptions clientOptions,
|
||||
final String bucketName, final String key) {
|
||||
|
||||
assert clientOptions != null;
|
||||
assert isNotBlank(bucketName);
|
||||
assert isNotBlank(key);
|
||||
|
||||
final AmazonS3 client = acquireClient(clientOptions);
|
||||
|
||||
client.deleteObject(bucketName, key);
|
||||
|
||||
}
|
||||
|
||||
public static void deleteDirectory(final ClientOptions clientOptions,
|
||||
final String bucketName, final String directoryName) {
|
||||
|
||||
assert clientOptions != null;
|
||||
assert isNotBlank(bucketName);
|
||||
assert isNotBlank(directoryName);
|
||||
|
||||
final AmazonS3 client = acquireClient(clientOptions);
|
||||
|
||||
final List<S3ObjectSummary> objects = listDirectory(bucketName,
|
||||
directoryName, client);
|
||||
|
||||
for (final S3ObjectSummary object : objects) {
|
||||
|
||||
client.deleteObject(bucketName, object.getKey());
|
||||
|
||||
}
|
||||
|
||||
client.deleteObject(bucketName, directoryName);
|
||||
|
||||
}
|
||||
|
||||
public static boolean canConnect(final ClientOptions clientOptions) {
|
||||
|
||||
try {
|
||||
|
||||
acquireClient(clientOptions);
|
||||
return true;
|
||||
|
||||
} catch (AmazonClientException e) {
|
||||
|
||||
LOGGER.warn("Ignored Exception while checking connection options",
|
||||
e);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static boolean doesBucketExist(final ClientOptions clientOptions,
|
||||
final String bucketName) {
|
||||
|
||||
assert clientOptions != null;
|
||||
assert !isBlank(bucketName);
|
||||
|
||||
try {
|
||||
|
||||
final List<Bucket> buckets = acquireClient(clientOptions)
|
||||
.listBuckets();
|
||||
|
||||
for (Bucket bucket : buckets) {
|
||||
if (bucket.getName().equals(bucketName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
} catch (AmazonClientException e) {
|
||||
|
||||
LOGGER.warn("Ignored Exception while checking bucket existence", e);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static boolean canReadWriteBucket(final ClientOptions clientOptions,
|
||||
final String bucketName) {
|
||||
|
||||
assert clientOptions != null;
|
||||
assert isNotBlank(bucketName);
|
||||
|
||||
try {
|
||||
|
||||
final AmazonS3 client = acquireClient(clientOptions);
|
||||
|
||||
final String fileContent = "testing put and delete";
|
||||
final InputStream inputStream = new ByteArrayInputStream(
|
||||
fileContent.getBytes());
|
||||
final String key = UUID.randomUUID().toString() + ".txt";
|
||||
|
||||
final ObjectMetadata metadata = new ObjectMetadata();
|
||||
metadata.setContentLength(fileContent.length());
|
||||
|
||||
client.putObject(bucketName, key, inputStream, metadata);
|
||||
client.deleteObject(bucketName, key);
|
||||
|
||||
return true;
|
||||
|
||||
} catch (AmazonClientException e) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static List<String> checkClientOptions(ClientOptions clientOptions) {
|
||||
|
||||
assert clientOptions != null;
|
||||
|
||||
List<String> errorMessages = new ArrayList<String>();
|
||||
|
||||
errorMessages.addAll(checkRequiredField("access key",
|
||||
clientOptions.getAccessKey()));
|
||||
errorMessages.addAll(checkRequiredField("secret key",
|
||||
clientOptions.getSecretKey()));
|
||||
|
||||
errorMessages.addAll(checkOptionalField("connection timeout",
|
||||
clientOptions.getConnectionTimeout()));
|
||||
errorMessages.addAll(checkOptionalField("socket timeout",
|
||||
clientOptions.getSocketTimeout()));
|
||||
errorMessages.addAll(checkOptionalField("max error retries",
|
||||
clientOptions.getMaxErrorRetry()));
|
||||
|
||||
return unmodifiableList(errorMessages);
|
||||
|
||||
}
|
||||
|
||||
public static List<String> checkBucketName(final String bucketLabel,
|
||||
final String bucket) {
|
||||
|
||||
assert isNotBlank(bucketLabel);
|
||||
assert isNotBlank(bucket);
|
||||
|
||||
final List<String> errorMessages = new ArrayList<String>();
|
||||
|
||||
if (bucket.length() < MIN_BUCKET_NAME_LENGTH) {
|
||||
errorMessages
|
||||
.add(format(
|
||||
"The length of %1$s "
|
||||
+ "for the %2$s must have a length of at least %3$s "
|
||||
+ "characters", bucket, bucketLabel,
|
||||
MIN_BUCKET_NAME_LENGTH));
|
||||
}
|
||||
|
||||
if (bucket.length() > MAX_BUCKET_NAME_LENGTH) {
|
||||
errorMessages.add(format("The length of %1$s "
|
||||
+ "for the %2$s must not have a length of at greater"
|
||||
+ " than %3$s characters", bucket, bucketLabel,
|
||||
MAX_BUCKET_NAME_LENGTH));
|
||||
}
|
||||
|
||||
return unmodifiableList(errorMessages);
|
||||
|
||||
}
|
||||
|
||||
private static List<String> checkOptionalField(final String fieldName,
|
||||
final Integer fieldValue) {
|
||||
if (fieldValue != null && fieldValue < 0) {
|
||||
return singletonList(format("The value of %1$s must "
|
||||
+ "be greater than zero.", fieldName));
|
||||
}
|
||||
return emptyList();
|
||||
}
|
||||
|
||||
private static List<String> checkRequiredField(String fieldName,
|
||||
String fieldValue) {
|
||||
if (isBlank(fieldValue)) {
|
||||
return singletonList(format("A %1$s must be specified.", fieldName));
|
||||
}
|
||||
return emptyList();
|
||||
}
|
||||
|
||||
public interface ClientOptions {
|
||||
|
||||
String getAccessKey();
|
||||
|
||||
String getSecretKey();
|
||||
|
||||
String getEndPoint();
|
||||
|
||||
Boolean isHttps();
|
||||
|
||||
Integer getConnectionTimeout();
|
||||
|
||||
Integer getMaxErrorRetry();
|
||||
|
||||
Integer getSocketTimeout();
|
||||
|
||||
}
|
||||
|
||||
public interface ObjectNamingStrategy {
|
||||
|
||||
String determineKey(File file);
|
||||
|
||||
}
|
||||
|
||||
public interface FileNamingStrategy {
|
||||
|
||||
String determineFileName(String key);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -16,6 +16,8 @@
|
||||
// under the License.
|
||||
package com.cloud.utils;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@ -42,8 +44,12 @@ public class StringUtils {
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static String join(final String delimiter,
|
||||
final Object... components) {
|
||||
return join(asList(components), delimiter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tags
|
||||
* @return List of tags
|
||||
@ -128,4 +134,5 @@ public class StringUtils {
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -16,8 +16,11 @@
|
||||
// under the License.
|
||||
package com.cloud.utils.db;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
@ -201,4 +204,41 @@ public class GlobalLock {
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public static <T> T executeWithLock(final String operationId,
|
||||
final int lockAcquisitionTimeout, final Callable<T> operation)
|
||||
throws Exception {
|
||||
|
||||
final GlobalLock lock = GlobalLock.getInternLock(operationId);
|
||||
|
||||
try {
|
||||
|
||||
if (!lock.lock(lockAcquisitionTimeout)) {
|
||||
if (s_logger.isDebugEnabled()) {
|
||||
s_logger.debug(format(
|
||||
"Failed to acquire lock for operation id %1$s",
|
||||
operationId));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return operation.call();
|
||||
|
||||
} finally {
|
||||
|
||||
if (lock != null) {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static <T> T executeWithNoWaitLock(final String operationId,
|
||||
final Callable<T> operation) throws Exception {
|
||||
|
||||
return executeWithLock(operationId, 0, operation);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user