diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupRepository.java b/api/src/main/java/org/apache/cloudstack/backup/BackupRepository.java new file mode 100644 index 00000000000..8e5c9740e69 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupRepository.java @@ -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(); +} diff --git a/core/src/main/java/org/apache/cloudstack/backup/DeleteBackupCommand.java b/core/src/main/java/org/apache/cloudstack/backup/DeleteBackupCommand.java index 2dd4e3d4f7f..ceb6426a316 100644 --- a/core/src/main/java/org/apache/cloudstack/backup/DeleteBackupCommand.java +++ b/core/src/main/java/org/apache/cloudstack/backup/DeleteBackupCommand.java @@ -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 details; + private String mountOptions; - public DeleteBackupCommand(String backupPath, String backupStoragePath, Map 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 getDetails() { - return details; + public String getBackupRepoAddress() { + return backupRepoAddress; } - public void setDetails(Map 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 diff --git a/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.java b/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.java index 91a49f1ce30..3deb3eb47b0 100644 --- a/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.java +++ b/core/src/main/java/org/apache/cloudstack/backup/TakeBackupCommand.java @@ -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 details; + private String mountOptions; - public TakeBackupCommand(String vmName, String backupPath, String backupStoragePath, Map 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 getDetails() { - return details; + public String getBackupRepoAddress() { + return backupRepoAddress; } - public void setDetails(Map 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 diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupRepositoryVO.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupRepositoryVO.java new file mode 100644 index 00000000000..8ae1cf91449 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/BackupRepositoryVO.java @@ -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; + } +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupRepositoryDao.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupRepositoryDao.java new file mode 100644 index 00000000000..0034bfb30ab --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupRepositoryDao.java @@ -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 { + List listByZoneAndProvider(Long zoneId, String provider); + + BackupRepository findByBackupOfferingId(Long backupOfferingId); +} diff --git a/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupRepositoryDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupRepositoryDaoImpl.java new file mode 100644 index 00000000000..460b6d8aba4 --- /dev/null +++ b/engine/schema/src/main/java/org/apache/cloudstack/backup/dao/BackupRepositoryDaoImpl.java @@ -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 implements BackupRepositoryDao { + @Inject + BackupOfferingDao backupOfferingDao; + + private SearchBuilder 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 listByZoneAndProvider(Long zoneId, String provider) { + SearchCriteria 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()); + } +} diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml index 8ab60a76624..305184bcf7f 100644 --- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml +++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml @@ -269,6 +269,7 @@ + diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql index f59eda5c06c..370abc3f9f2 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql @@ -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` diff --git a/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java b/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java index 24c0c7cc670..0e6b3122a3a 100644 --- a/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java +++ b/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NASBackupProvider.java @@ -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 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 NfsPool = new ConfigKey<>("Advanced", String.class, - "backup.plugin.nas.nfs.pool", "", - "The NFS NAS storage pool URL (format :", true, ConfigKey.Scope.Zone); - - private final ConfigKey CephFSPool = new ConfigKey<>("Advanced", String.class, - "backup.plugin.nas.cephfs.pool", "", - "The CephFS storage pool URL (format: :)", true, ConfigKey.Scope.Zone); - - private final ConfigKey CephFSPoolCredentials = new ConfigKey<>("Advanced", String.class, - "backup.plugin.nas.cephfs.credentials", "", - "The CephFS storage pool URL (format: )", 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 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 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 listBackupOfferings(Long zoneId) { - BackupOffering policy = new BackupOfferingVO(zoneId, "default", getName(), "Default", "Default Backup Offering", true); - return List.of(policy); + final List repositories = backupRepositoryDao.listByZoneAndProvider(zoneId, getName()); + final List 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 diff --git a/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NasBackupOffering.java b/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NasBackupOffering.java new file mode 100644 index 00000000000..91df74166e5 --- /dev/null +++ b/plugins/backup/nas/src/main/java/org/apache/cloudstack/backup/NasBackupOffering.java @@ -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; + } +} diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtDeleteBackupCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtDeleteBackupCommandWrapper.java index cc53fe779c6..cb1e3485824 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtDeleteBackupCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtDeleteBackupCommandWrapper.java @@ -35,14 +35,19 @@ import java.util.List; public class LibvirtDeleteBackupCommandWrapper extends CommandWrapper { @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 commands = new ArrayList<>(); commands.add(new String[]{ libvirtComputingResource.getNasBackupPath(), - "-d", backupFolder, - "-s", backupStoragePath + "-o", "delete", + "-t", backupRepoType, + "-s", backupRepoAddress, + "-m", mountOptions, + "-p", backupPath }); Pair result = Script.executePipedCommands(commands, libvirtComputingResource.getCmdsTimeout()); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtTakeBackupCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtTakeBackupCommandWrapper.java index 7938628653d..30f530ffd8a 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtTakeBackupCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtTakeBackupCommandWrapper.java @@ -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 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 result = Script.executePipedCommands(commands, libvirtComputingResource.getCmdsTimeout()); diff --git a/scripts/vm/hypervisor/kvm/nasbackup.sh b/scripts/vm/hypervisor/kvm/nasbackup.sh index 1595e7fb036..5c6cbc5476a 100755 --- a/scripts/vm/hypervisor/kvm/nasbackup.sh +++ b/scripts/vm/hypervisor/kvm/nasbackup.sh @@ -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 "" > $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 -s -p " @@ -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 -