From a6ce66e55a65eb0fbae9ead92de6ceac7a87c531 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Thu, 10 Oct 2013 18:00:21 -0700 Subject: [PATCH] move a lot of code into vmsnapshot strategy fix compile fix compile add vm_snapshot_details table in db add vmsnapshot test cases --- client/tomcatconf/applicationContext.xml.in | 12 +- .../agent/api/CreateVMSnapshotAnswer.java | 12 +- .../agent/api/CreateVMSnapshotCommand.java | 4 +- .../agent/api/DeleteVMSnapshotAnswer.java | 12 +- .../agent/api/DeleteVMSnapshotCommand.java | 3 +- .../agent/api/RevertToVMSnapshotAnswer.java | 14 +- .../agent/api/RevertToVMSnapshotCommand.java | 3 +- .../agent/api/VMSnapshotBaseCommand.java | 10 +- .../cloudstack/storage/to/VolumeObjectTO.java | 12 + .../api/storage/VMSnapshotStrategy.java | 28 + .../vm/snapshot/VMSnapshotDetailsVO.java | 87 ++++ .../com/cloud/vm/snapshot/VMSnapshotVO.java | 2 +- .../vm/snapshot/dao/VMSnapshotDetailsDao.java | 28 + .../dao/VMSnapshotDetailsDaoImpl.java | 52 ++ .../motion/AncientDataMotionStrategy.java | 26 - .../vm/snapshot/dao/VmSnapshotDaoTest.java | 46 ++ .../storage/test/ChildTestConfiguration.java | 47 +- .../cloudstack/storage/test/SnapshotTest.java | 102 ++-- .../test/resources/storageContext.xml | 2 + engine/storage/snapshot/pom.xml | 32 ++ .../storage/snapshot/SnapshotServiceImpl.java | 44 +- .../snapshot/XenserverSnapshotStrategy.java | 27 +- .../vmsnapshot/DefaultVMSnapshotStrategy.java | 369 +++++++++++++ .../storage/vmsnapshot/VMSnapshotHelper.java | 38 ++ .../vmsnapshot/VMSnapshotHelperImpl.java | 148 ++++++ .../test/src/VMSnapshotStrategyTest.java | 256 +++++++++ .../storage/volume/VolumeServiceImpl.java | 89 ++-- .../manager/VmwareStorageManagerImpl.java | 87 ++-- .../xen/resource/CitrixResourceBase.java | 28 +- .../storage/snapshot/SnapshotManagerImpl.java | 97 ---- .../vm/snapshot/VMSnapshotManagerImpl.java | 484 +++--------------- .../vm/snapshot/VMSnapshotManagerTest.java | 7 - setup/db/db/schema-421to430.sql | 9 + 33 files changed, 1401 insertions(+), 816 deletions(-) create mode 100644 engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VMSnapshotStrategy.java create mode 100644 engine/schema/src/com/cloud/vm/snapshot/VMSnapshotDetailsVO.java create mode 100644 engine/schema/src/com/cloud/vm/snapshot/dao/VMSnapshotDetailsDao.java create mode 100644 engine/schema/src/com/cloud/vm/snapshot/dao/VMSnapshotDetailsDaoImpl.java create mode 100644 engine/storage/integration-test/test/com/cloud/vm/snapshot/dao/VmSnapshotDaoTest.java create mode 100644 engine/storage/snapshot/src/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategy.java create mode 100644 engine/storage/snapshot/src/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotHelper.java create mode 100644 engine/storage/snapshot/src/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotHelperImpl.java create mode 100644 engine/storage/snapshot/test/src/VMSnapshotStrategyTest.java diff --git a/client/tomcatconf/applicationContext.xml.in b/client/tomcatconf/applicationContext.xml.in index 2a3520be9b2..13ab71e2a5d 100644 --- a/client/tomcatconf/applicationContext.xml.in +++ b/client/tomcatconf/applicationContext.xml.in @@ -365,6 +365,7 @@ + @@ -882,8 +883,17 @@ - + + + + + + + + + + diff --git a/core/src/com/cloud/agent/api/CreateVMSnapshotAnswer.java b/core/src/com/cloud/agent/api/CreateVMSnapshotAnswer.java index f9fb1642b3f..8b8e69e9c38 100644 --- a/core/src/com/cloud/agent/api/CreateVMSnapshotAnswer.java +++ b/core/src/com/cloud/agent/api/CreateVMSnapshotAnswer.java @@ -17,21 +17,21 @@ package com.cloud.agent.api; -import java.util.List; +import org.apache.cloudstack.storage.to.VolumeObjectTO; -import com.cloud.agent.api.to.VolumeTO; +import java.util.List; public class CreateVMSnapshotAnswer extends Answer { - private List volumeTOs; + private List volumeTOs; private VMSnapshotTO vmSnapshotTo; - public List getVolumeTOs() { + public List getVolumeTOs() { return volumeTOs; } - public void setVolumeTOs(List volumeTOs) { + public void setVolumeTOs(List volumeTOs) { this.volumeTOs = volumeTOs; } @@ -53,7 +53,7 @@ public class CreateVMSnapshotAnswer extends Answer { } public CreateVMSnapshotAnswer(CreateVMSnapshotCommand cmd, - VMSnapshotTO vmSnapshotTo, List volumeTOs) { + VMSnapshotTO vmSnapshotTo, List volumeTOs) { super(cmd, true, ""); this.vmSnapshotTo = vmSnapshotTo; this.volumeTOs = volumeTOs; diff --git a/core/src/com/cloud/agent/api/CreateVMSnapshotCommand.java b/core/src/com/cloud/agent/api/CreateVMSnapshotCommand.java index 478987d993b..bfbc21d1c2b 100644 --- a/core/src/com/cloud/agent/api/CreateVMSnapshotCommand.java +++ b/core/src/com/cloud/agent/api/CreateVMSnapshotCommand.java @@ -18,12 +18,14 @@ package com.cloud.agent.api; import java.util.List; +import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.VolumeTO; import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.storage.to.VolumeObjectTO; public class CreateVMSnapshotCommand extends VMSnapshotBaseCommand { - public CreateVMSnapshotCommand(String vmName, VMSnapshotTO snapshot, List volumeTOs, String guestOSType, VirtualMachine.State vmState) { + public CreateVMSnapshotCommand(String vmName, VMSnapshotTO snapshot, List volumeTOs, String guestOSType, VirtualMachine.State vmState) { super(vmName, snapshot, volumeTOs, guestOSType); this.vmState = vmState; } diff --git a/core/src/com/cloud/agent/api/DeleteVMSnapshotAnswer.java b/core/src/com/cloud/agent/api/DeleteVMSnapshotAnswer.java index 8f4ecad3d80..d6ae95cb89d 100644 --- a/core/src/com/cloud/agent/api/DeleteVMSnapshotAnswer.java +++ b/core/src/com/cloud/agent/api/DeleteVMSnapshotAnswer.java @@ -16,12 +16,12 @@ // under the License. package com.cloud.agent.api; +import org.apache.cloudstack.storage.to.VolumeObjectTO; + import java.util.List; -import com.cloud.agent.api.to.VolumeTO; - public class DeleteVMSnapshotAnswer extends Answer { - private List volumeTOs; + private List volumeTOs; public DeleteVMSnapshotAnswer() { } @@ -32,16 +32,16 @@ public class DeleteVMSnapshotAnswer extends Answer { } public DeleteVMSnapshotAnswer(DeleteVMSnapshotCommand cmd, - List volumeTOs) { + List volumeTOs) { super(cmd, true, ""); this.volumeTOs = volumeTOs; } - public List getVolumeTOs() { + public List getVolumeTOs() { return volumeTOs; } - public void setVolumeTOs(List volumeTOs) { + public void setVolumeTOs(List volumeTOs) { this.volumeTOs = volumeTOs; } diff --git a/core/src/com/cloud/agent/api/DeleteVMSnapshotCommand.java b/core/src/com/cloud/agent/api/DeleteVMSnapshotCommand.java index c213448bf9c..1c64a2b6e97 100644 --- a/core/src/com/cloud/agent/api/DeleteVMSnapshotCommand.java +++ b/core/src/com/cloud/agent/api/DeleteVMSnapshotCommand.java @@ -19,10 +19,11 @@ package com.cloud.agent.api; import java.util.List; import com.cloud.agent.api.to.VolumeTO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; public class DeleteVMSnapshotCommand extends VMSnapshotBaseCommand { - public DeleteVMSnapshotCommand(String vmName, VMSnapshotTO snapshot, List volumeTOs, String guestOSType) { + public DeleteVMSnapshotCommand(String vmName, VMSnapshotTO snapshot, List volumeTOs, String guestOSType) { super( vmName, snapshot, volumeTOs, guestOSType); } } diff --git a/core/src/com/cloud/agent/api/RevertToVMSnapshotAnswer.java b/core/src/com/cloud/agent/api/RevertToVMSnapshotAnswer.java index 848ffc0ebf8..6170864c08e 100644 --- a/core/src/com/cloud/agent/api/RevertToVMSnapshotAnswer.java +++ b/core/src/com/cloud/agent/api/RevertToVMSnapshotAnswer.java @@ -17,14 +17,14 @@ package com.cloud.agent.api; -import java.util.List; - -import com.cloud.agent.api.to.VolumeTO; import com.cloud.vm.VirtualMachine; +import org.apache.cloudstack.storage.to.VolumeObjectTO; + +import java.util.List; public class RevertToVMSnapshotAnswer extends Answer { - private List volumeTOs; + private List volumeTOs; private VirtualMachine.State vmState; public RevertToVMSnapshotAnswer(RevertToVMSnapshotCommand cmd, boolean result, @@ -37,7 +37,7 @@ public class RevertToVMSnapshotAnswer extends Answer { } public RevertToVMSnapshotAnswer(RevertToVMSnapshotCommand cmd, - List volumeTOs, + List volumeTOs, VirtualMachine.State vmState) { super(cmd, true, ""); this.volumeTOs = volumeTOs; @@ -48,11 +48,11 @@ public class RevertToVMSnapshotAnswer extends Answer { return vmState; } - public List getVolumeTOs() { + public List getVolumeTOs() { return volumeTOs; } - public void setVolumeTOs(List volumeTOs) { + public void setVolumeTOs(List volumeTOs) { this.volumeTOs = volumeTOs; } diff --git a/core/src/com/cloud/agent/api/RevertToVMSnapshotCommand.java b/core/src/com/cloud/agent/api/RevertToVMSnapshotCommand.java index 429a186e0dc..1e5fd6c9a68 100644 --- a/core/src/com/cloud/agent/api/RevertToVMSnapshotCommand.java +++ b/core/src/com/cloud/agent/api/RevertToVMSnapshotCommand.java @@ -19,10 +19,11 @@ package com.cloud.agent.api; import java.util.List; import com.cloud.agent.api.to.VolumeTO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; public class RevertToVMSnapshotCommand extends VMSnapshotBaseCommand { - public RevertToVMSnapshotCommand(String vmName, VMSnapshotTO snapshot, List volumeTOs, String guestOSType) { + public RevertToVMSnapshotCommand(String vmName, VMSnapshotTO snapshot, List volumeTOs, String guestOSType) { super(vmName, snapshot, volumeTOs, guestOSType); } diff --git a/core/src/com/cloud/agent/api/VMSnapshotBaseCommand.java b/core/src/com/cloud/agent/api/VMSnapshotBaseCommand.java index 2120f2f73b1..b2c524194ea 100644 --- a/core/src/com/cloud/agent/api/VMSnapshotBaseCommand.java +++ b/core/src/com/cloud/agent/api/VMSnapshotBaseCommand.java @@ -19,27 +19,29 @@ package com.cloud.agent.api; import java.util.List; +import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.VolumeTO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; public class VMSnapshotBaseCommand extends Command{ - protected List volumeTOs; + protected List volumeTOs; protected VMSnapshotTO target; protected String vmName; protected String guestOSType; - public VMSnapshotBaseCommand(String vmName, VMSnapshotTO snapshot, List volumeTOs, String guestOSType) { + public VMSnapshotBaseCommand(String vmName, VMSnapshotTO snapshot, List volumeTOs, String guestOSType) { this.vmName = vmName; this.target = snapshot; this.volumeTOs = volumeTOs; this.guestOSType = guestOSType; } - public List getVolumeTOs() { + public List getVolumeTOs() { return volumeTOs; } - public void setVolumeTOs(List volumeTOs) { + public void setVolumeTOs(List volumeTOs) { this.volumeTOs = volumeTOs; } diff --git a/core/src/org/apache/cloudstack/storage/to/VolumeObjectTO.java b/core/src/org/apache/cloudstack/storage/to/VolumeObjectTO.java index 5685fad59c4..46659a3a2d0 100644 --- a/core/src/org/apache/cloudstack/storage/to/VolumeObjectTO.java +++ b/core/src/org/apache/cloudstack/storage/to/VolumeObjectTO.java @@ -38,6 +38,8 @@ public class VolumeObjectTO implements DataTO { private String chainInfo; private Storage.ImageFormat format; private long id; + + private Long deviceId; private Long bytesReadRate; private Long bytesWriteRate; private Long iopsReadRate; @@ -70,6 +72,7 @@ public class VolumeObjectTO implements DataTO { this.iopsReadRate = volume.getIopsReadRate(); this.iopsWriteRate = volume.getIopsWriteRate(); this.hypervisorType = volume.getHypervisorType(); + setDeviceId(volume.getDeviceId()); } public String getUuid() { @@ -220,4 +223,13 @@ public class VolumeObjectTO implements DataTO { return iopsWriteRate; } + public Long getDeviceId() { + return deviceId; + } + + public void setDeviceId(Long deviceId) { + this.deviceId = deviceId; + } + + } diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VMSnapshotStrategy.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VMSnapshotStrategy.java new file mode 100644 index 00000000000..8dd6eca661d --- /dev/null +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VMSnapshotStrategy.java @@ -0,0 +1,28 @@ +/* + * 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.engine.subsystem.api.storage; + +import com.cloud.vm.snapshot.VMSnapshot; + +public interface VMSnapshotStrategy { + VMSnapshot takeVMSnapshot(VMSnapshot vmSnapshot); + boolean deleteVMSnapshot(VMSnapshot vmSnapshot); + boolean revertVMSnapshot(VMSnapshot vmSnapshot); + boolean canHandle(VMSnapshot vmSnapshot); +} diff --git a/engine/schema/src/com/cloud/vm/snapshot/VMSnapshotDetailsVO.java b/engine/schema/src/com/cloud/vm/snapshot/VMSnapshotDetailsVO.java new file mode 100644 index 00000000000..934dd92cca3 --- /dev/null +++ b/engine/schema/src/com/cloud/vm/snapshot/VMSnapshotDetailsVO.java @@ -0,0 +1,87 @@ +/* + * 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.vm.snapshot; + +import org.apache.cloudstack.api.InternalIdentity; + +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.TableGenerator; + +@Entity +@Table(name = "vm_snapshot_details") +public class VMSnapshotDetailsVO implements InternalIdentity { + @Id + @TableGenerator(name = "vm_snapshot_details_seq", table = "sequence", pkColumnName = "name", valueColumnName = "value", pkColumnValue = "vm_snapshot_details_seq", allocationSize = 1) + @GeneratedValue(strategy = GenerationType.TABLE) + @Column(name = "id") + private long id; + + @Column(name = "vm_snapshot_id") + Long vmSnapshotId; + + @Column(name = "name") + String name; + + @Column(name = "value") + String value; + + public VMSnapshotDetailsVO() { + + } + + public VMSnapshotDetailsVO(Long vmSnapshotId, String name, String value) { + this.vmSnapshotId = vmSnapshotId; + this.name = name; + this.value = value; + } + + public Long getVmSnapshotId() { + return this.vmSnapshotId; + } + + public void setVmSnapshotId(Long vmSnapshotId) { + this.vmSnapshotId = vmSnapshotId; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public String getValue() { + return this.value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public long getId() { + return id; + } +} diff --git a/engine/schema/src/com/cloud/vm/snapshot/VMSnapshotVO.java b/engine/schema/src/com/cloud/vm/snapshot/VMSnapshotVO.java index 03d4945fda0..477148cfa3b 100644 --- a/engine/schema/src/com/cloud/vm/snapshot/VMSnapshotVO.java +++ b/engine/schema/src/com/cloud/vm/snapshot/VMSnapshotVO.java @@ -41,7 +41,7 @@ public class VMSnapshotVO implements VMSnapshot { @TableGenerator(name = "vm_snapshots_sq", table = "sequence", pkColumnName = "name", valueColumnName = "value", pkColumnValue = "vm_snapshots_seq", allocationSize = 1) @GeneratedValue(strategy = GenerationType.TABLE) @Column(name = "id") - long id; + Long id; @Column(name = "uuid") String uuid = UUID.randomUUID().toString(); diff --git a/engine/schema/src/com/cloud/vm/snapshot/dao/VMSnapshotDetailsDao.java b/engine/schema/src/com/cloud/vm/snapshot/dao/VMSnapshotDetailsDao.java new file mode 100644 index 00000000000..e84178cc4fd --- /dev/null +++ b/engine/schema/src/com/cloud/vm/snapshot/dao/VMSnapshotDetailsDao.java @@ -0,0 +1,28 @@ +/* + * 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.vm.snapshot.dao; + +import com.cloud.utils.db.GenericDao; +import com.cloud.vm.snapshot.VMSnapshotDetailsVO; + +import java.util.Map; + +public interface VMSnapshotDetailsDao extends GenericDao { + Map getDetails(Long vmSnapshotId); +} diff --git a/engine/schema/src/com/cloud/vm/snapshot/dao/VMSnapshotDetailsDaoImpl.java b/engine/schema/src/com/cloud/vm/snapshot/dao/VMSnapshotDetailsDaoImpl.java new file mode 100644 index 00000000000..b528b39290f --- /dev/null +++ b/engine/schema/src/com/cloud/vm/snapshot/dao/VMSnapshotDetailsDaoImpl.java @@ -0,0 +1,52 @@ +/* + * 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.vm.snapshot.dao; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.vm.snapshot.VMSnapshotDetailsVO; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class VMSnapshotDetailsDaoImpl extends GenericDaoBase implements VMSnapshotDetailsDao { + protected final SearchBuilder searchDetails; + + protected VMSnapshotDetailsDaoImpl() { + super(); + searchDetails = createSearchBuilder(); + searchDetails.and("vmsnapshotId", searchDetails.entity().getVmSnapshotId(), SearchCriteria.Op.EQ); + searchDetails.done(); + } + @Override + public Map getDetails(Long vmSnapshotId) { + SearchCriteria sc = searchDetails.create(); + sc.setParameters("vmsnapshotId", vmSnapshotId); + + List details = listBy(sc); + Map detailsMap = new HashMap(); + for (VMSnapshotDetailsVO detail : details) { + detailsMap.put(detail.getName(), detail.getValue()); + } + + return detailsMap; + } +} diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java index 5f5f01e4e10..7b5b7fcea1b 100644 --- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java +++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java @@ -87,38 +87,12 @@ AncientDataMotionStrategy implements DataMotionStrategy { @Inject EndPointSelector selector; @Inject - TemplateManager templateMgr; - @Inject - VolumeDataStoreDao volumeStoreDao; - @Inject - HostDao hostDao; - @Inject ConfigurationDao configDao; @Inject - StorageManager storageMgr; - @Inject VolumeDao volDao; @Inject - VMTemplateDao templateDao; - @Inject - SnapshotManager snapshotMgr; - @Inject - SnapshotDao snapshotDao; - @Inject - SnapshotDataStoreDao _snapshotStoreDao; - @Inject - PrimaryDataStoreDao primaryDataStoreDao; - @Inject DataStoreManager dataStoreMgr; @Inject - TemplateDataStoreDao templateStoreDao; - @Inject - DiskOfferingDao diskOfferingDao; - @Inject - VMTemplatePoolDao templatePoolDao; - @Inject - VolumeOrchestrationService volumeMgr; - @Inject StorageCacheManager cacheMgr; @Inject ManagementService _mgmtServer; diff --git a/engine/storage/integration-test/test/com/cloud/vm/snapshot/dao/VmSnapshotDaoTest.java b/engine/storage/integration-test/test/com/cloud/vm/snapshot/dao/VmSnapshotDaoTest.java new file mode 100644 index 00000000000..fc52f89f5d0 --- /dev/null +++ b/engine/storage/integration-test/test/com/cloud/vm/snapshot/dao/VmSnapshotDaoTest.java @@ -0,0 +1,46 @@ +/* + * 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.vm.snapshot.dao; + +import com.cloud.vm.snapshot.VMSnapshotDetailsVO; +import junit.framework.Assert; +import org.apache.cloudstack.storage.test.CloudStackTestNGBase; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import javax.inject.Inject; +import java.util.Map; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = "classpath:/storageContext.xml") +public class VmSnapshotDaoTest extends CloudStackTestNGBase { + @Inject + VMSnapshotDetailsDao vmsnapshotDetailsDao; + + @Test + public void testVmSnapshotDetails() { + VMSnapshotDetailsVO detailsVO = new VMSnapshotDetailsVO(1L, "test", "foo"); + vmsnapshotDetailsDao.persist(detailsVO); + Map details = vmsnapshotDetailsDao.getDetails(1L); + Assert.assertTrue(details.containsKey("test")); + } + +} diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java index d5eea85fcd2..228b9571e1e 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java @@ -16,30 +16,6 @@ // under the License. package org.apache.cloudstack.storage.test; -import java.io.IOException; - -import com.cloud.event.ActionEventUtils; -import com.cloud.event.dao.EventDaoImpl; -import org.apache.cloudstack.acl.APIChecker; -import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; -import org.apache.cloudstack.engine.service.api.OrchestrationService; -import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; -import org.apache.cloudstack.framework.config.dao.ConfigurationDaoImpl; -import org.apache.cloudstack.framework.rpc.RpcProvider; -import org.apache.cloudstack.storage.cache.manager.StorageCacheManagerImpl; -import org.apache.cloudstack.storage.test.ChildTestConfiguration.Library; -import org.apache.cloudstack.test.utils.SpringUtils; - -import org.mockito.Mockito; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.ComponentScan.Filter; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.FilterType; -import org.springframework.core.type.classreading.MetadataReader; -import org.springframework.core.type.classreading.MetadataReaderFactory; -import org.springframework.core.type.filter.TypeFilter; - import com.cloud.agent.AgentManager; import com.cloud.alert.AlertManager; import com.cloud.capacity.dao.CapacityDaoImpl; @@ -55,6 +31,8 @@ import com.cloud.dc.dao.DcDetailsDaoImpl; import com.cloud.dc.dao.HostPodDaoImpl; import com.cloud.dc.dao.PodVlanDaoImpl; import com.cloud.domain.dao.DomainDaoImpl; +import com.cloud.event.ActionEventUtils; +import com.cloud.event.dao.EventDaoImpl; import com.cloud.host.dao.HostDao; import com.cloud.host.dao.HostDaoImpl; import com.cloud.host.dao.HostDetailsDaoImpl; @@ -80,7 +58,6 @@ import com.cloud.storage.dao.VolumeDaoImpl; import com.cloud.storage.dao.VolumeHostDaoImpl; import com.cloud.storage.download.DownloadMonitorImpl; import com.cloud.storage.secondary.SecondaryStorageVmManager; -import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.tags.dao.ResourceTagsDaoImpl; import com.cloud.template.TemplateManager; import com.cloud.user.AccountManager; @@ -96,6 +73,26 @@ import com.cloud.vm.dao.UserVmDaoImpl; import com.cloud.vm.dao.UserVmDetailsDaoImpl; import com.cloud.vm.dao.VMInstanceDaoImpl; import com.cloud.vm.snapshot.dao.VMSnapshotDaoImpl; +import org.apache.cloudstack.acl.APIChecker; +import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; +import org.apache.cloudstack.engine.service.api.OrchestrationService; +import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; +import org.apache.cloudstack.framework.config.dao.ConfigurationDaoImpl; +import org.apache.cloudstack.framework.rpc.RpcProvider; +import org.apache.cloudstack.storage.cache.manager.StorageCacheManagerImpl; +import org.apache.cloudstack.storage.test.ChildTestConfiguration.Library; +import org.apache.cloudstack.test.utils.SpringUtils; +import org.mockito.Mockito; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.filter.TypeFilter; + +import java.io.IOException; @Configuration @ComponentScan(basePackageClasses = { NicDaoImpl.class, VMInstanceDaoImpl.class, VMTemplateHostDaoImpl.class, diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTest.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTest.java index 81f77d62429..36bc9124318 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTest.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTest.java @@ -18,52 +18,6 @@ */ package org.apache.cloudstack.storage.test; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.ExecutionException; - -import javax.inject.Inject; - -import junit.framework.Assert; - -import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProviderManager; -import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; -import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; -import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; -import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; -import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotService; -import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotStrategy; -import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory; -import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; -import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService; -import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; -import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; -import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService; -import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService.TemplateApiResult; -import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService.VolumeApiResult; -import org.apache.cloudstack.framework.async.AsyncCallFuture; -import org.apache.cloudstack.storage.LocalHostEndpoint; -import org.apache.cloudstack.storage.MockLocalNfsSecondaryStorageResource; -import org.apache.cloudstack.storage.RemoteHostEndPoint; -import org.apache.cloudstack.storage.command.CopyCmdAnswer; -import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; -import org.apache.cloudstack.storage.datastore.db.ImageStoreVO; -import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; -import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; -import org.apache.cloudstack.storage.to.TemplateObjectTO; -import org.mockito.Matchers; -import org.mockito.Mockito; -import org.springframework.test.context.ContextConfiguration; -import org.testng.AssertJUnit; -import org.testng.annotations.Test; - import com.cloud.agent.AgentManager; import com.cloud.agent.api.Command; import com.cloud.dc.ClusterVO; @@ -99,6 +53,50 @@ import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.utils.component.ComponentContext; +import junit.framework.Assert; +import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProviderManager; +import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotService; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotStrategy; +import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority; +import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService; +import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService.TemplateApiResult; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService.VolumeApiResult; +import org.apache.cloudstack.framework.async.AsyncCallFuture; +import org.apache.cloudstack.storage.LocalHostEndpoint; +import org.apache.cloudstack.storage.MockLocalNfsSecondaryStorageResource; +import org.apache.cloudstack.storage.RemoteHostEndPoint; +import org.apache.cloudstack.storage.command.CopyCmdAnswer; +import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; +import org.apache.cloudstack.storage.datastore.db.ImageStoreVO; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; +import org.apache.cloudstack.storage.to.TemplateObjectTO; +import org.mockito.Matchers; +import org.mockito.Mockito; +import org.springframework.test.context.ContextConfiguration; +import org.testng.AssertJUnit; +import org.testng.annotations.Test; + +import javax.inject.Inject; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ExecutionException; @ContextConfiguration(locations = { "classpath:/storageContext.xml" }) public class SnapshotTest extends CloudStackTestNGBase { @@ -404,7 +402,7 @@ public class SnapshotTest extends CloudStackTestNGBase { StrategyPriority.sortStrategies(snapshotStrategies, snapshot); for (SnapshotStrategy strategy : this.snapshotStrategies) { - if (strategy.canHandle(snapshot) != Priority.CANT_HANDLE) { + if (strategy.canHandle(snapshot) != StrategyPriority.Priority.CANT_HANDLE) { snapshot = strategy.takeSnapshot(snapshot); result = true; } @@ -429,7 +427,7 @@ public class SnapshotTest extends CloudStackTestNGBase { StrategyPriority.sortStrategies(snapshotStrategies, newSnapshot); for (SnapshotStrategy strategy : this.snapshotStrategies) { - if (strategy.canHandle(snapshot) != Priority.CANT_HANDLE) { + if (strategy.canHandle(snapshot) != StrategyPriority.Priority.CANT_HANDLE) { newSnapshot = strategy.takeSnapshot(snapshot); } } @@ -437,7 +435,7 @@ public class SnapshotTest extends CloudStackTestNGBase { // create another snapshot for (SnapshotStrategy strategy : this.snapshotStrategies) { - if (strategy.canHandle(snapshot) != Priority.CANT_HANDLE) { + if (strategy.canHandle(snapshot) != StrategyPriority.Priority.CANT_HANDLE) { strategy.deleteSnapshot(newSnapshot.getId()); } } @@ -454,7 +452,7 @@ public class SnapshotTest extends CloudStackTestNGBase { StrategyPriority.sortStrategies(snapshotStrategies, snapshot); for (SnapshotStrategy strategy : this.snapshotStrategies) { - if (strategy.canHandle(snapshot) != Priority.CANT_HANDLE) { + if (strategy.canHandle(snapshot) != StrategyPriority.Priority.CANT_HANDLE) { snapshot = strategy.takeSnapshot(snapshot); result = true; } @@ -487,7 +485,7 @@ public class SnapshotTest extends CloudStackTestNGBase { StrategyPriority.sortStrategies(snapshotStrategies, newSnapshot); for (SnapshotStrategy strategy : this.snapshotStrategies) { - if (strategy.canHandle(snapshot) != Priority.CANT_HANDLE) { + if (strategy.canHandle(snapshot) != StrategyPriority.Priority.CANT_HANDLE) { newSnapshot = strategy.takeSnapshot(snapshot); } } @@ -499,7 +497,7 @@ public class SnapshotTest extends CloudStackTestNGBase { try { for (SnapshotStrategy strategy : this.snapshotStrategies) { - if (strategy.canHandle(snapshot) != Priority.CANT_HANDLE) { + if (strategy.canHandle(snapshot) != StrategyPriority.Priority.CANT_HANDLE) { boolean res = strategy.deleteSnapshot(newSnapshot.getId()); Assert.assertTrue(res); } diff --git a/engine/storage/integration-test/test/resources/storageContext.xml b/engine/storage/integration-test/test/resources/storageContext.xml index 664f1e3a290..884e813463f 100644 --- a/engine/storage/integration-test/test/resources/storageContext.xml +++ b/engine/storage/integration-test/test/resources/storageContext.xml @@ -85,4 +85,6 @@ + + diff --git a/engine/storage/snapshot/pom.xml b/engine/storage/snapshot/pom.xml index 8a847040cd8..808d0c207a6 100644 --- a/engine/storage/snapshot/pom.xml +++ b/engine/storage/snapshot/pom.xml @@ -30,5 +30,37 @@ cloud-engine-api ${project.version} + + org.apache.cloudstack + cloud-api + ${project.version} + test-jar + test + + + + + maven-compiler-plugin + + + + testCompile + + + + + + maven-surefire-plugin + + + integration-test + + test + + + + + + diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java index a4014b070e8..0799721312d 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java @@ -17,11 +17,10 @@ package org.apache.cloudstack.storage.snapshot; -import java.util.concurrent.ExecutionException; - -import javax.inject.Inject; - -import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; +import com.cloud.storage.DataStoreRole; +import com.cloud.storage.Snapshot; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.fsm.NoTransitionException; import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionService; @@ -41,57 +40,26 @@ import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.framework.async.AsyncRpcContext; import org.apache.cloudstack.storage.command.CommandResult; import org.apache.cloudstack.storage.command.CopyCmdAnswer; -import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager; import org.apache.cloudstack.storage.datastore.PrimaryDataStore; -import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; -import com.cloud.dc.dao.ClusterDao; -import com.cloud.storage.DataStoreRole; -import com.cloud.storage.Snapshot; -import com.cloud.storage.dao.SnapshotDao; -import com.cloud.storage.dao.VolumeDao; -import com.cloud.storage.snapshot.SnapshotManager; -import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.fsm.NoTransitionException; -import com.cloud.vm.dao.UserVmDao; -import com.cloud.vm.snapshot.dao.VMSnapshotDao; +import javax.inject.Inject; +import java.util.concurrent.ExecutionException; @Component public class SnapshotServiceImpl implements SnapshotService { private static final Logger s_logger = Logger.getLogger(SnapshotServiceImpl.class); @Inject - protected VolumeDao _volsDao; - @Inject - protected UserVmDao _vmDao; - @Inject - protected PrimaryDataStoreDao _storagePoolDao; - @Inject - protected ClusterDao _clusterDao; - @Inject - protected SnapshotDao _snapshotDao; - @Inject protected SnapshotDataStoreDao _snapshotStoreDao; - - @Inject - protected SnapshotManager snapshotMgr; - @Inject - protected VolumeOrchestrationService volumeMgr; - @Inject - protected SnapshotStateMachineManager stateMachineManager; @Inject SnapshotDataFactory snapshotfactory; @Inject DataStoreManager dataStoreMgr; @Inject DataMotionService motionSrv; - @Inject - ObjectInDataStoreManager objInStoreMgr; - @Inject - VMSnapshotDao _vmSnapshotDao; static private class CreateSnapshotContext extends AsyncRpcContext { final SnapshotInfo snapshot; diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java index 6a874d67b8b..403f113aa24 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java @@ -16,8 +16,17 @@ // under the License. package org.apache.cloudstack.storage.snapshot; -import javax.inject.Inject; - +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.storage.DataStoreRole; +import com.cloud.storage.Snapshot; +import com.cloud.storage.SnapshotVO; +import com.cloud.storage.Volume; +import com.cloud.storage.dao.SnapshotDao; +import com.cloud.storage.snapshot.SnapshotManager; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.db.DB; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.fsm.NoTransitionException; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; @@ -36,24 +45,12 @@ import org.apache.cloudstack.storage.to.SnapshotObjectTO; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.storage.DataStoreRole; -import com.cloud.storage.Snapshot; -import com.cloud.storage.SnapshotVO; -import com.cloud.storage.Volume; -import com.cloud.storage.dao.SnapshotDao; -import com.cloud.storage.snapshot.SnapshotManager; -import com.cloud.utils.NumbersUtil; -import com.cloud.utils.db.DB; -import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.fsm.NoTransitionException; +import javax.inject.Inject; @Component public class XenserverSnapshotStrategy extends SnapshotStrategyBase { private static final Logger s_logger = Logger.getLogger(XenserverSnapshotStrategy.class); - @Inject - SnapshotManager snapshotMgr; @Inject SnapshotService snapshotSvr; @Inject diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategy.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategy.java new file mode 100644 index 00000000000..6b5e5fbaea8 --- /dev/null +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategy.java @@ -0,0 +1,369 @@ +/* + * 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.storage.vmsnapshot; + +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import org.apache.cloudstack.engine.subsystem.api.storage.VMSnapshotStrategy; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.log4j.Logger; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.CreateVMSnapshotAnswer; +import com.cloud.agent.api.CreateVMSnapshotCommand; +import com.cloud.agent.api.DeleteVMSnapshotAnswer; +import com.cloud.agent.api.DeleteVMSnapshotCommand; +import com.cloud.agent.api.RevertToVMSnapshotAnswer; +import com.cloud.agent.api.RevertToVMSnapshotCommand; +import com.cloud.agent.api.VMSnapshotTO; +import com.cloud.event.EventTypes; +import com.cloud.event.UsageEventUtils; +import com.cloud.exception.AgentUnavailableException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.storage.DiskOfferingVO; +import com.cloud.storage.GuestOSVO; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.DiskOfferingDao; +import com.cloud.storage.dao.GuestOSDao; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.uservm.UserVm; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.fsm.NoTransitionException; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.snapshot.VMSnapshot; +import com.cloud.vm.snapshot.VMSnapshotVO; +import com.cloud.vm.snapshot.dao.VMSnapshotDao; + +public class DefaultVMSnapshotStrategy extends ManagerBase implements VMSnapshotStrategy { + private static final Logger s_logger = Logger.getLogger(DefaultVMSnapshotStrategy.class); + @Inject + VMSnapshotHelper vmSnapshotHelper; + @Inject + GuestOSDao guestOSDao; + @Inject + UserVmDao userVmDao; + @Inject + VMSnapshotDao vmSnapshotDao; + int _wait; + @Inject + ConfigurationDao configurationDao; + @Inject + AgentManager agentMgr; + @Inject + VolumeDao volumeDao; + @Inject + DiskOfferingDao diskOfferingDao; + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + String value = configurationDao.getValue("vmsnapshot.create.wait"); + _wait = NumbersUtil.parseInt(value, 1800); + return true; + } + + public VMSnapshot takeVMSnapshot(VMSnapshot vmSnapshot) { + Long hostId = vmSnapshotHelper.pickRunningHost(vmSnapshot.getVmId()); + UserVm userVm = userVmDao.findById(vmSnapshot.getVmId()); + VMSnapshotVO vmSnapshotVO = (VMSnapshotVO)vmSnapshot; + try { + vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshotVO, VMSnapshot.Event.CreateRequested); + } catch (NoTransitionException e) { + throw new CloudRuntimeException(e.getMessage()); + } + + CreateVMSnapshotAnswer answer = null; + boolean result = false; + try { + GuestOSVO guestOS = guestOSDao.findById(userVm.getGuestOSId()); + + List volumeTOs = vmSnapshotHelper.getVolumeTOList(userVm.getId()); + + VMSnapshotTO current = null; + VMSnapshotVO currentSnapshot = vmSnapshotDao.findCurrentSnapshotByVmId(userVm.getId()); + if (currentSnapshot != null) + current = vmSnapshotHelper.getSnapshotWithParents(currentSnapshot); + VMSnapshotTO target = new VMSnapshotTO(vmSnapshot.getId(), vmSnapshot.getName(), vmSnapshot.getType(), null, vmSnapshot.getDescription(), false, + current); + if (current == null) + vmSnapshotVO.setParent(null); + else + vmSnapshotVO.setParent(current.getId()); + + CreateVMSnapshotCommand ccmd = new CreateVMSnapshotCommand(userVm.getInstanceName(),target ,volumeTOs, guestOS.getDisplayName(),userVm.getState()); + ccmd.setWait(_wait); + + answer = (CreateVMSnapshotAnswer)agentMgr.send(hostId, ccmd); + if (answer != null && answer.getResult()) { + processAnswer(vmSnapshotVO, userVm, answer, hostId); + s_logger.debug("Create vm snapshot " + vmSnapshot.getName() + " succeeded for vm: " + userVm.getInstanceName()); + result = true; + + for (VolumeObjectTO volumeTo : answer.getVolumeTOs()){ + publishUsageEvent(EventTypes.EVENT_VM_SNAPSHOT_CREATE,vmSnapshot,userVm,volumeTo); + } + return vmSnapshot; + } else { + String errMsg = "Creating VM snapshot: " + vmSnapshot.getName() + " failed"; + if(answer != null && answer.getDetails() != null) + errMsg = errMsg + " due to " + answer.getDetails(); + s_logger.error(errMsg); + throw new CloudRuntimeException(errMsg); + } + } catch (OperationTimedoutException e) { + s_logger.debug("Creating VM snapshot: " + vmSnapshot.getName() + " failed: " + e.toString()); + throw new CloudRuntimeException("Creating VM snapshot: " + vmSnapshot.getName() + " failed: " + e.toString()); + } catch (AgentUnavailableException e) { + s_logger.debug("Creating VM snapshot: " + vmSnapshot.getName() + " failed", e); + throw new CloudRuntimeException("Creating VM snapshot: " + vmSnapshot.getName() + " failed: " + e.toString()); + } finally{ + if (!result) { + try { + vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationFailed); + } catch (NoTransitionException e1) { + s_logger.error("Cannot set vm snapshot state due to: " + e1.getMessage()); + } + } + } + } + + @Override + public boolean deleteVMSnapshot(VMSnapshot vmSnapshot) { + UserVmVO userVm = userVmDao.findById(vmSnapshot.getVmId()); + VMSnapshotVO vmSnapshotVO = (VMSnapshotVO)vmSnapshot; + try { + vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot,VMSnapshot.Event.ExpungeRequested); + } catch (NoTransitionException e) { + s_logger.debug("Failed to change vm snapshot state with event ExpungeRequested"); + throw new CloudRuntimeException("Failed to change vm snapshot state with event ExpungeRequested: " + e.getMessage()); + } + + try { + Long hostId = vmSnapshotHelper.pickRunningHost(vmSnapshot.getVmId()); + + List volumeTOs = vmSnapshotHelper.getVolumeTOList(vmSnapshot.getVmId()); + + String vmInstanceName = userVm.getInstanceName(); + VMSnapshotTO parent = vmSnapshotHelper.getSnapshotWithParents(vmSnapshotVO).getParent(); + VMSnapshotTO vmSnapshotTO = new VMSnapshotTO(vmSnapshot.getId(), vmSnapshot.getName(), vmSnapshot.getType(), + vmSnapshot.getCreated().getTime(), vmSnapshot.getDescription(), vmSnapshot.getCurrent(), parent); + GuestOSVO guestOS = guestOSDao.findById(userVm.getGuestOSId()); + DeleteVMSnapshotCommand deleteSnapshotCommand = new DeleteVMSnapshotCommand(vmInstanceName, vmSnapshotTO, volumeTOs,guestOS.getDisplayName()); + + Answer answer = agentMgr.send(hostId, deleteSnapshotCommand); + + if (answer != null && answer.getResult()) { + DeleteVMSnapshotAnswer deleteVMSnapshotAnswer = (DeleteVMSnapshotAnswer)answer; + processAnswer(vmSnapshotVO, userVm, answer, hostId); + for (VolumeObjectTO volumeTo : deleteVMSnapshotAnswer.getVolumeTOs()){ + publishUsageEvent(EventTypes.EVENT_VM_SNAPSHOT_DELETE,vmSnapshot,userVm,volumeTo); + } + return true; + } else { + String errMsg = (answer == null) ? null : answer.getDetails(); + s_logger.error("Delete vm snapshot " + vmSnapshot.getName() + " of vm " + userVm.getInstanceName() + " failed due to " + errMsg); + throw new CloudRuntimeException("Delete vm snapshot " + vmSnapshot.getName() + " of vm " + userVm.getInstanceName() + " failed due to " + errMsg); + } + } catch (OperationTimedoutException e) { + throw new CloudRuntimeException("Delete vm snapshot " + vmSnapshot.getName() + " of vm " + userVm.getInstanceName() + " failed due to " + e.getMessage()); + } catch (AgentUnavailableException e) { + throw new CloudRuntimeException("Delete vm snapshot " + vmSnapshot.getName() + " of vm " + userVm.getInstanceName() + " failed due to " + e.getMessage()); + } + } + + @DB + protected void processAnswer(VMSnapshotVO vmSnapshot, UserVm userVm, Answer as, Long hostId) { + final Transaction txn = Transaction.currentTxn(); + try { + txn.start(); + if (as instanceof CreateVMSnapshotAnswer) { + CreateVMSnapshotAnswer answer = (CreateVMSnapshotAnswer) as; + finalizeCreate(vmSnapshot, answer.getVolumeTOs()); + vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationSucceeded); + } else if (as instanceof RevertToVMSnapshotAnswer) { + RevertToVMSnapshotAnswer answer = (RevertToVMSnapshotAnswer) as; + finalizeRevert(vmSnapshot, answer.getVolumeTOs()); + vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationSucceeded); + } else if (as instanceof DeleteVMSnapshotAnswer) { + DeleteVMSnapshotAnswer answer = (DeleteVMSnapshotAnswer) as; + finalizeDelete(vmSnapshot, answer.getVolumeTOs()); + vmSnapshotDao.remove(vmSnapshot.getId()); + } + txn.commit(); + } catch (Exception e) { + String errMsg = "Error while process answer: " + as.getClass() + " due to " + e.getMessage(); + s_logger.error(errMsg, e); + txn.rollback(); + throw new CloudRuntimeException(errMsg); + } finally { + txn.close(); + } + } + + protected void finalizeDelete(VMSnapshotVO vmSnapshot, List VolumeTOs) { + // update volumes path + updateVolumePath(VolumeTOs); + + // update children's parent snapshots + List children= vmSnapshotDao.listByParent(vmSnapshot.getId()); + for (VMSnapshotVO child : children) { + child.setParent(vmSnapshot.getParent()); + vmSnapshotDao.persist(child); + } + + // update current snapshot + VMSnapshotVO current = vmSnapshotDao.findCurrentSnapshotByVmId(vmSnapshot.getVmId()); + if(current != null && current.getId() == vmSnapshot.getId() && vmSnapshot.getParent() != null){ + VMSnapshotVO parent = vmSnapshotDao.findById(vmSnapshot.getParent()); + parent.setCurrent(true); + vmSnapshotDao.persist(parent); + } + vmSnapshot.setCurrent(false); + vmSnapshotDao.persist(vmSnapshot); + } + + protected void finalizeCreate(VMSnapshotVO vmSnapshot, List VolumeTOs) { + // update volumes path + updateVolumePath(VolumeTOs); + + vmSnapshot.setCurrent(true); + + // change current snapshot + if (vmSnapshot.getParent() != null) { + VMSnapshotVO previousCurrent = vmSnapshotDao.findById(vmSnapshot.getParent()); + previousCurrent.setCurrent(false); + vmSnapshotDao.persist(previousCurrent); + } + vmSnapshotDao.persist(vmSnapshot); + } + + protected void finalizeRevert(VMSnapshotVO vmSnapshot, List volumeToList) { + // update volumes path + updateVolumePath(volumeToList); + + // update current snapshot, current snapshot is the one reverted to + VMSnapshotVO previousCurrent = vmSnapshotDao.findCurrentSnapshotByVmId(vmSnapshot.getVmId()); + if(previousCurrent != null){ + previousCurrent.setCurrent(false); + vmSnapshotDao.persist(previousCurrent); + } + vmSnapshot.setCurrent(true); + vmSnapshotDao.persist(vmSnapshot); + } + + private void updateVolumePath(List volumeTOs) { + for (VolumeObjectTO volume : volumeTOs) { + if (volume.getPath() != null) { + VolumeVO volumeVO = volumeDao.findById(volume.getId()); + volumeVO.setPath(volume.getPath()); + volumeVO.setVmSnapshotChainSize(volume.getSize()); + volumeDao.persist(volumeVO); + } + } + } + + private void publishUsageEvent(String type, VMSnapshot vmSnapshot, UserVm userVm, VolumeObjectTO volumeTo){ + VolumeVO volume = volumeDao.findById(volumeTo.getId()); + Long diskOfferingId = volume.getDiskOfferingId(); + Long offeringId = null; + if (diskOfferingId != null) { + DiskOfferingVO offering = diskOfferingDao.findById(diskOfferingId); + if (offering != null + && (offering.getType() == DiskOfferingVO.Type.Disk)) { + offeringId = offering.getId(); + } + } + UsageEventUtils.publishUsageEvent( + type, + vmSnapshot.getAccountId(), + userVm.getDataCenterId(), + userVm.getId(), + vmSnapshot.getName(), + offeringId, + volume.getId(), // save volume's id into templateId field + volumeTo.getSize(), + VMSnapshot.class.getName(), vmSnapshot.getUuid()); + } + + @Override + public boolean revertVMSnapshot(VMSnapshot vmSnapshot) { + VMSnapshotVO vmSnapshotVO = (VMSnapshotVO)vmSnapshot; + UserVmVO userVm = userVmDao.findById(vmSnapshot.getVmId()); + try { + vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshotVO, VMSnapshot.Event.RevertRequested); + } catch (NoTransitionException e) { + throw new CloudRuntimeException(e.getMessage()); + } + + boolean result = false; + try { + VMSnapshotVO snapshot = vmSnapshotDao.findById(vmSnapshotVO.getId()); + List volumeTOs = vmSnapshotHelper.getVolumeTOList(userVm.getId()); + String vmInstanceName = userVm.getInstanceName(); + VMSnapshotTO parent = vmSnapshotHelper.getSnapshotWithParents(snapshot).getParent(); + + VMSnapshotTO vmSnapshotTO = new VMSnapshotTO(snapshot.getId(), snapshot.getName(), snapshot.getType(), + snapshot.getCreated().getTime(), snapshot.getDescription(), snapshot.getCurrent(), parent); + Long hostId = vmSnapshotHelper.pickRunningHost(vmSnapshot.getVmId()); + GuestOSVO guestOS = guestOSDao.findById(userVm.getGuestOSId()); + RevertToVMSnapshotCommand revertToSnapshotCommand = new RevertToVMSnapshotCommand(vmInstanceName, vmSnapshotTO, volumeTOs, guestOS.getDisplayName()); + + RevertToVMSnapshotAnswer answer = (RevertToVMSnapshotAnswer) agentMgr.send(hostId, revertToSnapshotCommand); + if (answer != null && answer.getResult()) { + processAnswer(vmSnapshotVO, userVm, answer, hostId); + result = true; + } else { + String errMsg = "Revert VM: " + userVm.getInstanceName() + " to snapshot: "+ vmSnapshotVO.getName() + " failed"; + if(answer != null && answer.getDetails() != null) + errMsg = errMsg + " due to " + answer.getDetails(); + s_logger.error(errMsg); + throw new CloudRuntimeException(errMsg); + } + } catch (OperationTimedoutException e) { + s_logger.debug("Failed to revert vm snapshot", e); + throw new CloudRuntimeException(e.getMessage()); + } catch (AgentUnavailableException e) { + s_logger.debug("Failed to revert vm snapshot", e); + throw new CloudRuntimeException(e.getMessage()); + } finally { + if (!result) { + try { + vmSnapshotHelper.vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationFailed); + } catch (NoTransitionException e1) { + s_logger.error("Cannot set vm snapshot state due to: " + e1.getMessage()); + } + } + } + return result; + } + + @Override + public boolean canHandle(VMSnapshot vmSnapshot) { + return true; + } +} diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotHelper.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotHelper.java new file mode 100644 index 00000000000..1437f800c21 --- /dev/null +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotHelper.java @@ -0,0 +1,38 @@ +/* + * 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.storage.vmsnapshot; + +import com.cloud.agent.api.VMSnapshotTO; +import com.cloud.agent.api.to.DataTO; +import com.cloud.utils.fsm.NoTransitionException; +import com.cloud.vm.snapshot.VMSnapshot; +import com.cloud.vm.snapshot.VMSnapshotVO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; + +import java.util.List; + +public interface VMSnapshotHelper { + boolean vmSnapshotStateTransitTo(VMSnapshot vsnp, VMSnapshot.Event event) throws NoTransitionException; + + Long pickRunningHost(Long vmId); + + List getVolumeTOList(Long vmId); + + VMSnapshotTO getSnapshotWithParents(VMSnapshotVO snapshot); +} diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotHelperImpl.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotHelperImpl.java new file mode 100644 index 00000000000..320a59ce207 --- /dev/null +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/vmsnapshot/VMSnapshotHelperImpl.java @@ -0,0 +1,148 @@ +/* + * 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.storage.vmsnapshot; + +import com.cloud.agent.api.VMSnapshotTO; +import com.cloud.agent.api.to.DataTO; +import com.cloud.agent.api.to.VolumeTO; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.storage.StoragePool; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.utils.fsm.NoTransitionException; +import com.cloud.utils.fsm.StateMachine2; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.snapshot.VMSnapshot; +import com.cloud.vm.snapshot.VMSnapshotVO; +import com.cloud.vm.snapshot.dao.VMSnapshotDao; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class VMSnapshotHelperImpl implements VMSnapshotHelper { + @Inject + VMSnapshotDao _vmSnapshotDao; + @Inject + UserVmDao userVmDao; + @Inject + HostDao hostDao; + @Inject + VolumeDao volumeDao; + @Inject + PrimaryDataStoreDao primaryDataStoreDao; + @Inject + VolumeDataFactory volumeDataFactory; + + StateMachine2 _vmSnapshottateMachine ; + public VMSnapshotHelperImpl() { + _vmSnapshottateMachine = VMSnapshot.State.getStateMachine(); + } + @Override + public boolean vmSnapshotStateTransitTo(VMSnapshot vsnp, VMSnapshot.Event event) throws NoTransitionException { + return _vmSnapshottateMachine.transitTo(vsnp, event, null, _vmSnapshotDao); + } + + @Override + public Long pickRunningHost(Long vmId) { + UserVmVO vm = userVmDao.findById(vmId); + // use VM's host if VM is running + if(vm.getState() == VirtualMachine.State.Running) + return vm.getHostId(); + + // check if lastHostId is available + if(vm.getLastHostId() != null){ + HostVO lastHost = hostDao.findById(vm.getLastHostId()); + if(lastHost.getStatus() == com.cloud.host.Status.Up && !lastHost.isInMaintenanceStates()) + return lastHost.getId(); + } + + List listVolumes = volumeDao.findByInstance(vmId); + if (listVolumes == null || listVolumes.size() == 0) { + throw new InvalidParameterValueException("vmInstance has no volumes"); + } + VolumeVO volume = listVolumes.get(0); + Long poolId = volume.getPoolId(); + if (poolId == null) { + throw new InvalidParameterValueException("pool id is not found"); + } + StoragePoolVO storagePool = primaryDataStoreDao.findById(poolId); + if (storagePool == null) { + throw new InvalidParameterValueException("storage pool is not found"); + } + List listHost = hostDao.listAllUpAndEnabledNonHAHosts(Host.Type.Routing, storagePool.getClusterId(), storagePool.getPodId(), + storagePool.getDataCenterId(), null); + if (listHost == null || listHost.size() == 0) { + throw new InvalidParameterValueException("no host in up state is found"); + } + return listHost.get(0).getId(); + } + + @Override + public List getVolumeTOList(Long vmId) { + List volumeTOs = new ArrayList(); + List volumeVos = volumeDao.findByInstance(vmId); + VolumeInfo volumeInfo = null; + for (VolumeVO volume : volumeVos) { + volumeInfo = volumeDataFactory.getVolume(volume.getId()); + + volumeTOs.add((VolumeObjectTO)volumeInfo.getTO()); + } + return volumeTOs; + } + + + private VMSnapshotTO convert2VMSnapshotTO(VMSnapshotVO vo) { + return new VMSnapshotTO(vo.getId(), vo.getName(), vo.getType(), vo.getCreated().getTime(), vo.getDescription(), + vo.getCurrent(), null); + } + + @Override + public VMSnapshotTO getSnapshotWithParents(VMSnapshotVO snapshot) { + Map snapshotMap = new HashMap(); + List allSnapshots = _vmSnapshotDao.findByVm(snapshot.getVmId()); + for (VMSnapshotVO vmSnapshotVO : allSnapshots) { + snapshotMap.put(vmSnapshotVO.getId(), vmSnapshotVO); + } + + VMSnapshotTO currentTO = convert2VMSnapshotTO(snapshot); + VMSnapshotTO result = currentTO; + VMSnapshotVO current = snapshot; + while (current.getParent() != null) { + VMSnapshotVO parent = snapshotMap.get(current.getParent()); + currentTO.setParent(convert2VMSnapshotTO(parent)); + current = snapshotMap.get(current.getParent()); + currentTO = currentTO.getParent(); + } + return result; + } + +} diff --git a/engine/storage/snapshot/test/src/VMSnapshotStrategyTest.java b/engine/storage/snapshot/test/src/VMSnapshotStrategyTest.java new file mode 100644 index 00000000000..8e36fafb5d2 --- /dev/null +++ b/engine/storage/snapshot/test/src/VMSnapshotStrategyTest.java @@ -0,0 +1,256 @@ +/* + * 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 src; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.engine.subsystem.api.storage.VMSnapshotStrategy; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.cloudstack.storage.vmsnapshot.DefaultVMSnapshotStrategy; +import org.apache.cloudstack.storage.vmsnapshot.VMSnapshotHelper; +import org.apache.cloudstack.test.utils.SpringUtils; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.filter.TypeFilter; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.CreateVMSnapshotAnswer; +import com.cloud.agent.api.DeleteVMSnapshotAnswer; +import com.cloud.agent.api.RevertToVMSnapshotAnswer; +import com.cloud.agent.api.VMSnapshotTO; +import com.cloud.exception.AgentUnavailableException; +import com.cloud.exception.OperationTimedoutException; +import com.cloud.storage.GuestOSVO; +import com.cloud.storage.dao.DiskOfferingDao; +import com.cloud.storage.dao.GuestOSDao; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.utils.component.ComponentContext; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.net.NetUtils; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.snapshot.VMSnapshot; +import com.cloud.vm.snapshot.VMSnapshotVO; +import com.cloud.vm.snapshot.dao.VMSnapshotDao; + +import junit.framework.TestCase; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(loader = AnnotationConfigContextLoader.class) +public class VMSnapshotStrategyTest extends TestCase { + @Inject + VMSnapshotStrategy vmSnapshotStrategy; + @Inject + VMSnapshotHelper vmSnapshotHelper; + @Inject UserVmDao userVmDao; + @Inject + GuestOSDao guestOSDao; + @Inject + AgentManager agentMgr; + @Inject + VMSnapshotDao vmSnapshotDao; + @Override + @Before + public void setUp() { + ComponentContext.initComponentsLifeCycle(); + } + + + @Test + public void testCreateVMSnapshot() throws AgentUnavailableException, OperationTimedoutException { + Long hostId = 1L; + Long vmId = 1L; + Long guestOsId = 1L; + List volumeObjectTOs = new ArrayList(); + VMSnapshotVO vmSnapshot = Mockito.mock(VMSnapshotVO.class); + UserVmVO userVmVO = Mockito.mock(UserVmVO.class); + Mockito.when(userVmVO.getGuestOSId()).thenReturn(guestOsId); + Mockito.when(vmSnapshot.getVmId()).thenReturn(vmId); + Mockito.when(vmSnapshotHelper.pickRunningHost(Mockito.anyLong())).thenReturn(hostId); + Mockito.when(vmSnapshotHelper.getVolumeTOList(Mockito.anyLong())).thenReturn(volumeObjectTOs); + Mockito.when(userVmDao.findById(Mockito.anyLong())).thenReturn(userVmVO); + GuestOSVO guestOSVO = Mockito.mock(GuestOSVO.class); + Mockito.when(guestOSDao.findById(Mockito.anyLong())).thenReturn(guestOSVO); + Mockito.when(agentMgr.send(Mockito.anyLong(), Mockito.any(Command.class))).thenReturn(null); + Exception e = null; + try { + vmSnapshotStrategy.takeVMSnapshot(vmSnapshot); + } catch (CloudRuntimeException e1) { + e = e1; + } + + assertNotNull(e); + CreateVMSnapshotAnswer answer = Mockito.mock(CreateVMSnapshotAnswer.class); + Mockito.when(answer.getResult()).thenReturn(true); + Mockito.when(agentMgr.send(Mockito.anyLong(), Mockito.any(Command.class))).thenReturn(answer); + Mockito.when(vmSnapshotDao.findById(Mockito.anyLong())).thenReturn(vmSnapshot); + VMSnapshot snapshot = null; + snapshot = vmSnapshotStrategy.takeVMSnapshot(vmSnapshot); + assertNotNull(snapshot); + } + + @Test + public void testRevertSnapshot() throws AgentUnavailableException, OperationTimedoutException { + Long hostId = 1L; + Long vmId = 1L; + Long guestOsId = 1L; + List volumeObjectTOs = new ArrayList(); + VMSnapshotVO vmSnapshot = Mockito.mock(VMSnapshotVO.class); + UserVmVO userVmVO = Mockito.mock(UserVmVO.class); + Mockito.when(userVmVO.getGuestOSId()).thenReturn(guestOsId); + Mockito.when(vmSnapshot.getVmId()).thenReturn(vmId); + Mockito.when(vmSnapshotHelper.pickRunningHost(Mockito.anyLong())).thenReturn(hostId); + Mockito.when(vmSnapshotHelper.getVolumeTOList(Mockito.anyLong())).thenReturn(volumeObjectTOs); + Mockito.when(userVmDao.findById(Mockito.anyLong())).thenReturn(userVmVO); + GuestOSVO guestOSVO = Mockito.mock(GuestOSVO.class); + Mockito.when(guestOSDao.findById(Mockito.anyLong())).thenReturn(guestOSVO); + VMSnapshotTO vmSnapshotTO = Mockito.mock(VMSnapshotTO.class); + Mockito.when(vmSnapshotHelper.getSnapshotWithParents(Mockito.any(VMSnapshotVO.class))).thenReturn(vmSnapshotTO); + Mockito.when(vmSnapshotDao.findById(Mockito.anyLong())).thenReturn(vmSnapshot); + Mockito.when(vmSnapshot.getId()).thenReturn(1L); + Mockito.when(vmSnapshot.getCreated()).thenReturn(new Date()); + Mockito.when(agentMgr.send(Mockito.anyLong(), Mockito.any(Command.class))).thenReturn(null); + Exception e = null; + try { + vmSnapshotStrategy.revertVMSnapshot(vmSnapshot); + } catch (CloudRuntimeException e1) { + e = e1; + } + + assertNotNull(e); + + RevertToVMSnapshotAnswer answer = Mockito.mock(RevertToVMSnapshotAnswer.class); + Mockito.when(answer.getResult()).thenReturn(Boolean.TRUE); + Mockito.when(agentMgr.send(Mockito.anyLong(), Mockito.any(Command.class))).thenReturn(answer); + boolean result = vmSnapshotStrategy.revertVMSnapshot(vmSnapshot); + assertTrue(result); + } + + @Test + public void testDeleteVMSnapshot() throws AgentUnavailableException, OperationTimedoutException { + Long hostId = 1L; + Long vmId = 1L; + Long guestOsId = 1L; + List volumeObjectTOs = new ArrayList(); + VMSnapshotVO vmSnapshot = Mockito.mock(VMSnapshotVO.class); + UserVmVO userVmVO = Mockito.mock(UserVmVO.class); + Mockito.when(userVmVO.getGuestOSId()).thenReturn(guestOsId); + Mockito.when(vmSnapshot.getVmId()).thenReturn(vmId); + Mockito.when(vmSnapshotHelper.pickRunningHost(Mockito.anyLong())).thenReturn(hostId); + Mockito.when(vmSnapshotHelper.getVolumeTOList(Mockito.anyLong())).thenReturn(volumeObjectTOs); + Mockito.when(userVmDao.findById(Mockito.anyLong())).thenReturn(userVmVO); + GuestOSVO guestOSVO = Mockito.mock(GuestOSVO.class); + Mockito.when(guestOSDao.findById(Mockito.anyLong())).thenReturn(guestOSVO); + VMSnapshotTO vmSnapshotTO = Mockito.mock(VMSnapshotTO.class); + Mockito.when(vmSnapshotHelper.getSnapshotWithParents(Mockito.any(VMSnapshotVO.class))).thenReturn(vmSnapshotTO); + Mockito.when(vmSnapshotDao.findById(Mockito.anyLong())).thenReturn(vmSnapshot); + Mockito.when(vmSnapshot.getId()).thenReturn(1L); + Mockito.when(vmSnapshot.getCreated()).thenReturn(new Date()); + Mockito.when(agentMgr.send(Mockito.anyLong(), Mockito.any(Command.class))).thenReturn(null); + + Exception e = null; + try { + vmSnapshotStrategy.deleteVMSnapshot(vmSnapshot); + } catch (CloudRuntimeException e1) { + e = e1; + } + + assertNotNull(e); + + DeleteVMSnapshotAnswer answer = Mockito.mock(DeleteVMSnapshotAnswer.class); + Mockito.when(answer.getResult()).thenReturn(true); + Mockito.when(agentMgr.send(Mockito.anyLong(), Mockito.any(Command.class))).thenReturn(answer); + + boolean result = vmSnapshotStrategy.deleteVMSnapshot(vmSnapshot); + assertTrue(result); + } + + + @Configuration + @ComponentScan(basePackageClasses = {NetUtils.class, DefaultVMSnapshotStrategy.class}, includeFilters = {@ComponentScan.Filter(value = TestConfiguration.Library.class, type = FilterType.CUSTOM)}, useDefaultFilters = false) + public static class TestConfiguration extends SpringUtils.CloudStackTestConfiguration { + + public static class Library implements TypeFilter { + @Override + public boolean match(MetadataReader mdr, MetadataReaderFactory arg1) throws IOException { + mdr.getClassMetadata().getClassName(); + ComponentScan cs = TestConfiguration.class.getAnnotation(ComponentScan.class); + return SpringUtils.includedInBasePackageClasses(mdr.getClassMetadata().getClassName(), cs); + } + } + + @Bean + public VMSnapshotHelper vmSnapshotHelper() { + return Mockito.mock(VMSnapshotHelper.class); + } + + @Bean + public GuestOSDao guestOSDao() { + return Mockito.mock(GuestOSDao.class); + } + + @Bean + public UserVmDao userVmDao() { + return Mockito.mock(UserVmDao.class); + } + + @Bean + public VMSnapshotDao vmSnapshotDao() { + return Mockito.mock(VMSnapshotDao.class); + } + + @Bean + public ConfigurationDao configurationDao() { + return Mockito.mock(ConfigurationDao.class); + } + + @Bean + public AgentManager agentManager() { + return Mockito.mock(AgentManager.class); + } + + @Bean + public VolumeDao volumeDao() { + return Mockito.mock(VolumeDao.class); + } + + @Bean + public DiskOfferingDao diskOfferingDao() { + return Mockito.mock(DiskOfferingDao.class); + } + } +} diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java index c902aef1f55..376baa54a5e 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java @@ -18,51 +18,6 @@ */ package org.apache.cloudstack.storage.volume; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Map; - -import javax.inject.Inject; - -import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; - -import org.apache.cloudstack.engine.cloud.entity.api.VolumeEntity; -import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo; -import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; -import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; -import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionService; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver; -import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; -import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; -import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; -import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; -import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; -import org.apache.cloudstack.engine.subsystem.api.storage.Scope; -import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; -import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; -import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; -import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; -import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService; -import org.apache.cloudstack.framework.async.AsyncCallFuture; -import org.apache.cloudstack.framework.async.AsyncCallbackDispatcher; -import org.apache.cloudstack.framework.async.AsyncCompletionCallback; -import org.apache.cloudstack.framework.async.AsyncRpcContext; -import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.storage.command.CommandResult; -import org.apache.cloudstack.storage.command.CopyCmdAnswer; -import org.apache.cloudstack.storage.command.DeleteCommand; -import org.apache.cloudstack.storage.datastore.DataObjectManager; -import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager; -import org.apache.cloudstack.storage.datastore.PrimaryDataStore; -import org.apache.cloudstack.storage.datastore.PrimaryDataStoreProviderManager; -import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao; -import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO; -import org.apache.cloudstack.storage.to.VolumeObjectTO; - import com.cloud.agent.api.Answer; import com.cloud.agent.api.storage.ListVolumeAnswer; import com.cloud.agent.api.storage.ListVolumeCommand; @@ -94,6 +49,46 @@ import com.cloud.utils.NumbersUtil; import com.cloud.utils.db.DB; import com.cloud.utils.db.GlobalLock; import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.engine.cloud.entity.api.VolumeEntity; +import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; +import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; +import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionService; +import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver; +import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; +import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; +import org.apache.cloudstack.engine.subsystem.api.storage.Scope; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService; +import org.apache.cloudstack.framework.async.AsyncCallFuture; +import org.apache.cloudstack.framework.async.AsyncCallbackDispatcher; +import org.apache.cloudstack.framework.async.AsyncCompletionCallback; +import org.apache.cloudstack.framework.async.AsyncRpcContext; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.storage.command.CommandResult; +import org.apache.cloudstack.storage.command.CopyCmdAnswer; +import org.apache.cloudstack.storage.command.DeleteCommand; +import org.apache.cloudstack.storage.datastore.PrimaryDataStore; +import org.apache.cloudstack.storage.datastore.PrimaryDataStoreProviderManager; +import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; @Component public class VolumeServiceImpl implements VolumeService { @@ -103,10 +98,6 @@ public class VolumeServiceImpl implements VolumeService { @Inject PrimaryDataStoreProviderManager dataStoreMgr; @Inject - ObjectInDataStoreManager objectInDataStoreMgr; - @Inject - DataObjectManager dataObjectMgr; - @Inject DataMotionService motionSrv; @Inject VolumeDataFactory volFactory; diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java index e11e76612e3..0e2423e483b 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java @@ -16,39 +16,6 @@ // under the License. package com.cloud.hypervisor.vmware.manager; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.OutputStreamWriter; -import java.rmi.RemoteException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.UUID; - -import org.apache.log4j.Logger; - -import com.vmware.vim25.FileInfo; -import com.vmware.vim25.FileQueryFlags; -import com.vmware.vim25.HostDatastoreBrowserSearchResults; -import com.vmware.vim25.HostDatastoreBrowserSearchSpec; -import com.vmware.vim25.ManagedObjectReference; -import com.vmware.vim25.TaskInfo; -import com.vmware.vim25.VirtualDeviceConfigSpec; -import com.vmware.vim25.VirtualDeviceConfigSpecOperation; -import com.vmware.vim25.VirtualDisk; -import com.vmware.vim25.VirtualLsiLogicController; -import com.vmware.vim25.VirtualMachineConfigSpec; -import com.vmware.vim25.VirtualMachineFileInfo; -import com.vmware.vim25.VirtualMachineGuestOsIdentifier; -import com.vmware.vim25.VirtualSCSISharing; - -import org.apache.cloudstack.storage.to.TemplateObjectTO; -import org.apache.cloudstack.storage.to.VolumeObjectTO; - import com.cloud.agent.api.Answer; import com.cloud.agent.api.BackupSnapshotAnswer; import com.cloud.agent.api.BackupSnapshotCommand; @@ -73,7 +40,6 @@ import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.NfsTO; import com.cloud.agent.api.to.StorageFilerTO; -import com.cloud.agent.api.to.VolumeTO; import com.cloud.hypervisor.vmware.mo.CustomFieldConstants; import com.cloud.hypervisor.vmware.mo.DatacenterMO; import com.cloud.hypervisor.vmware.mo.DatastoreMO; @@ -97,6 +63,29 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.Script; import com.cloud.vm.VirtualMachine; import com.cloud.vm.snapshot.VMSnapshot; +import com.vmware.vim25.FileInfo; +import com.vmware.vim25.FileQueryFlags; +import com.vmware.vim25.HostDatastoreBrowserSearchResults; +import com.vmware.vim25.HostDatastoreBrowserSearchSpec; +import com.vmware.vim25.ManagedObjectReference; +import com.vmware.vim25.TaskInfo; +import com.vmware.vim25.VirtualDisk; +import org.apache.cloudstack.storage.to.TemplateObjectTO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.log4j.Logger; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.rmi.RemoteException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.UUID; public class VmwareStorageManagerImpl implements VmwareStorageManager { @Override @@ -1280,7 +1269,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { @Override public CreateVMSnapshotAnswer execute(VmwareHostService hostService, CreateVMSnapshotCommand cmd) { - List volumeTOs = cmd.getVolumeTOs(); + List volumeTOs = cmd.getVolumeTOs(); String vmName = cmd.getVmName(); String vmSnapshotName = cmd.getTarget().getSnapshotName(); String vmSnapshotDesc = cmd.getTarget().getDescription(); @@ -1330,19 +1319,20 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { mapNewDisk.put(baseName, vmdkName); } } - for (VolumeTO volumeTO : volumeTOs) { + for (VolumeObjectTO volumeTO : volumeTOs) { String baseName = extractSnapshotBaseFileName(volumeTO.getPath()); String newPath = mapNewDisk.get(baseName); // get volume's chain size for this VM snapshot, exclude current volume vdisk + DataStoreTO store = volumeTO.getDataStore(); long size = getVMSnapshotChainSize(context,hyperHost,baseName + "*.vmdk", - volumeTO.getPoolUuid(), newPath); + store.getUuid(), newPath); - if(volumeTO.getType()== Volume.Type.ROOT){ + if(volumeTO.getVolumeType()== Volume.Type.ROOT){ // add memory snapshot size - size = size + getVMSnapshotChainSize(context,hyperHost,cmd.getVmName()+"*.vmsn",volumeTO.getPoolUuid(),null); + size = size + getVMSnapshotChainSize(context,hyperHost,cmd.getVmName()+"*.vmsn",store.getUuid(),null); } - volumeTO.setChainSize(size); + volumeTO.setSize(size); volumeTO.setPath(newPath); } return new CreateVMSnapshotAnswer(cmd, cmd.getTarget(), volumeTOs); @@ -1362,7 +1352,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { @Override public DeleteVMSnapshotAnswer execute(VmwareHostService hostService, DeleteVMSnapshotCommand cmd) { - List listVolumeTo = cmd.getVolumeTOs(); + List listVolumeTo = cmd.getVolumeTOs(); VirtualMachineMO vmMo = null; VmwareContext context = hostService.getServiceContext(cmd); Map mapNewDisk = new HashMap(); @@ -1403,16 +1393,17 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { mapNewDisk.put(baseName, vmdkName); } } - for (VolumeTO volumeTo : listVolumeTo) { + for (VolumeObjectTO volumeTo : listVolumeTo) { String baseName = extractSnapshotBaseFileName(volumeTo.getPath()); String newPath = mapNewDisk.get(baseName); + DataStoreTO store = volumeTo.getDataStore(); long size = getVMSnapshotChainSize(context,hyperHost, - baseName + "*.vmdk", volumeTo.getPoolUuid(), newPath); - if(volumeTo.getType()== Volume.Type.ROOT){ + baseName + "*.vmdk", store.getUuid(), newPath); + if(volumeTo.getVolumeType()== Volume.Type.ROOT){ // add memory snapshot size - size = size + getVMSnapshotChainSize(context,hyperHost,cmd.getVmName()+"*.vmsn",volumeTo.getPoolUuid(),null); + size = size + getVMSnapshotChainSize(context,hyperHost,cmd.getVmName()+"*.vmsn",volumeTo.getUuid(),null); } - volumeTo.setChainSize(size); + volumeTo.setSize(size); volumeTo.setPath(newPath); } return new DeleteVMSnapshotAnswer(cmd, listVolumeTo); @@ -1429,7 +1420,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { String snapshotName = cmd.getTarget().getSnapshotName(); String vmName = cmd.getVmName(); Boolean snapshotMemory = cmd.getTarget().getType() == VMSnapshot.Type.DiskAndMemory; - List listVolumeTo = cmd.getVolumeTOs(); + List listVolumeTo = cmd.getVolumeTOs(); VirtualMachine.State vmState = VirtualMachine.State.Running; VirtualMachineMO vmMo = null; VmwareContext context = hostService.getServiceContext(cmd); @@ -1483,7 +1474,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { } } String key = null; - for (VolumeTO volumeTo : listVolumeTo) { + for (VolumeObjectTO volumeTo : listVolumeTo) { String parentUUID = volumeTo.getPath(); String[] s = parentUUID.split("-"); key = s[0]; diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index 0ac8b1c3130..c6a35cca69b 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -734,7 +734,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe private Answer execute(RevertToVMSnapshotCommand cmd) { String vmName = cmd.getVmName(); - List listVolumeTo = cmd.getVolumeTOs(); + List listVolumeTo = cmd.getVolumeTOs(); VMSnapshot.Type vmSnapshotType = cmd.getTarget().getType(); Boolean snapshotMemory = vmSnapshotType == VMSnapshot.Type.DiskAndMemory; Connection conn = getConnection(); @@ -786,7 +786,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } // after revert, VM's volumes path have been changed, need to report to manager - for (VolumeTO volumeTo : listVolumeTo) { + for (VolumeObjectTO volumeTo : listVolumeTo) { Long deviceId = volumeTo.getDeviceId(); VDI vdi = vdiMap.get(deviceId.toString()); volumeTo.setPath(vdi.getUuid(conn)); @@ -6633,7 +6633,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } - private long getVMSnapshotChainSize(Connection conn, VolumeTO volumeTo, String vmName) + private long getVMSnapshotChainSize(Connection conn, VolumeObjectTO volumeTo, String vmName) throws BadServerResponse, XenAPIException, XmlRpcException { Set allvolumeVDIs = VDI.getByNameLabel(conn, volumeTo.getName()); long size = 0; @@ -6657,7 +6657,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe continue; } } - if (volumeTo.getType() == Volume.Type.ROOT) { + if (volumeTo.getVolumeType() == Volume.Type.ROOT) { Map allVMs = VM.getAllRecords(conn); // add size of memory snapshot vdi if (allVMs.size() > 0) { @@ -6690,7 +6690,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe protected Answer execute(final CreateVMSnapshotCommand cmd) { String vmName = cmd.getVmName(); String vmSnapshotName = cmd.getTarget().getSnapshotName(); - List listVolumeTo = cmd.getVolumeTOs(); + List listVolumeTo = cmd.getVolumeTOs(); VirtualMachine.State vmState = cmd.getVmState(); String guestOSType = cmd.getGuestOSType(); @@ -6770,9 +6770,9 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } // calculate used capacity for this VM snapshot - for (VolumeTO volumeTo : cmd.getVolumeTOs()){ + for (VolumeObjectTO volumeTo : cmd.getVolumeTOs()){ long size = getVMSnapshotChainSize(conn,volumeTo,cmd.getVmName()); - volumeTo.setChainSize(size); + volumeTo.setSize(size); } success = true; @@ -6821,7 +6821,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } private VM createWorkingVM(Connection conn, String vmName, - String guestOSType, List listVolumeTo) + String guestOSType, List listVolumeTo) throws BadServerResponse, VmBadPowerState, SrFull, OperationNotAllowed, XenAPIException, XmlRpcException { String guestOsTypeName = getGuestOsType(guestOSType, false); @@ -6835,8 +6835,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe VM template = getVM(conn, guestOsTypeName); VM vm = template.createClone(conn, vmName); vm.setIsATemplate(conn, false); - Map vdiMap = new HashMap(); - for (VolumeTO volume : listVolumeTo) { + Map vdiMap = new HashMap(); + for (VolumeObjectTO volume : listVolumeTo) { String vdiUuid = volume.getPath(); try { VDI vdi = VDI.getByUuid(conn, vdiUuid); @@ -6847,11 +6847,11 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } } for (VDI vdi : vdiMap.keySet()) { - VolumeTO volumeTO = vdiMap.get(vdi); + VolumeObjectTO volumeTO = vdiMap.get(vdi); VBD.Record vbdr = new VBD.Record(); vbdr.VM = vm; vbdr.VDI = vdi; - if (volumeTO.getType() == Volume.Type.ROOT) { + if (volumeTO.getVolumeType() == Volume.Type.ROOT) { vbdr.bootable = true; vbdr.unpluggable = false; } else { @@ -6898,9 +6898,9 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } // re-calculate used capacify for this VM snapshot - for (VolumeTO volumeTo : cmd.getVolumeTOs()){ + for (VolumeObjectTO volumeTo : cmd.getVolumeTOs()){ long size = getVMSnapshotChainSize(conn,volumeTo,cmd.getVmName()); - volumeTo.setChainSize(size); + volumeTo.setSize(size); } return new DeleteVMSnapshotAnswer(cmd, cmd.getVolumeTOs()); diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java index dade9837d90..9de0031ba19 100755 --- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -342,8 +342,6 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, return snapshot; } - - @Override public Snapshot backupSnapshot(Long snapshotId) { SnapshotInfo snapshot = snapshotFactory.getSnapshot(snapshotId, DataStoreRole.Image); @@ -354,97 +352,6 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, return snapshotSrv.backupSnapshot(snapshot); } - /* - @Override - public void downloadSnapshotsFromSwift(SnapshotVO ss) { - - long volumeId = ss.getVolumeId(); - VolumeVO volume = _volsDao.findById(volumeId); - Long dcId = volume.getDataCenterId(); - Long accountId = volume.getAccountId(); - DataStore secStore = this.dataStoreMgr.getImageStore(dcId); - - Long swiftId = ss.getSwiftId(); - SwiftTO swift = _swiftMgr.getSwiftTO(swiftId); - SnapshotVO tss = ss; - List BackupUuids = new ArrayList(30); - while (true) { - BackupUuids.add(0, tss.getBackupSnapshotId()); - if (tss.getPrevSnapshotId() == 0) - break; - Long id = tss.getPrevSnapshotId(); - tss = _snapshotDao.findById(id); - assert tss != null : " can not find snapshot " + id; - } - String parent = null; - try { - for (String backupUuid : BackupUuids) { -<<<<<<< HEAD - downloadSnapshotFromSwiftCommand cmd = new downloadSnapshotFromSwiftCommand(swift, secStore.getUri(), dcId, accountId, volumeId, parent, backupUuid, _backupsnapshotwait); -======= - DownloadSnapshotFromSwiftCommand cmd = new DownloadSnapshotFromSwiftCommand(swift, secondaryStoragePoolUrl, dcId, accountId, volumeId, parent, backupUuid, _backupsnapshotwait); ->>>>>>> master - Answer answer = _agentMgr.sendToSSVM(dcId, cmd); - if ((answer == null) || !answer.getResult()) { - throw new CloudRuntimeException("downloadSnapshotsFromSwift failed "); - } - parent = backupUuid; - } - } catch (Exception e) { - throw new CloudRuntimeException("downloadSnapshotsFromSwift failed due to " + e.toString()); - } - - } - - private List determineBackupUuids(final SnapshotVO snapshot) { - - final List backupUuids = new ArrayList(); - 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 DataStore secStore = this.dataStoreMgr.getImageStore(zoneId); - - final S3TO s3 = _s3Mgr.getS3TO(snapshot.getS3Id()); - final List backupUuids = determineBackupUuids(snapshot); - - try { - String parent = null; - for (final String backupUuid : backupUuids) { - final DownloadSnapshotFromS3Command cmd = new DownloadSnapshotFromS3Command( - s3, parent, secStore.getUri(), 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 public SnapshotVO getParentSnapshot(VolumeInfo volume) { long preId = _snapshotDao.getLastSnapshot(volume.getId(), DataStoreRole.Primary); @@ -546,7 +453,6 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, } } - @Override public String getSecondaryStorageURL(SnapshotVO snapshot) { SnapshotDataStoreVO snapshotStore = _snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Image); @@ -672,7 +578,6 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, return new Pair, Integer>(result.first(), result.second()); } - @Override public boolean deleteSnapshotDirsForAccount(long accountId) { @@ -942,8 +847,6 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, return null; } - - private boolean hostSupportSnapsthotForVolume(HostVO host, VolumeInfo volume) { if (host.getHypervisorType() != HypervisorType.KVM) { return true; diff --git a/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java b/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java index aa772fefa9d..7a200ff0f7e 100644 --- a/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java +++ b/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java @@ -17,82 +17,34 @@ package com.cloud.vm.snapshot; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.ejb.Local; -import javax.inject.Inject; -import javax.naming.ConfigurationException; - -import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; - -import org.apache.cloudstack.api.command.user.vmsnapshot.ListVMSnapshotCmd; -import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; -import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; -import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; - -import com.cloud.agent.AgentManager; -import com.cloud.agent.api.Answer; -import com.cloud.agent.api.Command; -import com.cloud.agent.api.CreateVMSnapshotAnswer; -import com.cloud.agent.api.CreateVMSnapshotCommand; -import com.cloud.agent.api.DeleteVMSnapshotAnswer; -import com.cloud.agent.api.DeleteVMSnapshotCommand; -import com.cloud.agent.api.RevertToVMSnapshotAnswer; -import com.cloud.agent.api.RevertToVMSnapshotCommand; -import com.cloud.agent.api.VMSnapshotTO; -import com.cloud.agent.api.to.VolumeTO; import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; -import com.cloud.event.UsageEventUtils; -import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; -import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; -import com.cloud.host.Host; -import com.cloud.host.HostVO; -import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; -import com.cloud.hypervisor.HypervisorGuruManager; import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao; import com.cloud.projects.Project.ListProjectResourcesCriteria; -import com.cloud.service.dao.ServiceOfferingDao; -import com.cloud.storage.DiskOfferingVO; -import com.cloud.storage.GuestOSVO; import com.cloud.storage.Snapshot; import com.cloud.storage.SnapshotVO; -import com.cloud.storage.StoragePool; import com.cloud.storage.VolumeVO; -import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.GuestOSDao; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.dao.AccountDao; -import com.cloud.user.dao.UserDao; import com.cloud.uservm.UserVm; import com.cloud.utils.DateUtil; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Ternary; import com.cloud.utils.component.ManagerBase; -import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; -import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.fsm.NoTransitionException; -import com.cloud.utils.fsm.StateMachine2; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; @@ -100,8 +52,22 @@ import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.UserVmDao; -import com.cloud.vm.dao.VMInstanceDao; import com.cloud.vm.snapshot.dao.VMSnapshotDao; +import org.apache.cloudstack.api.command.user.vmsnapshot.ListVMSnapshotCmd; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.engine.subsystem.api.storage.VMSnapshotStrategy; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import javax.ejb.Local; +import javax.inject.Inject; +import javax.naming.ConfigurationException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; @Component @Local(value = { VMSnapshotManager.class, VMSnapshotService.class }) @@ -111,25 +77,27 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana @Inject VMSnapshotDao _vmSnapshotDao; @Inject VolumeDao _volumeDao; @Inject AccountDao _accountDao; - @Inject VMInstanceDao _vmInstanceDao; @Inject UserVmDao _userVMDao; - @Inject HostDao _hostDao; - @Inject UserDao _userDao; - @Inject AgentManager _agentMgr; - @Inject HypervisorGuruManager _hvGuruMgr; @Inject AccountManager _accountMgr; @Inject GuestOSDao _guestOSDao; - @Inject PrimaryDataStoreDao _storagePoolDao; @Inject SnapshotDao _snapshotDao; @Inject VirtualMachineManager _itMgr; - @Inject DataStoreManager dataStoreMgr; @Inject ConfigurationDao _configDao; @Inject HypervisorCapabilitiesDao _hypervisorCapabilitiesDao; - @Inject DiskOfferingDao _diskOfferingDao; - @Inject ServiceOfferingDao _serviceOfferingDao; + @Inject List vmSnapshotStrategies; + + public List getVmSnapshotStrategies() { + return vmSnapshotStrategies; + } + + @Inject + public void setVmSnapshotStrategies(List vmSnapshotStrategies) { + this.vmSnapshotStrategies = vmSnapshotStrategies; + } + int _vmSnapshotMax; int _wait; - StateMachine2 _vmSnapshottateMachine ; + @Override public boolean configure(String name, Map params) throws ConfigurationException { @@ -144,7 +112,6 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana String value = _configDao.getValue("vmsnapshot.create.wait"); _wait = NumbersUtil.parseInt(value, 1800); - _vmSnapshottateMachine = VMSnapshot.State.getStateMachine(); return true; } @@ -336,6 +303,22 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana return _name; } + private VMSnapshotStrategy findVMSnapshotStrategy(VMSnapshot vmSnapshot) { + VMSnapshotStrategy snapshotStrategy = null; + for(VMSnapshotStrategy strategy : vmSnapshotStrategies) { + if (strategy.canHandle(vmSnapshot)) { + snapshotStrategy = strategy; + break; + } + } + + if (snapshotStrategy == null) { + throw new CloudRuntimeException("can't find vm snapshot strategy for vmsnapshot: " + vmSnapshot.getId()); + } + + return snapshotStrategy; + } + @Override @ActionEvent(eventType = EventTypes.EVENT_VM_SNAPSHOT_CREATE, eventDescription = "creating VM snapshot", async = true) public VMSnapshot creatVMSnapshot(Long vmId, Long vmSnapshotId) { @@ -347,241 +330,21 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana if(vmSnapshot == null){ throw new CloudRuntimeException("VM snapshot id: " + vmSnapshotId + " can not be found"); } - Long hostId = pickRunningHost(vmId); - try { - vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.CreateRequested); - } catch (NoTransitionException e) { - throw new CloudRuntimeException(e.getMessage()); - } - return createVmSnapshotInternal(userVm, vmSnapshot, hostId); - } - protected VMSnapshot createVmSnapshotInternal(UserVmVO userVm, VMSnapshotVO vmSnapshot, Long hostId) { - CreateVMSnapshotAnswer answer = null; try { - GuestOSVO guestOS = _guestOSDao.findById(userVm.getGuestOSId()); - - // prepare snapshotVolumeTos - List volumeTOs = getVolumeTOList(userVm.getId()); - - // prepare target snapshotTO and its parent snapshot (current snapshot) - VMSnapshotTO current = null; - VMSnapshotVO currentSnapshot = _vmSnapshotDao.findCurrentSnapshotByVmId(userVm.getId()); - if (currentSnapshot != null) - current = getSnapshotWithParents(currentSnapshot); - VMSnapshotTO target = new VMSnapshotTO(vmSnapshot.getId(), vmSnapshot.getName(), vmSnapshot.getType(), null, vmSnapshot.getDescription(), false, - current); - if (current == null) - vmSnapshot.setParent(null); - else - vmSnapshot.setParent(current.getId()); - - CreateVMSnapshotCommand ccmd = new CreateVMSnapshotCommand(userVm.getInstanceName(),target ,volumeTOs, guestOS.getDisplayName(),userVm.getState()); - ccmd.setWait(_wait); - - answer = (CreateVMSnapshotAnswer) sendToPool(hostId, ccmd); - if (answer != null && answer.getResult()) { - processAnswer(vmSnapshot, userVm, answer, hostId); - s_logger.debug("Create vm snapshot " + vmSnapshot.getName() + " succeeded for vm: " + userVm.getInstanceName()); - }else{ - - String errMsg = "Creating VM snapshot: " + vmSnapshot.getName() + " failed"; - if(answer != null && answer.getDetails() != null) - errMsg = errMsg + " due to " + answer.getDetails(); - s_logger.error(errMsg); - vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationFailed); - throw new CloudRuntimeException(errMsg); - } - return vmSnapshot; + VMSnapshotStrategy strategy = findVMSnapshotStrategy(vmSnapshot); + VMSnapshot snapshot = strategy.takeVMSnapshot(vmSnapshot); + return snapshot; } catch (Exception e) { - if(e instanceof AgentUnavailableException){ - try { - vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationFailed); - } catch (NoTransitionException e1) { - s_logger.error("Cannot set vm snapshot state due to: " + e1.getMessage()); - } - } - String msg = e.getMessage(); - s_logger.error("Create vm snapshot " + vmSnapshot.getName() + " failed for vm: " + userVm.getInstanceName() + " due to " + msg); - throw new CloudRuntimeException(msg); - } finally{ - if(vmSnapshot.getState() == VMSnapshot.State.Allocated){ - s_logger.warn("Create vm snapshot " + vmSnapshot.getName() + " failed for vm: " + userVm.getInstanceName()); - _vmSnapshotDao.remove(vmSnapshot.getId()); - } - if(vmSnapshot.getState() == VMSnapshot.State.Ready && answer != null){ - for (VolumeTO volumeTo : answer.getVolumeTOs()){ - publishUsageEvent(EventTypes.EVENT_VM_SNAPSHOT_CREATE,vmSnapshot,userVm,volumeTo); - } - } + s_logger.debug("Failed to create vm snapshot: " + vmSnapshotId ,e); + return null; } } - private void publishUsageEvent(String type, VMSnapshot vmSnapshot, UserVm userVm, VolumeTO volumeTo){ - VolumeVO volume = _volumeDao.findById(volumeTo.getId()); - Long diskOfferingId = volume.getDiskOfferingId(); - Long offeringId = null; - if (diskOfferingId != null) { - DiskOfferingVO offering = _diskOfferingDao.findById(diskOfferingId); - if (offering != null - && (offering.getType() == DiskOfferingVO.Type.Disk)) { - offeringId = offering.getId(); - } - } - UsageEventUtils.publishUsageEvent( - type, - vmSnapshot.getAccountId(), - userVm.getDataCenterId(), - userVm.getId(), - vmSnapshot.getName(), - offeringId, - volume.getId(), // save volume's id into templateId field - volumeTo.getChainSize(), - VMSnapshot.class.getName(), vmSnapshot.getUuid()); - } - - protected List getVolumeTOList(Long vmId) { - List volumeTOs = new ArrayList(); - List volumeVos = _volumeDao.findByInstance(vmId); - - for (VolumeVO volume : volumeVos) { - StoragePool pool = (StoragePool)dataStoreMgr.getPrimaryDataStore(volume.getPoolId()); - VolumeTO volumeTO = new VolumeTO(volume, pool); - volumeTOs.add(volumeTO); - } - return volumeTOs; - } - - // get snapshot and its parents recursively - private VMSnapshotTO getSnapshotWithParents(VMSnapshotVO snapshot) { - Map snapshotMap = new HashMap(); - List allSnapshots = _vmSnapshotDao.findByVm(snapshot.getVmId()); - for (VMSnapshotVO vmSnapshotVO : allSnapshots) { - snapshotMap.put(vmSnapshotVO.getId(), vmSnapshotVO); - } - - VMSnapshotTO currentTO = convert2VMSnapshotTO(snapshot); - VMSnapshotTO result = currentTO; - VMSnapshotVO current = snapshot; - while (current.getParent() != null) { - VMSnapshotVO parent = snapshotMap.get(current.getParent()); - currentTO.setParent(convert2VMSnapshotTO(parent)); - current = snapshotMap.get(current.getParent()); - currentTO = currentTO.getParent(); - } - return result; - } - - private VMSnapshotTO convert2VMSnapshotTO(VMSnapshotVO vo) { - return new VMSnapshotTO(vo.getId(), vo.getName(), vo.getType(), vo.getCreated().getTime(), vo.getDescription(), - vo.getCurrent(), null); - } - - protected boolean vmSnapshotStateTransitTo(VMSnapshotVO vsnp, VMSnapshot.Event event) throws NoTransitionException { - return _vmSnapshottateMachine.transitTo(vsnp, event, null, _vmSnapshotDao); - } - - @DB - protected void processAnswer(VMSnapshotVO vmSnapshot, UserVmVO userVm, Answer as, Long hostId) { - final Transaction txn = Transaction.currentTxn(); - try { - txn.start(); - if (as instanceof CreateVMSnapshotAnswer) { - CreateVMSnapshotAnswer answer = (CreateVMSnapshotAnswer) as; - finalizeCreate(vmSnapshot, answer.getVolumeTOs()); - vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationSucceeded); - } else if (as instanceof RevertToVMSnapshotAnswer) { - RevertToVMSnapshotAnswer answer = (RevertToVMSnapshotAnswer) as; - finalizeRevert(vmSnapshot, answer.getVolumeTOs()); - vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationSucceeded); - } else if (as instanceof DeleteVMSnapshotAnswer) { - DeleteVMSnapshotAnswer answer = (DeleteVMSnapshotAnswer) as; - finalizeDelete(vmSnapshot, answer.getVolumeTOs()); - _vmSnapshotDao.remove(vmSnapshot.getId()); - } - txn.commit(); - } catch (Exception e) { - String errMsg = "Error while process answer: " + as.getClass() + " due to " + e.getMessage(); - s_logger.error(errMsg, e); - txn.rollback(); - throw new CloudRuntimeException(errMsg); - } finally { - txn.close(); - } - } - - protected void finalizeDelete(VMSnapshotVO vmSnapshot, List VolumeTOs) { - // update volumes path - updateVolumePath(VolumeTOs); - - // update children's parent snapshots - List children= _vmSnapshotDao.listByParent(vmSnapshot.getId()); - for (VMSnapshotVO child : children) { - child.setParent(vmSnapshot.getParent()); - _vmSnapshotDao.persist(child); - } - - // update current snapshot - VMSnapshotVO current = _vmSnapshotDao.findCurrentSnapshotByVmId(vmSnapshot.getVmId()); - if(current != null && current.getId() == vmSnapshot.getId() && vmSnapshot.getParent() != null){ - VMSnapshotVO parent = _vmSnapshotDao.findById(vmSnapshot.getParent()); - parent.setCurrent(true); - _vmSnapshotDao.persist(parent); - } - vmSnapshot.setCurrent(false); - _vmSnapshotDao.persist(vmSnapshot); - } - - protected void finalizeCreate(VMSnapshotVO vmSnapshot, List VolumeTOs) { - // update volumes path - updateVolumePath(VolumeTOs); - - vmSnapshot.setCurrent(true); - - // change current snapshot - if (vmSnapshot.getParent() != null) { - VMSnapshotVO previousCurrent = _vmSnapshotDao.findById(vmSnapshot.getParent()); - previousCurrent.setCurrent(false); - _vmSnapshotDao.persist(previousCurrent); - } - _vmSnapshotDao.persist(vmSnapshot); - } - - protected void finalizeRevert(VMSnapshotVO vmSnapshot, List volumeToList) { - // update volumes path - updateVolumePath(volumeToList); - - // update current snapshot, current snapshot is the one reverted to - VMSnapshotVO previousCurrent = _vmSnapshotDao.findCurrentSnapshotByVmId(vmSnapshot.getVmId()); - if(previousCurrent != null){ - previousCurrent.setCurrent(false); - _vmSnapshotDao.persist(previousCurrent); - } - vmSnapshot.setCurrent(true); - _vmSnapshotDao.persist(vmSnapshot); - } - - private void updateVolumePath(List volumeTOs) { - for (VolumeTO volume : volumeTOs) { - if (volume.getPath() != null) { - VolumeVO volumeVO = _volumeDao.findById(volume.getId()); - volumeVO.setPath(volume.getPath()); - volumeVO.setVmSnapshotChainSize(volume.getChainSize()); - _volumeDao.persist(volumeVO); - } - } - } - public VMSnapshotManagerImpl() { } - - protected Answer sendToPool(Long hostId, Command cmd) throws AgentUnavailableException, OperationTimedoutException { - long targetHostId = _hvGuruMgr.getGuruProcessedCommandTargetHost(hostId, cmd); - Answer answer = _agentMgr.send(targetHostId, cmd); - return answer; - } - + @Override public boolean hasActiveVMSnapshotTasks(Long vmId){ List activeVMSnapshots = _vmSnapshotDao.listByInstanceId(vmId, @@ -617,50 +380,14 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana if(vmSnapshot.getState() == VMSnapshot.State.Allocated){ return _vmSnapshotDao.remove(vmSnapshot.getId()); - }else{ - return deleteSnapshotInternal(vmSnapshot); - } - } - - @DB - protected boolean deleteSnapshotInternal(VMSnapshotVO vmSnapshot) { - UserVmVO userVm = _userVMDao.findById(vmSnapshot.getVmId()); - DeleteVMSnapshotAnswer answer = null; - try { - vmSnapshotStateTransitTo(vmSnapshot,VMSnapshot.Event.ExpungeRequested); - Long hostId = pickRunningHost(vmSnapshot.getVmId()); - - // prepare snapshotVolumeTos - List volumeTOs = getVolumeTOList(vmSnapshot.getVmId()); - - // prepare DeleteVMSnapshotCommand - String vmInstanceName = userVm.getInstanceName(); - VMSnapshotTO parent = getSnapshotWithParents(vmSnapshot).getParent(); - VMSnapshotTO vmSnapshotTO = new VMSnapshotTO(vmSnapshot.getId(), vmSnapshot.getName(), vmSnapshot.getType(), - vmSnapshot.getCreated().getTime(), vmSnapshot.getDescription(), vmSnapshot.getCurrent(), parent); - GuestOSVO guestOS = _guestOSDao.findById(userVm.getGuestOSId()); - DeleteVMSnapshotCommand deleteSnapshotCommand = new DeleteVMSnapshotCommand(vmInstanceName, vmSnapshotTO, volumeTOs,guestOS.getDisplayName()); - - answer = (DeleteVMSnapshotAnswer) sendToPool(hostId, deleteSnapshotCommand); - - if (answer != null && answer.getResult()) { - processAnswer(vmSnapshot, userVm, answer, hostId); - s_logger.debug("Delete VM snapshot " + vmSnapshot.getName() + " succeeded for vm: " + userVm.getInstanceName()); - return true; - } else { - s_logger.error("Delete vm snapshot " + vmSnapshot.getName() + " of vm " + userVm.getInstanceName() + " failed due to " + answer.getDetails()); + } else{ + try { + VMSnapshotStrategy strategy = findVMSnapshotStrategy(vmSnapshot); + return strategy.deleteVMSnapshot(vmSnapshot); + } catch (Exception e) { + s_logger.debug("Failed to delete vm snapshot: " + vmSnapshotId, e); return false; } - } catch (Exception e) { - String msg = "Delete vm snapshot " + vmSnapshot.getName() + " of vm " + userVm.getInstanceName() + " failed due to " + e.getMessage(); - s_logger.error(msg , e); - throw new CloudRuntimeException(e.getMessage()); - } finally{ - if(answer != null && answer.getResult()){ - for (VolumeTO volumeTo : answer.getVolumeTOs()){ - publishUsageEvent(EventTypes.EVENT_VM_SNAPSHOT_DELETE,vmSnapshot,userVm,volumeTo); - } - } } } @@ -726,108 +453,29 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana throw new CloudRuntimeException(e.getMessage()); } } - hostId = pickRunningHost(userVm.getId()); } - - if(hostId == null) - throw new CloudRuntimeException("Can not find any host to revert snapshot " + vmSnapshotVo.getName()); - + // check if there are other active VM snapshot tasks if (hasActiveVMSnapshotTasks(userVm.getId())) { throw new InvalidParameterValueException("There is other active vm snapshot tasks on the instance, please try again later"); } - - userVm = _userVMDao.findById(userVm.getId()); - try { - vmSnapshotStateTransitTo(vmSnapshotVo, VMSnapshot.Event.RevertRequested); - } catch (NoTransitionException e) { - throw new CloudRuntimeException(e.getMessage()); - } - return revertInternal(userVm, vmSnapshotVo, hostId); - } - private UserVm revertInternal(UserVmVO userVm, VMSnapshotVO vmSnapshotVo, Long hostId) { try { - VMSnapshotVO snapshot = _vmSnapshotDao.findById(vmSnapshotVo.getId()); - // prepare RevertToSnapshotCommand - List volumeTOs = getVolumeTOList(userVm.getId()); - String vmInstanceName = userVm.getInstanceName(); - VMSnapshotTO parent = getSnapshotWithParents(snapshot).getParent(); - VMSnapshotTO vmSnapshotTO = new VMSnapshotTO(snapshot.getId(), snapshot.getName(), snapshot.getType(), - snapshot.getCreated().getTime(), snapshot.getDescription(), snapshot.getCurrent(), parent); - - GuestOSVO guestOS = _guestOSDao.findById(userVm.getGuestOSId()); - RevertToVMSnapshotCommand revertToSnapshotCommand = new RevertToVMSnapshotCommand(vmInstanceName, vmSnapshotTO, volumeTOs, guestOS.getDisplayName()); - - RevertToVMSnapshotAnswer answer = (RevertToVMSnapshotAnswer) sendToPool(hostId, revertToSnapshotCommand); - if (answer != null && answer.getResult()) { - processAnswer(vmSnapshotVo, userVm, answer, hostId); - s_logger.debug("RevertTo " + vmSnapshotVo.getName() + " succeeded for vm: " + userVm.getInstanceName()); - } else { - String errMsg = "Revert VM: " + userVm.getInstanceName() + " to snapshot: "+ vmSnapshotVo.getName() + " failed"; - if(answer != null && answer.getDetails() != null) - errMsg = errMsg + " due to " + answer.getDetails(); - s_logger.error(errMsg); - // agent report revert operation fails - vmSnapshotStateTransitTo(vmSnapshotVo, VMSnapshot.Event.OperationFailed); - throw new CloudRuntimeException(errMsg); - } + VMSnapshotStrategy strategy = findVMSnapshotStrategy(vmSnapshotVo); + strategy.revertVMSnapshot(vmSnapshotVo); + return userVm; } catch (Exception e) { - if(e instanceof AgentUnavailableException){ - try { - vmSnapshotStateTransitTo(vmSnapshotVo, VMSnapshot.Event.OperationFailed); - } catch (NoTransitionException e1) { - s_logger.error("Cannot set vm snapshot state due to: " + e1.getMessage()); - } - } - // for other exceptions, do not change VM snapshot state, leave it for snapshotSync - String errMsg = "revert vm: " + userVm.getInstanceName() + " to snapshot " + vmSnapshotVo.getName() + " failed due to " + e.getMessage(); - s_logger.error(errMsg); - throw new CloudRuntimeException(e.getMessage()); + s_logger.debug("Failed to revert vmsnapshot: " + vmSnapshotId, e); + return null; } - return userVm; } - @Override public VMSnapshot getVMSnapshotById(Long id) { VMSnapshotVO vmSnapshot = _vmSnapshotDao.findById(id); return vmSnapshot; } - protected Long pickRunningHost(Long vmId) { - UserVmVO vm = _userVMDao.findById(vmId); - // use VM's host if VM is running - if(vm.getState() == State.Running) - return vm.getHostId(); - - // check if lastHostId is available - if(vm.getLastHostId() != null){ - HostVO lastHost = _hostDao.findById(vm.getLastHostId()); - if(lastHost.getStatus() == com.cloud.host.Status.Up && !lastHost.isInMaintenanceStates()) - return lastHost.getId(); - } - - List listVolumes = _volumeDao.findByInstance(vmId); - if (listVolumes == null || listVolumes.size() == 0) { - throw new InvalidParameterValueException("vmInstance has no volumes"); - } - VolumeVO volume = listVolumes.get(0); - Long poolId = volume.getPoolId(); - if (poolId == null) { - throw new InvalidParameterValueException("pool id is not found"); - } - StoragePoolVO storagePool = _storagePoolDao.findById(poolId); - if (storagePool == null) { - throw new InvalidParameterValueException("storage pool is not found"); - } - List listHost = _hostDao.listAllUpAndEnabledNonHAHosts(Host.Type.Routing, storagePool.getClusterId(), storagePool.getPodId(), - storagePool.getDataCenterId(), null); - if (listHost == null || listHost.size() == 0) { - throw new InvalidParameterValueException("no host in up state is found"); - } - return listHost.get(0).getId(); - } @Override public VirtualMachine getVMBySnapshotId(Long id) { @@ -851,7 +499,8 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana VMSnapshotVO target = _vmSnapshotDao.findById(snapshot.getId()); if(type != null && target.getType() != type) continue; - if (!deleteSnapshotInternal(target)) { + VMSnapshotStrategy strategy = findVMSnapshotStrategy(target); + if (!strategy.deleteVMSnapshot(target)) { result = false; break; } @@ -869,12 +518,13 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana List vmSnapshotsInExpungingStates = _vmSnapshotDao.listByInstanceId(vm.getId(), VMSnapshot.State.Expunging, VMSnapshot.State.Reverting, VMSnapshot.State.Creating); for (VMSnapshotVO vmSnapshotVO : vmSnapshotsInExpungingStates) { + VMSnapshotStrategy strategy = findVMSnapshotStrategy(vmSnapshotVO); if(vmSnapshotVO.getState() == VMSnapshot.State.Expunging){ - return deleteSnapshotInternal(vmSnapshotVO); + return strategy.deleteVMSnapshot(vmSnapshotVO); }else if(vmSnapshotVO.getState() == VMSnapshot.State.Creating){ - return createVmSnapshotInternal(userVm, vmSnapshotVO, hostId) != null; + return strategy.takeVMSnapshot(vmSnapshotVO) != null; }else if(vmSnapshotVO.getState() == VMSnapshot.State.Reverting){ - return revertInternal(userVm, vmSnapshotVO, hostId) != null; + return strategy.revertVMSnapshot(vmSnapshotVO); } } }catch (Exception e) { diff --git a/server/test/com/cloud/vm/snapshot/VMSnapshotManagerTest.java b/server/test/com/cloud/vm/snapshot/VMSnapshotManagerTest.java index 055b2b08984..da6e7af8e43 100644 --- a/server/test/com/cloud/vm/snapshot/VMSnapshotManagerTest.java +++ b/server/test/com/cloud/vm/snapshot/VMSnapshotManagerTest.java @@ -192,13 +192,6 @@ public class VMSnapshotManagerTest { when(vmMock.getState()).thenReturn(State.Running); _vmSnapshotMgr.allocVMSnapshot(TEST_VM_ID,"","",true); - when(_vmSnapshotDao.findCurrentSnapshotByVmId(anyLong())).thenReturn(null); - doReturn(new ArrayList()).when(_vmSnapshotMgr).getVolumeTOList(anyLong()); - doReturn(new CreateVMSnapshotAnswer(null,true,"")).when(_vmSnapshotMgr).sendToPool(anyLong(), any(CreateVMSnapshotCommand.class)); - doNothing().when(_vmSnapshotMgr).processAnswer(any(VMSnapshotVO.class), - any(UserVmVO.class), any(Answer.class), anyLong()); - doReturn(true).when(_vmSnapshotMgr).vmSnapshotStateTransitTo(any(VMSnapshotVO.class),any(VMSnapshot.Event.class)); - _vmSnapshotMgr.createVmSnapshotInternal(vmMock, mock(VMSnapshotVO.class), 5L); } } diff --git a/setup/db/db/schema-421to430.sql b/setup/db/db/schema-421to430.sql index 8e4aa939e03..d355104ea0d 100644 --- a/setup/db/db/schema-421to430.sql +++ b/setup/db/db/schema-421to430.sql @@ -39,6 +39,15 @@ ALTER TABLE `cloud`.`vm_instance` ADD COLUMN `power_state_update_count` INT DEFA ALTER TABLE `cloud`.`vm_instance` ADD COLUMN `power_host` bigint unsigned; ALTER TABLE `cloud`.`vm_instance` ADD CONSTRAINT `fk_vm_instance__power_host` FOREIGN KEY (`power_host`) REFERENCES `cloud`.`host`(`id`); +DROP TABLE IF EXISTS `cloud`.`vm_snapshot_details`; +CREATE TABLE `cloud`.`vm_snapshot_details` ( + `id` bigint unsigned UNIQUE NOT NULL, + `vm_snapshot_id` bigint unsigned NOT NULL, + `name` varchar(255) NOT NULL, + `value` varchar(255) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + CREATE TABLE `cloud`.`vm_work_job` ( `id` bigint unsigned UNIQUE NOT NULL, `step` char(32) NOT NULL COMMENT 'state',