mirror of https://github.com/apache/cloudstack.git
introduce backup repository concept for NAS & other backups
Each backup repository is for a specific zone & provider. Eack NAS bkcp offering is tied to a back repo & each bkup offer assinged to VM -> defined its backup repository Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
parent
18e05894c7
commit
97ce34ce2b
|
|
@ -0,0 +1,34 @@
|
|||
//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
|
||||
//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 org.apache.cloudstack.backup;
|
||||
|
||||
import org.apache.cloudstack.api.Identity;
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public interface BackupRepository extends InternalIdentity, Identity {
|
||||
String getProvider();
|
||||
long getZoneId();
|
||||
String getName();
|
||||
String getType();
|
||||
String getAddress();
|
||||
String getMountOptions();
|
||||
Long getCapacityBytes();
|
||||
Long getUsedBytes();
|
||||
Date getCreated();
|
||||
}
|
||||
|
|
@ -22,19 +22,19 @@ package org.apache.cloudstack.backup;
|
|||
import com.cloud.agent.api.Command;
|
||||
import com.cloud.agent.api.LogLevel;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class DeleteBackupCommand extends Command {
|
||||
private String backupPath;
|
||||
private String backupStoragePath;
|
||||
private String backupRepoType;
|
||||
private String backupRepoAddress;
|
||||
@LogLevel(LogLevel.Log4jLevel.Off)
|
||||
private Map<String, String> details;
|
||||
private String mountOptions;
|
||||
|
||||
public DeleteBackupCommand(String backupPath, String backupStoragePath, Map<String, String> details) {
|
||||
public DeleteBackupCommand(String backupPath, String backupRepoType, String backupRepoAddress, String mountOptions) {
|
||||
super();
|
||||
this.backupPath = backupPath;
|
||||
this.backupStoragePath = backupStoragePath;
|
||||
this.details = details;
|
||||
this.backupRepoType = backupRepoType;
|
||||
this.backupRepoAddress = backupRepoAddress;
|
||||
this.mountOptions = mountOptions;
|
||||
}
|
||||
|
||||
public String getBackupPath() {
|
||||
|
|
@ -45,20 +45,28 @@ public class DeleteBackupCommand extends Command {
|
|||
this.backupPath = backupPath;
|
||||
}
|
||||
|
||||
public String getBackupStoragePath() {
|
||||
return backupStoragePath;
|
||||
public String getBackupRepoType() {
|
||||
return backupRepoType;
|
||||
}
|
||||
|
||||
public void setBackupStoragePath(String backupStoragePath) {
|
||||
this.backupStoragePath = backupStoragePath;
|
||||
public void setBackupRepoType(String backupRepoType) {
|
||||
this.backupRepoType = backupRepoType;
|
||||
}
|
||||
|
||||
public Map<String, String> getDetails() {
|
||||
return details;
|
||||
public String getBackupRepoAddress() {
|
||||
return backupRepoAddress;
|
||||
}
|
||||
|
||||
public void setDetails(Map<String, String> details) {
|
||||
this.details = details;
|
||||
public void setBackupRepoAddress(String backupRepoAddress) {
|
||||
this.backupRepoAddress = backupRepoAddress;
|
||||
}
|
||||
|
||||
public String getMountOptions() {
|
||||
return mountOptions;
|
||||
}
|
||||
|
||||
public void setMountOptions(String mountOptions) {
|
||||
this.mountOptions = mountOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -22,21 +22,18 @@ package org.apache.cloudstack.backup;
|
|||
import com.cloud.agent.api.Command;
|
||||
import com.cloud.agent.api.LogLevel;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class TakeBackupCommand extends Command {
|
||||
private String vmName;
|
||||
private String backupPath;
|
||||
private String backupStoragePath;
|
||||
private String backupRepoType;
|
||||
private String backupRepoAddress;
|
||||
@LogLevel(LogLevel.Log4jLevel.Off)
|
||||
private Map<String, String> details;
|
||||
private String mountOptions;
|
||||
|
||||
public TakeBackupCommand(String vmName, String backupPath, String backupStoragePath, Map<String, String> details) {
|
||||
public TakeBackupCommand(String vmName, String backupPath) {
|
||||
super();
|
||||
this.vmName = vmName;
|
||||
this.backupPath = backupPath;
|
||||
this.backupStoragePath = backupStoragePath;
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
public String getVmName() {
|
||||
|
|
@ -55,20 +52,28 @@ public class TakeBackupCommand extends Command {
|
|||
this.backupPath = backupPath;
|
||||
}
|
||||
|
||||
public String getBackupStoragePath() {
|
||||
return backupStoragePath;
|
||||
public String getBackupRepoType() {
|
||||
return backupRepoType;
|
||||
}
|
||||
|
||||
public void setBackupStoragePath(String backupStoragePath) {
|
||||
this.backupStoragePath = backupStoragePath;
|
||||
public void setBackupRepoType(String backupRepoType) {
|
||||
this.backupRepoType = backupRepoType;
|
||||
}
|
||||
|
||||
public Map<String, String> getDetails() {
|
||||
return details;
|
||||
public String getBackupRepoAddress() {
|
||||
return backupRepoAddress;
|
||||
}
|
||||
|
||||
public void setDetails(Map<String, String> details) {
|
||||
this.details = details;
|
||||
public void setBackupRepoAddress(String backupRepoAddress) {
|
||||
this.backupRepoAddress = backupRepoAddress;
|
||||
}
|
||||
|
||||
public String getMountOptions() {
|
||||
return mountOptions;
|
||||
}
|
||||
|
||||
public void setMountOptions(String mountOptions) {
|
||||
this.mountOptions = mountOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -0,0 +1,152 @@
|
|||
// 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 org.apache.cloudstack.backup;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
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 javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
@Entity
|
||||
@Table(name = "backup_repository")
|
||||
public class BackupRepositoryVO implements BackupRepository {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id")
|
||||
private long id;
|
||||
|
||||
@Column(name = "uuid")
|
||||
private String uuid;
|
||||
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
|
||||
@Column(name = "zone_id", nullable = false)
|
||||
private long zoneId;
|
||||
|
||||
@Column(name = "provider", nullable = false)
|
||||
private String provider;
|
||||
|
||||
@Column(name = "type", nullable = false)
|
||||
private String type;
|
||||
|
||||
@Column(name = "address", nullable = false)
|
||||
private String address;
|
||||
|
||||
@Column(name = "mount_opts")
|
||||
private String mountOptions;
|
||||
|
||||
@Column(name = "used_bytes",nullable = true)
|
||||
private Long usedBytes;
|
||||
|
||||
@Column(name = "capacity_bytes", nullable = true)
|
||||
private Long capacityBytes;
|
||||
|
||||
@Column(name = "created")
|
||||
@Temporal(value = TemporalType.TIMESTAMP)
|
||||
private Date created;
|
||||
|
||||
@Column(name = "removed")
|
||||
@Temporal(value = TemporalType.TIMESTAMP)
|
||||
private Date removed;
|
||||
|
||||
public BackupRepositoryVO() {
|
||||
this.uuid = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
public BackupRepositoryVO(final long zoneId, final String provider, final String name, final String type, final String address, final String mountOptions, final Long capacityBytes) {
|
||||
this();
|
||||
this.zoneId = zoneId;
|
||||
this.provider = provider;
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.address = address;
|
||||
this.mountOptions = mountOptions;
|
||||
this.capacityBytes = capacityBytes;
|
||||
this.created = new Date();
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getZoneId() {
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProvider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMountOptions() {
|
||||
return mountOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getUsedBytes() {
|
||||
return usedBytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getCapacityBytes() {
|
||||
return capacityBytes;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// 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 org.apache.cloudstack.backup.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.backup.BackupRepository;
|
||||
import org.apache.cloudstack.backup.BackupRepositoryVO;
|
||||
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
public interface BackupRepositoryDao extends GenericDao<BackupRepositoryVO, Long> {
|
||||
List<BackupRepository> listByZoneAndProvider(Long zoneId, String provider);
|
||||
|
||||
BackupRepository findByBackupOfferingId(Long backupOfferingId);
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
// 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 org.apache.cloudstack.backup.dao;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.backup.BackupOfferingVO;
|
||||
import org.apache.cloudstack.backup.BackupRepository;
|
||||
import org.apache.cloudstack.backup.BackupRepositoryVO;
|
||||
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
|
||||
public class BackupRepositoryDaoImpl extends GenericDaoBase<BackupRepositoryVO, Long> implements BackupRepositoryDao {
|
||||
@Inject
|
||||
BackupOfferingDao backupOfferingDao;
|
||||
|
||||
private SearchBuilder<BackupRepositoryVO> backupRepoSearch;
|
||||
|
||||
public BackupRepositoryDaoImpl() {
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
protected void init() {
|
||||
backupRepoSearch = createSearchBuilder();
|
||||
backupRepoSearch.and("zone_id", backupRepoSearch.entity().getZoneId(), SearchCriteria.Op.EQ);
|
||||
backupRepoSearch.and("provider", backupRepoSearch.entity().getProvider(), SearchCriteria.Op.EQ);
|
||||
backupRepoSearch.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BackupRepository> listByZoneAndProvider(Long zoneId, String provider) {
|
||||
SearchCriteria<BackupRepositoryVO> sc = backupRepoSearch.create();
|
||||
sc.setParameters("zone_id", zoneId);
|
||||
sc.setParameters("provider", provider);
|
||||
return new ArrayList<>(listBy(sc));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BackupRepository findByBackupOfferingId(Long backupOfferingId) {
|
||||
BackupOfferingVO offering = backupOfferingDao.findByIdIncludingRemoved(backupOfferingId);
|
||||
if (offering == null) {
|
||||
return null;
|
||||
}
|
||||
return findByUuid(offering.getExternalId());
|
||||
}
|
||||
}
|
||||
|
|
@ -269,6 +269,7 @@
|
|||
<bean id="annotationDaoImpl" class="org.apache.cloudstack.annotation.dao.AnnotationDaoImpl" />
|
||||
<bean id="backupScheduleDaoImpl" class="org.apache.cloudstack.backup.dao.BackupScheduleDaoImpl" />
|
||||
<bean id="backupDaoImpl" class="org.apache.cloudstack.backup.dao.BackupDaoImpl" />
|
||||
<bean id="backupRepositoryDaoImpl" class="org.apache.cloudstack.backup.dao.BackupRepositoryDaoImpl" />
|
||||
<bean id="directDownloadCertificateDaoImpl" class="org.apache.cloudstack.direct.download.DirectDownloadCertificateDaoImpl" />
|
||||
<bean id="directDownloadCertificateHostMapDaoImpl" class="org.apache.cloudstack.direct.download.DirectDownloadCertificateHostMapDaoImpl" />
|
||||
<bean id="routerHealthCheckResultsDaoImpl" class="com.cloud.network.dao.RouterHealthCheckResultDaoImpl" />
|
||||
|
|
|
|||
|
|
@ -134,6 +134,26 @@ CREATE TABLE `cloud`.`webhook_delivery` (
|
|||
CONSTRAINT `fk_webhook__webhook_id` FOREIGN KEY (`webhook_id`) REFERENCES `webhook`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Backup Repository NAS feature
|
||||
DROP TABLE IF EXISTS `cloud`.`backup_repository`;
|
||||
CREATE TABLE `cloud`.`backup_repository` (
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id of the backup repository',
|
||||
`uuid` varchar(255) NOT NULL COMMENT 'uuid of the backup repository',
|
||||
`name` varchar(255) NOT NULL COMMENT 'name of the backup repository',
|
||||
`zone_id` bigint unsigned NOT NULL COMMENT 'id of zone',
|
||||
`provider` varchar(255) NOT NULL COMMENT 'backup provider name',
|
||||
`type` varchar(255) NOT NULL COMMENT 'backup repo type',
|
||||
`address` varchar(1024) NOT NULL COMMENT 'url of the backup repository',
|
||||
`mount_opts` varchar(1024) COMMENT 'mount options for the backup repository',
|
||||
`used_bytes` bigint unsigned,
|
||||
`capacity_bytes` bigint unsigned,
|
||||
`created` datetime,
|
||||
`removed` datetime,
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `i_backup_repository__uuid`(`uuid`),
|
||||
INDEX `i_backup_repository__zone_id_provider`(`zone_id`, `provider`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Normalize quota.usage.smtp.useStartTLS, quota.usage.smtp.useAuth, alert.smtp.useAuth and project.smtp.useAuth values
|
||||
UPDATE
|
||||
`cloud`.`configuration`
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ import com.cloud.utils.exception.CloudRuntimeException;
|
|||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import org.apache.cloudstack.backup.dao.BackupDao;
|
||||
import org.apache.cloudstack.backup.dao.BackupOfferingDao;
|
||||
import org.apache.cloudstack.backup.dao.BackupRepositoryDao;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.Configurable;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
|
@ -43,6 +45,7 @@ import org.apache.logging.log4j.Logger;
|
|||
import org.apache.logging.log4j.LogManager;
|
||||
import javax.inject.Inject;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
|
@ -50,24 +53,16 @@ import java.util.HashMap;
|
|||
|
||||
public class NASBackupProvider extends AdapterBase implements BackupProvider, Configurable {
|
||||
private static final Logger LOG = LogManager.getLogger(NASBackupProvider.class);
|
||||
private final ConfigKey<String> NasType = new ConfigKey<>("Advanced", String.class,
|
||||
"backup.plugin.nas.target.type", "nfs",
|
||||
"The NAS storage target type. Only supported: nfs and cephfs", true, ConfigKey.Scope.Zone);
|
||||
private final ConfigKey<String> NfsPool = new ConfigKey<>("Advanced", String.class,
|
||||
"backup.plugin.nas.nfs.pool", "",
|
||||
"The NFS NAS storage pool URL (format <domain|ip>:<path>", true, ConfigKey.Scope.Zone);
|
||||
|
||||
private final ConfigKey<String> CephFSPool = new ConfigKey<>("Advanced", String.class,
|
||||
"backup.plugin.nas.cephfs.pool", "",
|
||||
"The CephFS storage pool URL (format: <comma-separated domain|ip>:<path>)", true, ConfigKey.Scope.Zone);
|
||||
|
||||
private final ConfigKey<String> CephFSPoolCredentials = new ConfigKey<>("Advanced", String.class,
|
||||
"backup.plugin.nas.cephfs.credentials", "",
|
||||
"The CephFS storage pool URL (format: <name=username,secret=secretkey>)", true, ConfigKey.Scope.Zone);
|
||||
|
||||
@Inject
|
||||
private BackupDao backupDao;
|
||||
|
||||
@Inject
|
||||
private BackupRepositoryDao backupRepositoryDao;
|
||||
|
||||
@Inject
|
||||
private BackupOfferingDao backupOfferingDao;
|
||||
|
||||
@Inject
|
||||
private HostDao hostDao;
|
||||
|
||||
|
|
@ -86,18 +81,6 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
|
|||
@Inject
|
||||
private AgentManager agentManager;
|
||||
|
||||
protected String getNasType(final Long zoneId) {
|
||||
return NasType.valueIn(zoneId);
|
||||
}
|
||||
|
||||
protected String getBackupStoragePath(final Long zoneId) {
|
||||
final String type = getNasType(zoneId);
|
||||
if ("nfs".equalsIgnoreCase(type)) {
|
||||
return NfsPool.valueIn(zoneId);
|
||||
}
|
||||
throw new CloudRuntimeException("NAS backup plugin not configured");
|
||||
}
|
||||
|
||||
protected Host getLastVMHypervisorHost(VirtualMachine vm) {
|
||||
Long hostId = vm.getLastHostId();
|
||||
if (hostId == null) {
|
||||
|
|
@ -145,14 +128,18 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
|
|||
// TODO: add support for backup of stopped VMs
|
||||
final Host host = getRunningVMHypervisorHost(vm);
|
||||
|
||||
final String backupStoragePath = getBackupStoragePath(vm.getDataCenterId());
|
||||
final String nasType = getNasType(vm.getDataCenterId());
|
||||
final Map<String, String> backupDetails = Map.of(
|
||||
"type", nasType
|
||||
);
|
||||
final String backupPath = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss").format(new java.util.Date());
|
||||
final BackupRepository backupRepository = backupRepositoryDao.findByBackupOfferingId(vm.getBackupOfferingId());
|
||||
if (backupRepository == null) {
|
||||
throw new CloudRuntimeException("No valid backup repository found for the VM, please check the attached backup offering");
|
||||
}
|
||||
|
||||
TakeBackupCommand command = new TakeBackupCommand(vm.getInstanceName(), backupPath, backupStoragePath, backupDetails);
|
||||
final String backupPath = String.format("%s/%s", vm.getInstanceName(),
|
||||
new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss").format(new java.util.Date()));
|
||||
|
||||
TakeBackupCommand command = new TakeBackupCommand(vm.getInstanceName(), backupPath);
|
||||
command.setBackupRepoType(backupRepository.getType());
|
||||
command.setBackupRepoAddress(backupRepository.getAddress());
|
||||
command.setMountOptions(backupRepository.getMountOptions());
|
||||
|
||||
BackupAnswer answer = null;
|
||||
try {
|
||||
|
|
@ -166,7 +153,7 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
|
|||
if (answer != null) {
|
||||
BackupVO backup = new BackupVO();
|
||||
backup.setVmId(vm.getId());
|
||||
backup.setExternalId(String.format("%s:%s/%s", nasType, vm.getInstanceName(), backupPath));
|
||||
backup.setExternalId(backupPath);
|
||||
backup.setType("FULL");
|
||||
backup.setDate(new Date());
|
||||
backup.setSize(answer.getSize());
|
||||
|
|
@ -224,19 +211,17 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
|
|||
|
||||
@Override
|
||||
public boolean deleteBackup(Backup backup, boolean forced) {
|
||||
final Long zoneId = backup.getZoneId();
|
||||
final String backupStoragePath = getBackupStoragePath(zoneId);
|
||||
final String nasType = getNasType(zoneId);
|
||||
final Map<String, String> backupDetails = Map.of(
|
||||
"type", nasType
|
||||
);
|
||||
final String backupPath = backup.getExternalId().split(":")[1];
|
||||
final BackupRepository backupRepository = backupRepositoryDao.findByBackupOfferingId(backup.getBackupOfferingId());
|
||||
if (backupRepository == null) {
|
||||
throw new CloudRuntimeException("No valid backup repository found for the VM, please check the attached backup offering");
|
||||
}
|
||||
|
||||
// TODO: this can be any host in the cluster or last host
|
||||
final VirtualMachine vm = vmInstanceDao.findByIdIncludingRemoved(backup.getVmId());
|
||||
final Host host = getRunningVMHypervisorHost(vm);
|
||||
|
||||
DeleteBackupCommand command = new DeleteBackupCommand(backupPath, backupStoragePath, backupDetails);
|
||||
DeleteBackupCommand command = new DeleteBackupCommand(backup.getExternalId(), backupRepository.getType(),
|
||||
backupRepository.getAddress(), backupRepository.getMountOptions());
|
||||
|
||||
BackupAnswer answer = null;
|
||||
try {
|
||||
|
|
@ -299,8 +284,12 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
|
|||
|
||||
@Override
|
||||
public List<BackupOffering> listBackupOfferings(Long zoneId) {
|
||||
BackupOffering policy = new BackupOfferingVO(zoneId, "default", getName(), "Default", "Default Backup Offering", true);
|
||||
return List.of(policy);
|
||||
final List<BackupRepository> repositories = backupRepositoryDao.listByZoneAndProvider(zoneId, getName());
|
||||
final List<BackupOffering> offerings = new ArrayList<>();
|
||||
for (final BackupRepository repository : repositories) {
|
||||
offerings.add(new NasBackupOffering(repository.getName(), repository.getUuid()));
|
||||
}
|
||||
return offerings;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -311,10 +300,6 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
|
|||
@Override
|
||||
public ConfigKey<?>[] getConfigKeys() {
|
||||
return new ConfigKey[]{
|
||||
NasType,
|
||||
NfsPool,
|
||||
CephFSPool,
|
||||
CephFSPoolCredentials
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -325,7 +310,7 @@ public class NASBackupProvider extends AdapterBase implements BackupProvider, Co
|
|||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "NAS KVM Backup Plugin";
|
||||
return "NAS Backup Plugin";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
// 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 org.apache.cloudstack.backup;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class NasBackupOffering implements BackupOffering {
|
||||
|
||||
private String name;
|
||||
private String uid;
|
||||
|
||||
public NasBackupOffering(String name, String uid) {
|
||||
this.name = name;
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExternalId() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "NAS Backup Offering (Repository)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getZoneId() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserDrivenBackupAllowed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProvider() {
|
||||
return "nas";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getCreated() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUuid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
@ -35,14 +35,19 @@ import java.util.List;
|
|||
public class LibvirtDeleteBackupCommandWrapper extends CommandWrapper<DeleteBackupCommand, Answer, LibvirtComputingResource> {
|
||||
@Override
|
||||
public Answer execute(DeleteBackupCommand command, LibvirtComputingResource libvirtComputingResource) {
|
||||
final String backupStoragePath = command.getBackupStoragePath();
|
||||
final String backupFolder = command.getBackupPath();
|
||||
final String backupPath = command.getBackupPath();
|
||||
final String backupRepoType = command.getBackupRepoType();
|
||||
final String backupRepoAddress = command.getBackupRepoAddress();
|
||||
final String mountOptions = command.getMountOptions();
|
||||
|
||||
List<String[]> commands = new ArrayList<>();
|
||||
commands.add(new String[]{
|
||||
libvirtComputingResource.getNasBackupPath(),
|
||||
"-d", backupFolder,
|
||||
"-s", backupStoragePath
|
||||
"-o", "delete",
|
||||
"-t", backupRepoType,
|
||||
"-s", backupRepoAddress,
|
||||
"-m", mountOptions,
|
||||
"-p", backupPath
|
||||
});
|
||||
|
||||
Pair<Integer, String> result = Script.executePipedCommands(commands, libvirtComputingResource.getCmdsTimeout());
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ import org.apache.cloudstack.backup.BackupAnswer;
|
|||
import org.apache.cloudstack.backup.TakeBackupCommand;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -37,15 +39,20 @@ public class LibvirtTakeBackupCommandWrapper extends CommandWrapper<TakeBackupCo
|
|||
@Override
|
||||
public Answer execute(TakeBackupCommand command, LibvirtComputingResource libvirtComputingResource) {
|
||||
final String vmName = command.getVmName();
|
||||
final String backupStoragePath = command.getBackupStoragePath();
|
||||
final String backupFolder = command.getBackupPath();
|
||||
final String backupPath = command.getBackupPath();
|
||||
final String backupRepoType = command.getBackupRepoType();
|
||||
final String backupRepoAddress = command.getBackupRepoAddress();
|
||||
final String mountOptions = command.getMountOptions();
|
||||
|
||||
List<String[]> commands = new ArrayList<>();
|
||||
commands.add(new String[]{
|
||||
libvirtComputingResource.getNasBackupPath(),
|
||||
"-b", vmName,
|
||||
"-s", backupStoragePath,
|
||||
"-p", String.format("%s%s%s", vmName, File.separator, backupFolder)
|
||||
"-b", "backup",
|
||||
"-v", vmName,
|
||||
"-t", backupRepoType,
|
||||
"-s", backupRepoAddress,
|
||||
"-m", mountOptions,
|
||||
"-p", backupPath
|
||||
});
|
||||
|
||||
Pair<Integer, String> result = Script.executePipedCommands(commands, libvirtComputingResource.getCmdsTimeout());
|
||||
|
|
|
|||
|
|
@ -22,16 +22,22 @@ set -e
|
|||
|
||||
# TODO: do libvirt/logging etc checks
|
||||
|
||||
### Declare variables ###
|
||||
|
||||
OP=""
|
||||
VM=""
|
||||
NAS_TYPE=""
|
||||
NAS_ADDRESS=""
|
||||
MOUNT_OPTS=""
|
||||
BACKUP_DIR=""
|
||||
|
||||
### Operation methods ###
|
||||
|
||||
backup_vm() {
|
||||
vm=$1
|
||||
path=$2
|
||||
storage=$3
|
||||
|
||||
mount_point=$(mktemp -d -t csbackup.XXXXX)
|
||||
dest="$mount_point/$path"
|
||||
dest="$mount_point/${BACKUP_DIR}"
|
||||
|
||||
mount -t nfs $storage $mount_point
|
||||
mount -t ${NAS_TYPE} ${NAS_ADDRESS} ${mount_point} $([[ ! -z "${MOUNT_OPTS}" ]] && echo -o ${MOUNT_OPTS})
|
||||
mkdir -p $dest
|
||||
|
||||
echo "<domainbackup mode='push'><disks>" > $dest/backup.xml
|
||||
|
|
@ -42,11 +48,11 @@ backup_vm() {
|
|||
|
||||
virsh -c qemu:///system backup-begin --domain $vm --backupxml $dest/backup.xml > /dev/null 2>/dev/null
|
||||
virsh -c qemu:///system dumpxml $vm > $dest/domain-$vm.xml 2>/dev/null
|
||||
rm -f $dest/backup.xml
|
||||
|
||||
until virsh -c qemu:///system domjobinfo $vm --completed 2>/dev/null | grep "Completed" > /dev/null; do
|
||||
sleep 5
|
||||
done
|
||||
rm -f $dest/backup.xml
|
||||
|
||||
# Print directory size
|
||||
sync
|
||||
|
|
@ -56,25 +62,18 @@ backup_vm() {
|
|||
}
|
||||
|
||||
delete_backup() {
|
||||
path=$1
|
||||
storage=$2
|
||||
|
||||
mount_point=$(mktemp -d -t csbackup.XXXXX)
|
||||
dest="$mount_point/$path"
|
||||
dest="$mount_point/${BACKUP_DIR}"
|
||||
|
||||
mount -t ${NAS_TYPE} ${NAS_ADDRESS} ${mount_point} $([[ ! -z "${MOUNT_OPTS}" ]] && echo -o ${MOUNT_OPTS})
|
||||
|
||||
mount -t nfs $storage $mount_point
|
||||
rm -frv $dest
|
||||
sync
|
||||
|
||||
umount $mount_point
|
||||
rmdir $mount_point
|
||||
}
|
||||
|
||||
OP=""
|
||||
VM=""
|
||||
DEST=""
|
||||
NAS=""
|
||||
TYPE=""
|
||||
|
||||
function usage {
|
||||
echo ""
|
||||
echo "Usage: $0 -b <domain> -s <NAS storage mount path> -p <backup dest path>"
|
||||
|
|
@ -84,37 +83,36 @@ function usage {
|
|||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-b|--backup)
|
||||
OP="backup"
|
||||
-o|--operation)
|
||||
OP="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-v|--vm)
|
||||
VM="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-d|--delete)
|
||||
OP="delete"
|
||||
DEST="$2"
|
||||
-t|--type)
|
||||
NAS_TYPE="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-s|--storage)
|
||||
NAS="$2"
|
||||
TYPE="nfs"
|
||||
NAS_ADDRESS="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-m|--mount)
|
||||
MOUNT_OPTS="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-p|--path)
|
||||
DEST="$2"
|
||||
BACKUP_DIR="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-r|--recover)
|
||||
OP="recover"
|
||||
shift
|
||||
;;
|
||||
-rv|--recover)
|
||||
OP="recover-volume"
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
shift
|
||||
|
|
@ -127,8 +125,7 @@ while [[ $# -gt 0 ]]; do
|
|||
done
|
||||
|
||||
if [ "$OP" = "backup" ]; then
|
||||
backup_vm $VM $DEST $NAS
|
||||
backup_vm
|
||||
elif [ "$OP" = "delete" ]; then
|
||||
delete_backup $DEST $NAS
|
||||
delete_backup
|
||||
fi
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue