CLOUDSTACK-684 Support VM Snapshot

This commit is contained in:
Mice Xia 2013-01-30 10:13:05 +08:00
parent 1f7eaf3d61
commit bb7cd90b61
56 changed files with 4393 additions and 86 deletions

View File

@ -0,0 +1,62 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.agent.api;
import java.util.List;
import com.cloud.agent.api.to.VolumeTO;
public class CreateVMSnapshotAnswer extends Answer {
private List<VolumeTO> volumeTOs;
private VMSnapshotTO vmSnapshotTo;
public List<VolumeTO> getVolumeTOs() {
return volumeTOs;
}
public void setVolumeTOs(List<VolumeTO> volumeTOs) {
this.volumeTOs = volumeTOs;
}
public VMSnapshotTO getVmSnapshotTo() {
return vmSnapshotTo;
}
public void setVmSnapshotTo(VMSnapshotTO vmSnapshotTo) {
this.vmSnapshotTo = vmSnapshotTo;
}
public CreateVMSnapshotAnswer() {
}
public CreateVMSnapshotAnswer(CreateVMSnapshotCommand cmd, boolean success,
String result) {
super(cmd, success, result);
}
public CreateVMSnapshotAnswer(CreateVMSnapshotCommand cmd,
VMSnapshotTO vmSnapshotTo, List<VolumeTO> volumeTOs) {
super(cmd, true, "");
this.vmSnapshotTo = vmSnapshotTo;
this.volumeTOs = volumeTOs;
}
}

View File

@ -0,0 +1,42 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.agent.api;
import java.util.List;
import com.cloud.agent.api.to.VolumeTO;
import com.cloud.vm.VirtualMachine;
public class CreateVMSnapshotCommand extends VMSnapshotBaseCommand {
public CreateVMSnapshotCommand(String vmName, VMSnapshotTO snapshot, List<VolumeTO> volumeTOs, String guestOSType, VirtualMachine.State vmState) {
super(vmName, snapshot, volumeTOs, guestOSType);
this.vmState = vmState;
}
private VirtualMachine.State vmState;
public VirtualMachine.State getVmState() {
return vmState;
}
public void setVmState(VirtualMachine.State vmState) {
this.vmState = vmState;
}
}

View File

@ -0,0 +1,54 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.agent.api;
import com.cloud.agent.api.to.VolumeTO;
public class CreateVolumeFromVMSnapshotAnswer extends Answer {
private String path;
private VolumeTO volumeTo;
public VolumeTO getVolumeTo() {
return volumeTo;
}
public CreateVolumeFromVMSnapshotAnswer(
CreateVolumeFromVMSnapshotCommand cmd, VolumeTO volumeTo) {
super(cmd, true, "");
this.volumeTo = volumeTo;
}
public String getPath() {
return path;
}
protected CreateVolumeFromVMSnapshotAnswer() {
}
public CreateVolumeFromVMSnapshotAnswer(
CreateVolumeFromVMSnapshotCommand cmd, String path) {
super(cmd, true, "");
this.path = path;
}
public CreateVolumeFromVMSnapshotAnswer(
CreateVolumeFromVMSnapshotCommand cmd, boolean result, String string) {
super(cmd, result, string);
}
}

View File

@ -0,0 +1,88 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.agent.api;
import com.cloud.agent.api.to.StorageFilerTO;
import com.cloud.vm.DiskProfile;
public class CreateVolumeFromVMSnapshotCommand extends Command {
protected String path;
protected String name;
protected Boolean fullClone;
protected String storagePoolUuid;
private StorageFilerTO pool;
private DiskProfile diskProfile;
private Long volumeId;
public DiskProfile getDskch() {
return diskProfile;
}
public String getPath() {
return path;
}
public Long getVolumeId() {
return volumeId;
}
protected CreateVolumeFromVMSnapshotCommand() {
}
public CreateVolumeFromVMSnapshotCommand(String path, String name,
Boolean fullClone, String storagePoolUuid) {
this.path = path;
this.name = name;
this.fullClone = fullClone;
this.storagePoolUuid = storagePoolUuid;
}
public CreateVolumeFromVMSnapshotCommand(String path, String name,
Boolean fullClone, String storagePoolUuid, StorageFilerTO pool,
DiskProfile diskProfile, Long volumeId) {
this.path = path;
this.name = name;
this.fullClone = fullClone;
this.storagePoolUuid = storagePoolUuid;
this.pool = pool;
this.diskProfile = diskProfile;
this.volumeId = volumeId;
}
@Override
public boolean executeInSequence() {
return false;
}
public String getName() {
return name;
}
public Boolean getFullClone() {
return fullClone;
}
public String getStoragePoolUuid() {
return storagePoolUuid;
}
public StorageFilerTO getPool() {
return pool;
}
}

View File

@ -0,0 +1,49 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.agent.api;
import java.util.List;
import com.cloud.agent.api.to.VolumeTO;
public class DeleteVMSnapshotAnswer extends Answer {
private List<VolumeTO> volumeTOs;
public DeleteVMSnapshotAnswer() {
}
public DeleteVMSnapshotAnswer(DeleteVMSnapshotCommand cmd, boolean result,
String message) {
super(cmd, result, message);
}
public DeleteVMSnapshotAnswer(DeleteVMSnapshotCommand cmd,
List<VolumeTO> volumeTOs) {
super(cmd, true, "");
this.volumeTOs = volumeTOs;
}
public List<VolumeTO> getVolumeTOs() {
return volumeTOs;
}
public void setVolumeTOs(List<VolumeTO> volumeTOs) {
this.volumeTOs = volumeTOs;
}
}

View File

@ -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.agent.api;
import java.util.List;
import com.cloud.agent.api.to.VolumeTO;
public class DeleteVMSnapshotCommand extends VMSnapshotBaseCommand {
public DeleteVMSnapshotCommand(String vmName, VMSnapshotTO snapshot, List<VolumeTO> volumeTOs, String guestOSType) {
super( vmName, snapshot, volumeTOs, guestOSType);
}
}

View File

@ -0,0 +1,63 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.agent.api;
import java.util.List;
import com.cloud.agent.api.to.VolumeTO;
import com.cloud.vm.VirtualMachine;
public class RevertToVMSnapshotAnswer extends Answer {
private List<VolumeTO> volumeTOs;
private VirtualMachine.State vmState;
public RevertToVMSnapshotAnswer(RevertToVMSnapshotCommand cmd, boolean result,
String message) {
super(cmd, result, message);
}
public RevertToVMSnapshotAnswer() {
super();
}
public RevertToVMSnapshotAnswer(RevertToVMSnapshotCommand cmd,
List<VolumeTO> volumeTOs,
VirtualMachine.State vmState) {
super(cmd, true, "");
this.volumeTOs = volumeTOs;
this.vmState = vmState;
}
public VirtualMachine.State getVmState() {
return vmState;
}
public List<VolumeTO> getVolumeTOs() {
return volumeTOs;
}
public void setVolumeTOs(List<VolumeTO> volumeTOs) {
this.volumeTOs = volumeTOs;
}
public void setVmState(VirtualMachine.State vmState) {
this.vmState = vmState;
}
}

View File

@ -0,0 +1,29 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.agent.api;
import java.util.List;
import com.cloud.agent.api.to.VolumeTO;
public class RevertToVMSnapshotCommand extends VMSnapshotBaseCommand {
public RevertToVMSnapshotCommand(String vmName, VMSnapshotTO snapshot, List<VolumeTO> volumeTOs, String guestOSType) {
super(vmName, snapshot, volumeTOs, guestOSType);
}
}

View File

@ -0,0 +1,74 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.agent.api;
import java.util.List;
import com.cloud.agent.api.to.VolumeTO;
public class VMSnapshotBaseCommand extends Command{
protected List<VolumeTO> volumeTOs;
protected VMSnapshotTO target;
protected String vmName;
protected String guestOSType;
public VMSnapshotBaseCommand(String vmName, VMSnapshotTO snapshot, List<VolumeTO> volumeTOs, String guestOSType) {
this.vmName = vmName;
this.target = snapshot;
this.volumeTOs = volumeTOs;
this.guestOSType = guestOSType;
}
public List<VolumeTO> getVolumeTOs() {
return volumeTOs;
}
public void setVolumeTOs(List<VolumeTO> volumeTOs) {
this.volumeTOs = volumeTOs;
}
public VMSnapshotTO getTarget() {
return target;
}
public void setTarget(VMSnapshotTO target) {
this.target = target;
}
public String getVmName() {
return vmName;
}
public void setVmName(String vmName) {
this.vmName = vmName;
}
@Override
public boolean executeInSequence() {
return false;
}
public String getGuestOSType() {
return guestOSType;
}
public void setGuestOSType(String guestOSType) {
this.guestOSType = guestOSType;
}
}

View File

@ -0,0 +1,90 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.agent.api;
import com.cloud.vm.snapshot.VMSnapshot;
public class VMSnapshotTO {
private Long id;
private String snapshotName;
private VMSnapshot.Type type;
private Long createTime;
private Boolean current;
private String description;
private VMSnapshotTO parent;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public VMSnapshotTO(Long id, String snapshotName,
VMSnapshot.Type type, Long createTime,
String description, Boolean current, VMSnapshotTO parent) {
super();
this.id = id;
this.snapshotName = snapshotName;
this.type = type;
this.createTime = createTime;
this.current = current;
this.description = description;
this.parent = parent;
}
public VMSnapshotTO() {
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Boolean getCurrent() {
return current;
}
public void setCurrent(Boolean current) {
this.current = current;
}
public Long getCreateTime() {
return createTime;
}
public void setCreateTime(Long createTime) {
this.createTime = createTime;
}
public VMSnapshot.Type getType() {
return type;
}
public void setType(VMSnapshot.Type type) {
this.type = type;
}
public String getSnapshotName() {
return snapshotName;
}
public void setSnapshotName(String snapshotName) {
this.snapshotName = snapshotName;
}
public VMSnapshotTO getParent() {
return parent;
}
public void setParent(VMSnapshotTO parent) {
this.parent = parent;
}
}

View File

@ -123,6 +123,10 @@ public class VolumeTO implements InternalIdentity {
public String getOsType() {
return guestOsType;
}
public void setPath(String path){
this.path = path;
}
@Override
public String toString() {

View File

@ -291,6 +291,11 @@ public class EventTypes {
// tag related events
public static final String EVENT_TAGS_CREATE = "CREATE_TAGS";
public static final String EVENT_TAGS_DELETE = "DELETE_TAGS";
// vm snapshot events
public static final String EVENT_VM_SNAPSHOT_CREATE = "VMSNAPSHOT.CREATE";
public static final String EVENT_VM_SNAPSHOT_DELETE = "VMSNAPSHOT.DELETE";
public static final String EVENT_VM_SNAPSHOT_REVERT = "VMSNAPSHOT.REVERT";
// external network device events
public static final String EVENT_EXTERNAL_NVP_CONTROLLER_ADD = "PHYSICAL.NVPCONTROLLER.ADD";

View File

@ -37,7 +37,8 @@ public interface ResourceTag extends ControlledEntity, Identity, InternalIdentit
Project,
Vpc,
NetworkACL,
StaticRoute
StaticRoute,
VMSnapshot
}
/**

View File

@ -42,7 +42,11 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, Identity, I
Migrating(true, "VM is being migrated. host id holds to from host"),
Error(false, "VM is in error"),
Unknown(false, "VM state is unknown."),
Shutdowned(false, "VM is shutdowned from inside");
Shutdowned(false, "VM is shutdowned from inside"),
RunningSnapshotting(true, "VM is taking a snapshot in running state"),
StoppedSnapshotting(true, "VM is taking a snapshot in stopped state"),
RevertingToRunning(true, "VM is reverting to snapshot"),
RevertingToStopped(true, "VM is reverting to snapshot");
private final boolean _transitional;
String _description;
@ -110,8 +114,24 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, Identity, I
s_fsm.addTransition(State.Expunging, VirtualMachine.Event.ExpungeOperation, State.Expunging);
s_fsm.addTransition(State.Error, VirtualMachine.Event.DestroyRequested, State.Expunging);
s_fsm.addTransition(State.Error, VirtualMachine.Event.ExpungeOperation, State.Expunging);
}
s_fsm.addTransition(State.Running, VirtualMachine.Event.SnapshotRequested, State.RunningSnapshotting);
s_fsm.addTransition(State.Stopped, VirtualMachine.Event.SnapshotRequested, State.StoppedSnapshotting);
s_fsm.addTransition(State.RunningSnapshotting, VirtualMachine.Event.OperationSucceeded, State.Running);
s_fsm.addTransition(State.StoppedSnapshotting, VirtualMachine.Event.OperationSucceeded, State.Stopped);
s_fsm.addTransition(State.RunningSnapshotting, VirtualMachine.Event.OperationFailed, State.Running);
s_fsm.addTransition(State.StoppedSnapshotting, VirtualMachine.Event.OperationFailed, State.Stopped);
s_fsm.addTransition(State.Running, VirtualMachine.Event.RevertRequested, State.RevertingToRunning);
s_fsm.addTransition(State.Stopped, VirtualMachine.Event.RevertRequested, State.RevertingToStopped);
s_fsm.addTransition(State.RevertingToRunning, VirtualMachine.Event.OperationFailed, State.Running);
s_fsm.addTransition(State.RevertingToStopped, VirtualMachine.Event.OperationFailed, State.Stopped);
s_fsm.addTransition(State.RevertingToRunning, VirtualMachine.Event.OperationSucceeded, State.Running);
s_fsm.addTransition(State.RevertingToStopped, VirtualMachine.Event.OperationSucceeded, State.Stopped);
}
public static boolean isVmStarted(State oldState, Event e, State newState) {
if (oldState == State.Starting && newState == State.Running) {
return true;
@ -173,7 +193,9 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, Identity, I
OperationFailedToError,
OperationRetry,
AgentReportShutdowned,
AgentReportMigrated
AgentReportMigrated,
RevertRequested,
SnapshotRequested
};
public enum Type {

View File

@ -0,0 +1,109 @@
// 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 java.util.Date;
import org.apache.cloudstack.api.Identity;
import org.apache.cloudstack.api.InternalIdentity;
import org.apache.cloudstack.acl.ControlledEntity;
import com.cloud.utils.fsm.StateMachine2;
import com.cloud.utils.fsm.StateObject;
public interface VMSnapshot extends ControlledEntity, Identity, InternalIdentity,StateObject<VMSnapshot.State> {
enum State {
Allocated("The VM snapshot is allocated but has not been created yet."),
Creating("The VM snapshot is being created."),
Ready("The VM snapshot is ready to be used."),
Reverting("The VM snapshot is being used to revert"),
Expunging("The volume is being expunging"),
Removed("The volume is destroyed, and can't be recovered."),
Error ("The volume is in error state, and can't be recovered");
String _description;
private State(String description) {
_description = description;
}
public static StateMachine2<State, Event, VMSnapshot> getStateMachine() {
return s_fsm;
}
public String getDescription() {
return _description;
}
private final static StateMachine2<State, Event, VMSnapshot> s_fsm = new StateMachine2<State, Event, VMSnapshot>();
static {
s_fsm.addTransition(Allocated, Event.CreateRequested, Creating);
s_fsm.addTransition(Creating, Event.OperationSucceeded, Ready);
s_fsm.addTransition(Creating, Event.OperationFailed, Error);
s_fsm.addTransition(Ready, Event.RevertRequested, Reverting);
s_fsm.addTransition(Reverting, Event.OperationSucceeded, Ready);
s_fsm.addTransition(Reverting, Event.OperationFailed, Ready);
s_fsm.addTransition(Ready, Event.ExpungeRequested, Expunging);
s_fsm.addTransition(Error, Event.ExpungeRequested, Expunging);
s_fsm.addTransition(Expunging, Event.OperationSucceeded, Removed);
}
}
enum Type{
Disk, DiskAndMemory
}
enum Event {
CreateRequested,
OperationFailed,
OperationSucceeded,
RevertRequested,
ExpungeRequested,
}
long getId();
public String getName();
public Long getVmId();
public State getState();
public Date getCreated();
public String getDescription();
public String getDisplayName();
public Long getParent();
public Boolean getCurrent();
public Type getType();
public long getUpdatedCount();
public void incrUpdatedCount();
public Date getUpdated();
public Date getRemoved();
public long getAccountId();
}

View File

@ -0,0 +1,51 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.vm.snapshot;
import java.util.List;
import org.apache.cloudstack.api.command.user.vmsnapshot.CreateVMSnapshotCmd;
import org.apache.cloudstack.api.command.user.vmsnapshot.DeleteVMSnapshotCmd;
import org.apache.cloudstack.api.command.user.vmsnapshot.ListVMSnapshotCmd;
import org.apache.cloudstack.api.command.user.vmsnapshot.RevertToSnapshotCmd;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InsufficientServerCapacityException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.uservm.UserVm;
import com.cloud.vm.VirtualMachine;
public interface VMSnapshotService {
List<? extends VMSnapshot> listVMSnapshots(ListVMSnapshotCmd cmd);
VMSnapshot getVMSnapshotById(long id);
VMSnapshot creatVMSnapshot(CreateVMSnapshotCmd cmd);
VMSnapshot allocVMSnapshot(CreateVMSnapshotCmd cmd)
throws ResourceAllocationException;
boolean deleteVMSnapshot(DeleteVMSnapshotCmd cmd);
UserVm revertToSnapshot(RevertToSnapshotCmd cmd) throws InsufficientServerCapacityException, InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException;
VirtualMachine getVMBySnapshotId(Long id);
}

View File

@ -428,6 +428,11 @@ public class ApiConstants {
public static final String COUNTERPARAM_LIST = "counterparam";
public static final String AUTOSCALE_USER_ID = "autoscaleuserid";
public static final String BAREMETAL_DISCOVER_NAME = "baremetaldiscovername";
public static final String VM_SNAPSHOT_DESCRIPTION = "description";
public static final String VM_SNAPSHOT_DISPLAYNAME = "name";
public static final String VM_SNAPSHOT_ID = "vmsnapshotid";
public static final String VM_SNAPSHOT_DISK_IDS = "vmsnapshotdiskids";
public static final String VM_SNAPSHOT_MEMORY = "snapshotmemory";
public enum HostDetails {
all, capacity, events, stats, min;

View File

@ -67,6 +67,7 @@ import com.cloud.utils.Pair;
import com.cloud.utils.component.ComponentLocator;
import com.cloud.vm.BareMetalVmService;
import com.cloud.vm.UserVmService;
import com.cloud.vm.snapshot.VMSnapshotService;
public abstract class BaseCmd {
private static final Logger s_logger = Logger.getLogger(BaseCmd.class.getName());
@ -122,6 +123,7 @@ public abstract class BaseCmd {
public static VpcService _vpcService;
public static NetworkACLService _networkACLService;
public static Site2SiteVpnService _s2sVpnService;
public static VMSnapshotService _vmSnapshotService;
public static QueryService _queryService;
@ -157,6 +159,7 @@ public abstract class BaseCmd {
_networkACLService = locator.getManager(NetworkACLService.class);
_s2sVpnService = locator.getManager(Site2SiteVpnService.class);
_queryService = locator.getManager(QueryService.class);
_vmSnapshotService = locator.getManager(VMSnapshotService.class);
}
public abstract void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException;

View File

@ -64,6 +64,7 @@ import org.apache.cloudstack.api.response.RemoteAccessVpnResponse;
import org.apache.cloudstack.api.response.ResourceCountResponse;
import org.apache.cloudstack.api.response.ResourceLimitResponse;
import org.apache.cloudstack.api.response.ResourceTagResponse;
import org.apache.cloudstack.api.response.S3Response;
import org.apache.cloudstack.api.response.SecurityGroupResponse;
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
import org.apache.cloudstack.api.response.ServiceResponse;
@ -84,6 +85,7 @@ import org.apache.cloudstack.api.response.TemplateResponse;
import org.apache.cloudstack.api.response.TrafficTypeResponse;
import org.apache.cloudstack.api.response.UserResponse;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.api.response.VMSnapshotResponse;
import org.apache.cloudstack.api.response.VirtualRouterProviderResponse;
import org.apache.cloudstack.api.response.VlanIpRangeResponse;
import org.apache.cloudstack.api.response.VolumeResponse;
@ -92,8 +94,6 @@ import org.apache.cloudstack.api.response.VpcResponse;
import org.apache.cloudstack.api.response.VpnUsersResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.cloudstack.api.response.S3Response;
import com.cloud.async.AsyncJob;
import com.cloud.capacity.Capacity;
import com.cloud.configuration.Configuration;
@ -159,6 +159,7 @@ import com.cloud.user.UserAccount;
import com.cloud.uservm.UserVm;
import com.cloud.vm.InstanceGroup;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.snapshot.VMSnapshot;
public interface ResponseGenerator {
UserResponse createUserResponse(UserAccount user);
@ -372,4 +373,6 @@ public interface ResponseGenerator {
GuestOSResponse createGuestOSResponse(GuestOS os);
SnapshotScheduleResponse createSnapshotScheduleResponse(SnapshotSchedule sched);
VMSnapshotResponse createVMSnapshotResponse(VMSnapshot vmSnapshot);
}

View File

@ -0,0 +1,125 @@
// 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.api.command.user.vmsnapshot;
import java.util.logging.Logger;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseAsyncCreateCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.api.response.VMSnapshotResponse;
import com.cloud.event.EventTypes;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.user.UserContext;
import com.cloud.uservm.UserVm;
import com.cloud.vm.snapshot.VMSnapshot;
@APICommand(name = "createVMSnapshot", description = "Creates snapshot for a vm.", responseObject = VMSnapshotResponse.class, since="4.1.0")
public class CreateVMSnapshotCmd extends BaseAsyncCreateCmd {
public static final Logger s_logger = Logger
.getLogger(CreateVMSnapshotCmd.class.getName());
private static final String s_name = "createvmsnapshotresponse";
@Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, required = true, entityType=UserVmResponse.class, description = "The ID of the vm")
private Long vmId;
@Parameter(name = ApiConstants.VM_SNAPSHOT_DESCRIPTION, type = CommandType.STRING, required = false, description = "The discription of the snapshot")
private String description;
@Parameter(name = ApiConstants.VM_SNAPSHOT_DISPLAYNAME, type = CommandType.STRING, required = false, description = "The display name of the snapshot")
private String displayName;
@Parameter(name = ApiConstants.VM_SNAPSHOT_MEMORY, type = CommandType.BOOLEAN, required = false, description = "snapshot memory if true")
private Boolean snapshotMemory;
public Boolean snapshotMemory() {
if (snapshotMemory == null) {
return false;
} else {
return snapshotMemory;
}
}
public String getDisplayName() {
return displayName;
}
public String getDescription() {
return description;
}
public Long getVmId() {
return vmId;
}
@Override
public void create() throws ResourceAllocationException {
VMSnapshot vmsnapshot = _vmSnapshotService.allocVMSnapshot(this);
if (vmsnapshot != null) {
this.setEntityId(vmsnapshot.getId());
} else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR,
"Failed to create vm snapshot");
}
}
@Override
public String getEventDescription() {
return "creating snapshot for vm: " + getVmId();
}
@Override
public String getEventType() {
return EventTypes.EVENT_VM_SNAPSHOT_CREATE;
}
@Override
public void execute() {
UserContext.current().setEventDetails("VM Id: " + getVmId());
VMSnapshot result = _vmSnapshotService.creatVMSnapshot(this);
if (result != null) {
VMSnapshotResponse response = _responseGenerator
.createVMSnapshotResponse(result);
response.setResponseName(getCommandName());
this.setResponseObject(response);
} else {
throw new ServerApiException(
ApiErrorCode.INTERNAL_ERROR,
"Failed to create vm snapshot due to an internal error creating snapshot for vm "
+ getVmId());
}
}
@Override
public String getCommandName() {
return s_name;
}
@Override
public long getEntityOwnerId() {
UserVm userVM = _userVmService.getUserVm(vmId);
return userVM.getAccountId();
}
}

View File

@ -0,0 +1,99 @@
// 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.api.command.user.vmsnapshot;
import org.apache.log4j.Logger;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseAsyncCmd;
import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.BaseCmd.CommandType;
import org.apache.cloudstack.api.response.SuccessResponse;
import org.apache.cloudstack.api.response.VolumeResponse;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.VMSnapshotResponse;
import com.cloud.event.EventTypes;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.storage.Snapshot;
import com.cloud.storage.Volume;
import com.cloud.user.Account;
import com.cloud.user.UserContext;
import com.cloud.uservm.UserVm;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.snapshot.VMSnapshot;
//import com.cloud.api.ApiDBUtils;
@APICommand(name="deleteVMSnapshot", description = "Deletes a vmsnapshot.", responseObject = SuccessResponse.class)
public class DeleteVMSnapshotCmd extends BaseAsyncCmd {
public static final Logger s_logger = Logger
.getLogger(DeleteVMSnapshotCmd.class.getName());
private static final String s_name = "deletevmsnapshotresponse";
@Parameter(name=ApiConstants.VM_SNAPSHOT_ID, type=CommandType.UUID, entityType=VMSnapshotResponse.class,
required=true, description="The ID of the VM snapshot")
private Long id;
public Long getId() {
return id;
}
@Override
public String getCommandName() {
return s_name;
}
public static String getResultObjectName() {
return "vm_snapshots";
}
@Override
public long getEntityOwnerId() {
VMSnapshot vmSnapshot = _entityMgr.findById(VMSnapshot.class, getId());
if (vmSnapshot != null) {
return vmSnapshot.getAccountId();
}
return Account.ACCOUNT_ID_SYSTEM;
}
@Override
public void execute() {
UserContext.current().setEventDetails("vmsnapshot id: " + getId());
boolean result = _vmSnapshotService.deleteVMSnapshot(this);
if (result) {
SuccessResponse response = new SuccessResponse(getCommandName());
this.setResponseObject(response);
} else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete vm snapshot");
}
}
@Override
public String getEventDescription() {
return "Delete VM snapshot: " + getId();
}
@Override
public String getEventType() {
return EventTypes.EVENT_VM_SNAPSHOT_DELETE;
}
}

View File

@ -0,0 +1,105 @@
// 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.api.command.user.vmsnapshot;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseListTaggedResourcesCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.command.admin.host.ListHostsCmd;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.api.response.VMSnapshotResponse;
import com.cloud.vm.snapshot.VMSnapshot;
@APICommand(name="listVMSnapshot", description = "List virtual machine snapshot by conditions", responseObject = VMSnapshotResponse.class, since = "4.1.0")
public class ListVMSnapshotCmd extends BaseListTaggedResourcesCmd {
public static final Logger s_logger = Logger.getLogger(ListHostsCmd.class
.getName());
private static final String s_name = "listvmsnapshotresponse";
// ///////////////////////////////////////////////////
// ////////////// API parameters /////////////////////
// ///////////////////////////////////////////////////
@Parameter(name=ApiConstants.VM_SNAPSHOT_ID, type=CommandType.UUID, entityType=VMSnapshotResponse.class,
description="The ID of the VM snapshot")
private Long id;
@Parameter(name=ApiConstants.STATE, type=CommandType.STRING, description="state of the virtual machine snapshot")
private String state;
@Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType=UserVmResponse.class, description = "the ID of the vm")
private Long vmId;
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "lists snapshot by snapshot name or display name")
private String vmSnapshotName;
// ///////////////////////////////////////////////////
// ///////////////// Accessors ///////////////////////
// ///////////////////////////////////////////////////
public String getState() {
return state;
}
public String getVmSnapshotName() {
return vmSnapshotName;
}
public Long getVmId() {
return vmId;
}
public Long getId() {
return id;
}
// ///////////////////////////////////////////////////
// ///////////// API Implementation///////////////////
// ///////////////////////////////////////////////////
@Override
public void execute() {
List<? extends VMSnapshot> result = _vmSnapshotService
.listVMSnapshots(this);
ListResponse<VMSnapshotResponse> response = new ListResponse<VMSnapshotResponse>();
List<VMSnapshotResponse> snapshotResponses = new ArrayList<VMSnapshotResponse>();
for (VMSnapshot r : result) {
VMSnapshotResponse vmSnapshotResponse = _responseGenerator
.createVMSnapshotResponse(r);
vmSnapshotResponse.setObjectName("vmSnapshot");
snapshotResponses.add(vmSnapshotResponse);
}
response.setResponses(snapshotResponses);
response.setResponseName(getCommandName());
this.setResponseObject(response);
}
@Override
public String getCommandName() {
return s_name;
}
}

View File

@ -0,0 +1,98 @@
// 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.api.command.user.vmsnapshot;
import java.util.logging.Logger;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.BaseAsyncCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.api.response.VMSnapshotResponse;
import com.cloud.event.EventTypes;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.user.UserContext;
import com.cloud.uservm.UserVm;
import com.cloud.vm.snapshot.VMSnapshot;
@APICommand(name = "revertToSnapshot",description = "Revert VM from a vmsnapshot.", responseObject = UserVmResponse.class)
public class RevertToSnapshotCmd extends BaseAsyncCmd {
public static final Logger s_logger = Logger
.getLogger(RevertToSnapshotCmd.class.getName());
private static final String s_name = "reverttosnapshotresponse";
@Parameter(name = ApiConstants.VM_SNAPSHOT_ID, type = CommandType.UUID, required = true,entityType=VMSnapshotResponse.class,description = "The ID of the vm snapshot")
private Long vmSnapShotId;
public Long getVmSnapShotId() {
return vmSnapShotId;
}
@Override
public String getCommandName() {
return s_name;
}
public static String getResultObjectName() {
return "vm_snapshots";
}
@Override
public long getEntityOwnerId() {
VMSnapshot vmSnapshot = _vmSnapshotService.getVMSnapshotById(getVmSnapShotId());
if (vmSnapshot == null) {
throw new InvalidParameterValueException(
"Unable to find the snapshot by id=" + getVmSnapShotId());
}
UserVm userVM = _userVmService.getUserVm(vmSnapshot.getVmId());
return userVM.getAccountId();
}
@Override
public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ResourceAllocationException, ConcurrentOperationException {
UserContext.current().setEventDetails(
"vmsnapshot id: " + getVmSnapShotId());
UserVm result = _vmSnapshotService.revertToSnapshot(this);
if (result != null) {
UserVmResponse response = _responseGenerator.createUserVmResponse(
"virtualmachine", result).get(0);
response.setResponseName(getCommandName());
this.setResponseObject(response);
} else {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR,"Failed to revert vm snapshot");
}
}
@Override
public String getEventDescription() {
return "Revert from VM snapshot: " + getVmSnapShotId();
}
@Override
public String getEventType() {
return EventTypes.EVENT_VM_SNAPSHOT_REVERT;
}
}

View File

@ -0,0 +1,220 @@
// 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.api.response;
import java.util.Date;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import org.apache.cloudstack.api.EntityReference;
import com.cloud.serializer.Param;
import com.cloud.vm.snapshot.VMSnapshot;
import com.google.gson.annotations.SerializedName;
@EntityReference(value=VMSnapshot.class)
public class VMSnapshotResponse extends BaseResponse implements ControlledEntityResponse{
@SerializedName(ApiConstants.ID)
@Param(description = "the ID of the vm snapshot")
private String id;
@SerializedName(ApiConstants.NAME)
@Param(description = "the name of the vm snapshot")
private String name;
@SerializedName(ApiConstants.STATE)
@Param(description = "the state of the vm snapshot")
private VMSnapshot.State state;
@SerializedName(ApiConstants.DESCRIPTION)
@Param(description = "the description of the vm snapshot")
private String description;
@SerializedName(ApiConstants.DISPLAY_NAME)
@Param(description = "the display name of the vm snapshot")
private String displayName;
@SerializedName(ApiConstants.ZONE_ID)
@Param(description = "the Zone ID of the vm snapshot")
private String zoneId;
@SerializedName(ApiConstants.VIRTUAL_MACHINE_ID)
@Param(description = "the vm ID of the vm snapshot")
private String virtualMachineid;
@SerializedName("parent")
@Param(description = "the parent ID of the vm snapshot")
private String parent;
@SerializedName("parentName")
@Param(description = "the parent displayName of the vm snapshot")
private String parentName;
@SerializedName("current")
@Param(description = "indiates if this is current snapshot")
private Boolean current;
@SerializedName("type")
@Param(description = "VM Snapshot type")
private String type;
@SerializedName(ApiConstants.CREATED)
@Param(description = "the create date of the vm snapshot")
private Date created;
@SerializedName(ApiConstants.ACCOUNT)
@Param(description = "the account associated with the disk volume")
private String accountName;
@SerializedName(ApiConstants.PROJECT_ID) @Param(description="the project id of the vpn")
private String projectId;
@SerializedName(ApiConstants.PROJECT) @Param(description="the project name of the vpn")
private String projectName;
@SerializedName(ApiConstants.DOMAIN_ID)
@Param(description = "the ID of the domain associated with the disk volume")
private String domainId;
@SerializedName(ApiConstants.DOMAIN)
@Param(description = "the domain associated with the disk volume")
private String domainName;
@Override
public String getObjectId() {
return getId();
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getZoneId() {
return zoneId;
}
public void setZoneId(String zoneId) {
this.zoneId = zoneId;
}
public String getVirtualMachineid() {
return virtualMachineid;
}
public void setVirtualMachineid(String virtualMachineid) {
this.virtualMachineid = virtualMachineid;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setState(VMSnapshot.State state) {
this.state = state;
}
public VMSnapshot.State getState() {
return state;
}
public Boolean getCurrent() {
return current;
}
public void setCurrent(Boolean current) {
this.current = current;
}
public void setParentName(String parentName) {
this.parentName = parentName;
}
public String getParentName() {
return parentName;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public void setAccountName(String accountName) {
this.accountName = accountName;
}
@Override
public void setProjectId(String projectId) {
this.projectId = projectId;
}
@Override
public void setProjectName(String projectName) {
this.projectName = projectName;
}
@Override
public void setDomainId(String domainId) {
this.domainId = domainId;
}
@Override
public void setDomainName(String domainName) {
this.domainName = domainName;
}
}

View File

@ -1408,6 +1408,20 @@ label.zone.step.4.title=Step 4: <strong>Add an IP range</strong>
label.zone.wide=Zone-Wide
label.zone=Zone
#VM snapshot label
label.vmsnapshot=VM Snapshots
label.vmsnapshot.type=Type
label.vmsnapshot.parentname=Parent
label.vmsnapshot.current=isCurrent
label.vmsnapshot.memory=Snapshot memory
message.action.vmsnapshot.delete=Please confirm that you want to delete this VM snapshot.
label.action.vmsnapshot.delete=Delete VM snapshot
label.action.vmsnapshot.revert=Revert to VM snapshot
message.action.vmsnapshot.revert=Revert VM snapshot
label.action.vmsnapshot.create=Take VM Snapshot
#Messages
message.acquire.public.ip=Please select a zone from which you want to acquire your new IP from.
message.action.cancel.maintenance.mode=Please confirm that you want to cancel this maintenance.

View File

@ -514,3 +514,9 @@ listApis=15
getApiLimit=15
resetApiLimit=1
### VM Snapshot commands
listVMSnapshot=15
createVMSnapshot=15
deleteVMSnapshot=15
revertToSnapshot=15

View File

@ -0,0 +1,224 @@
// 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 java.util.Date;
import java.util.UUID;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import com.cloud.utils.db.GenericDao;
@Entity
@Table(name = "vm_snapshots")
public class VMSnapshotVO implements VMSnapshot {
@Id
@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;
@Column(name = "uuid")
String uuid = UUID.randomUUID().toString();
@Column(name = "name")
String name;
@Column(name = "display_name")
String displayName;
@Column(name = "description")
String description;
@Column(name = "vm_id")
long vmId;
@Column(name = "account_id")
long accountId;
@Column(name = "domain_id")
long domainId;
@Column(name = "vm_snapshot_type")
@Enumerated(EnumType.STRING)
VMSnapshot.Type type;
@Column(name = "state", updatable = true, nullable = false)
@Enumerated(value = EnumType.STRING)
private State state;
@Column(name = GenericDao.CREATED_COLUMN)
Date created;
@Column(name = GenericDao.REMOVED_COLUMN)
Date removed;
@Column(name = "current")
Boolean current;
@Column(name = "parent")
Long parent;
@Column(name = "updated")
@Temporal(value = TemporalType.TIMESTAMP)
Date updated;
@Column(name="update_count", updatable = true, nullable=false)
protected long updatedCount;
public Long getParent() {
return parent;
}
public void setParent(Long parent) {
this.parent = parent;
}
public VMSnapshotVO() {
}
public Date getRemoved() {
return removed;
}
public VMSnapshotVO(Long accountId, Long domainId, Long vmId,
String description, String vmSnapshotName, String vsDisplayName,
Long serviceOfferingId, Type type, Boolean current) {
this.accountId = accountId;
this.domainId = domainId;
this.vmId = vmId;
this.state = State.Allocated;
this.description = description;
this.name = vmSnapshotName;
this.displayName = vsDisplayName;
this.type = type;
this.current = current;
}
public String getDescription() {
return description;
}
@Override
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
@Override
public long getId() {
return id;
}
@Override
public Long getVmId() {
return vmId;
}
public void setVmId(Long vmId) {
this.vmId = vmId;
}
@Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
@Override
public String getUuid() {
return uuid;
}
@Override
public long getAccountId() {
return accountId;
}
@Override
public long getDomainId() {
return domainId;
}
@Override
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public Boolean getCurrent() {
return current;
}
public void setCurrent(Boolean current) {
this.current = current;
}
@Override
public long getUpdatedCount() {
return updatedCount;
}
@Override
public void incrUpdatedCount() {
this.updatedCount++;
}
@Override
public Date getUpdated() {
return updated;
}
@Override
public Type getType() {
return type;
}
public void setRemoved(Date removed) {
this.removed = removed;
}
}

View File

@ -20,15 +20,21 @@ import com.cloud.agent.api.Answer;
import com.cloud.agent.api.BackupSnapshotCommand;
import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
import com.cloud.agent.api.CreateVMSnapshotCommand;
import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
import com.cloud.agent.api.DeleteVMSnapshotCommand;
import com.cloud.agent.api.RevertToVMSnapshotCommand;
import com.cloud.agent.api.storage.CopyVolumeCommand;
import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
public interface VmwareStorageManager {
Answer execute(VmwareHostService hostService, PrimaryStorageDownloadCommand cmd);
Answer execute(VmwareHostService hostService, BackupSnapshotCommand cmd);
Answer execute(VmwareHostService hostService, CreatePrivateTemplateFromVolumeCommand cmd);
Answer execute(VmwareHostService hostService, CreatePrivateTemplateFromSnapshotCommand cmd);
Answer execute(VmwareHostService hostService, CopyVolumeCommand cmd);
Answer execute(VmwareHostService hostService, CreateVolumeFromSnapshotCommand cmd);
Answer execute(VmwareHostService hostService, BackupSnapshotCommand cmd);
Answer execute(VmwareHostService hostService, CreatePrivateTemplateFromVolumeCommand cmd);
Answer execute(VmwareHostService hostService, CreatePrivateTemplateFromSnapshotCommand cmd);
Answer execute(VmwareHostService hostService, CopyVolumeCommand cmd);
Answer execute(VmwareHostService hostService, CreateVolumeFromSnapshotCommand cmd);
Answer execute(VmwareHostService hostService, CreateVMSnapshotCommand cmd);
Answer execute(VmwareHostService hostService, DeleteVMSnapshotCommand cmd);
Answer execute(VmwareHostService hostService, RevertToVMSnapshotCommand cmd);
}

View File

@ -22,6 +22,7 @@ import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@ -32,18 +33,27 @@ import com.cloud.agent.api.BackupSnapshotAnswer;
import com.cloud.agent.api.BackupSnapshotCommand;
import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
import com.cloud.agent.api.CreateVMSnapshotAnswer;
import com.cloud.agent.api.CreateVMSnapshotCommand;
import com.cloud.agent.api.CreateVolumeFromSnapshotAnswer;
import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
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.storage.CopyVolumeAnswer;
import com.cloud.agent.api.storage.CopyVolumeCommand;
import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer;
import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer;
import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
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;
import com.cloud.hypervisor.vmware.mo.HostMO;
import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper;
import com.cloud.hypervisor.vmware.mo.TaskMO;
import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost;
import com.cloud.hypervisor.vmware.util.VmwareContext;
@ -57,7 +67,11 @@ import com.cloud.utils.Pair;
import com.cloud.utils.StringUtils;
import com.cloud.utils.Ternary;
import com.cloud.utils.script.Script;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.snapshot.VMSnapshot;
import com.vmware.vim25.ManagedObjectReference;
import com.vmware.vim25.TaskEvent;
import com.vmware.vim25.TaskInfo;
import com.vmware.vim25.VirtualDeviceConfigSpec;
import com.vmware.vim25.VirtualDeviceConfigSpecOperation;
import com.vmware.vim25.VirtualDisk;
@ -222,8 +236,12 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager {
}
} finally {
if(vmMo != null)
vmMo.removeAllSnapshots();
if(vmMo != null){
ManagedObjectReference snapshotMor = vmMo.getSnapshotMor(snapshotUuid);
if (snapshotMor != null){
vmMo.removeSnapshot(snapshotUuid, false);
}
}
try {
if (workerVm != null) {
@ -377,47 +395,47 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager {
}
@Override
public Answer execute(VmwareHostService hostService, CreateVolumeFromSnapshotCommand cmd) {
public Answer execute(VmwareHostService hostService, CreateVolumeFromSnapshotCommand cmd) {
String primaryStorageNameLabel = cmd.getPrimaryStoragePoolNameLabel();
Long accountId = cmd.getAccountId();
Long volumeId = cmd.getVolumeId();
String secondaryStorageUrl = cmd.getSecondaryStorageUrl();
String backedUpSnapshotUuid = cmd.getSnapshotUuid();
String primaryStorageNameLabel = cmd.getPrimaryStoragePoolNameLabel();
Long accountId = cmd.getAccountId();
Long volumeId = cmd.getVolumeId();
String secondaryStorageUrl = cmd.getSecondaryStorageUrl();
String backedUpSnapshotUuid = cmd.getSnapshotUuid();
String details = null;
boolean success = false;
String newVolumeName = UUID.randomUUID().toString().replaceAll("-", "");
String details = null;
boolean success = false;
String newVolumeName = UUID.randomUUID().toString().replaceAll("-", "");
VmwareContext context = hostService.getServiceContext(cmd);
try {
VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
ManagedObjectReference morPrimaryDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, primaryStorageNameLabel);
if (morPrimaryDs == null) {
String msg = "Unable to find datastore: " + primaryStorageNameLabel;
s_logger.error(msg);
throw new Exception(msg);
}
VmwareContext context = hostService.getServiceContext(cmd);
try {
VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
ManagedObjectReference morPrimaryDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost,
primaryStorageNameLabel);
if (morPrimaryDs == null) {
String msg = "Unable to find datastore: " + primaryStorageNameLabel;
s_logger.error(msg);
throw new Exception(msg);
}
DatastoreMO primaryDsMo = new DatastoreMO(hyperHost.getContext(), morPrimaryDs);
details = createVolumeFromSnapshot(hyperHost, primaryDsMo,
newVolumeName, accountId, volumeId, secondaryStorageUrl, backedUpSnapshotUuid);
if (details == null) {
success = true;
}
} catch (Throwable e) {
if (e instanceof RemoteException) {
hostService.invalidateServiceContext(context);
}
DatastoreMO primaryDsMo = new DatastoreMO(hyperHost.getContext(), morPrimaryDs);
details = createVolumeFromSnapshot(hyperHost, primaryDsMo,
newVolumeName, accountId, volumeId, secondaryStorageUrl, backedUpSnapshotUuid);
if (details == null) {
success = true;
}
} catch (Throwable e) {
if (e instanceof RemoteException) {
hostService.invalidateServiceContext(context);
}
s_logger.error("Unexpecpted exception ", e);
details = "CreateVolumeFromSnapshotCommand exception: " + StringUtils.getExceptionStackInfo(e);
}
s_logger.error("Unexpecpted exception ", e);
details = "CreateVolumeFromSnapshotCommand exception: " + StringUtils.getExceptionStackInfo(e);
}
return new CreateVolumeFromSnapshotAnswer(cmd, success, details, newVolumeName);
}
return new CreateVolumeFromSnapshotAnswer(cmd, success, details, newVolumeName);
}
// templateName: name in secondary storage
// templateUuid: will be used at hypervisor layer
private void copyTemplateFromSecondaryToPrimary(VmwareHypervisorHost hyperHost, DatastoreMO datastoreMo, String secondaryStorageUrl,
@ -881,4 +899,244 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager {
private static String getSnapshotRelativeDirInSecStorage(long accountId, long volumeId) {
return "snapshots/" + accountId + "/" + volumeId;
}
@Override
public CreateVMSnapshotAnswer execute(VmwareHostService hostService, CreateVMSnapshotCommand cmd) {
List<VolumeTO> volumeTOs = cmd.getVolumeTOs();
String vmName = cmd.getVmName();
String vmSnapshotName = cmd.getTarget().getSnapshotName();
String vmSnapshotDesc = cmd.getTarget().getDescription();
boolean snapshotMemory = cmd.getTarget().getType() == VMSnapshot.Type.DiskAndMemory;
VirtualMachineMO vmMo = null;
VmwareContext context = hostService.getServiceContext(cmd);
Map<String, String> mapNewDisk = new HashMap<String, String>();
try {
VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
// wait if there are already VM snapshot task running
ManagedObjectReference taskmgr = context.getServiceContent().getTaskManager();
ManagedObjectReference[] tasks = (ManagedObjectReference[]) context.getServiceUtil().getDynamicProperty(taskmgr, "recentTask");
for (ManagedObjectReference taskMor : tasks) {
TaskInfo info = (TaskInfo) (context.getServiceUtil().getDynamicProperty(taskMor, "info"));
if(info.getEntityName().equals(cmd.getVmName()) && info.getName().equalsIgnoreCase("CreateSnapshot_Task")){
s_logger.debug("There is already a VM snapshot task running, wait for it");
context.getServiceUtil().waitForTask(taskMor);
}
}
vmMo = hyperHost.findVmOnHyperHost(vmName);
if(vmMo == null)
vmMo = hyperHost.findVmOnPeerHyperHost(vmName);
if (vmMo == null) {
String msg = "Unable to find VM for CreateVMSnapshotCommand";
s_logger.debug(msg);
return new CreateVMSnapshotAnswer(cmd, false, msg);
} else {
if (vmMo.getSnapshotMor(vmSnapshotName) != null){
s_logger.debug("VM snapshot " + vmSnapshotName + " already exists");
}else if (!vmMo.createSnapshot(vmSnapshotName, vmSnapshotDesc, snapshotMemory, true)) {
return new CreateVMSnapshotAnswer(cmd, false,
"Unable to create snapshot due to esxi internal failed");
}
// find VM disk file path after creating snapshot
VirtualDisk[] vdisks = vmMo.getAllDiskDevice();
for (int i = 0; i < vdisks.length; i ++){
@SuppressWarnings("deprecation")
List<Pair<String, ManagedObjectReference>> vmdkFiles = vmMo.getDiskDatastorePathChain(vdisks[i], false);
for(Pair<String, ManagedObjectReference> fileItem : vmdkFiles) {
String vmdkName = fileItem.first().split(" ")[1];
if ( vmdkName.endsWith(".vmdk")){
vmdkName = vmdkName.substring(0, vmdkName.length() - (".vmdk").length());
}
String[] s = vmdkName.split("-");
mapNewDisk.put(s[0], vmdkName);
}
}
// update volume path using maps
for (VolumeTO volumeTO : volumeTOs) {
String parentUUID = volumeTO.getPath();
String[] s = parentUUID.split("-");
String key = s[0];
volumeTO.setPath(mapNewDisk.get(key));
}
return new CreateVMSnapshotAnswer(cmd, cmd.getTarget(), volumeTOs);
}
} catch (Exception e) {
String msg = e.getMessage();
s_logger.error("failed to create snapshot for vm:" + vmName + " due to " + msg);
try {
if (vmMo.getSnapshotMor(vmSnapshotName) != null) {
vmMo.removeSnapshot(vmSnapshotName, false);
}
} catch (Exception e1) {
}
return new CreateVMSnapshotAnswer(cmd, false, e.getMessage());
}
}
@Override
public DeleteVMSnapshotAnswer execute(VmwareHostService hostService, DeleteVMSnapshotCommand cmd) {
List<VolumeTO> listVolumeTo = cmd.getVolumeTOs();
VirtualMachineMO vmMo = null;
VmwareContext context = hostService.getServiceContext(cmd);
Map<String, String> mapNewDisk = new HashMap<String, String>();
String vmName = cmd.getVmName();
String vmSnapshotName = cmd.getTarget().getSnapshotName();
try {
VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
vmMo = hyperHost.findVmOnHyperHost(vmName);
if(vmMo == null)
vmMo = hyperHost.findVmOnPeerHyperHost(vmName);
if (vmMo == null) {
String msg = "Unable to find VM for RevertToVMSnapshotCommand";
s_logger.debug(msg);
return new DeleteVMSnapshotAnswer(cmd, false, msg);
} else {
if (vmMo.getSnapshotMor(vmSnapshotName) == null) {
s_logger.debug("can not find the snapshot " + vmSnapshotName + ", assume it is already removed");
} else {
if (!vmMo.removeSnapshot(vmSnapshotName, false)) {
String msg = "delete vm snapshot " + vmSnapshotName + " due to error occured in vmware";
s_logger.error(msg);
return new DeleteVMSnapshotAnswer(cmd, false, msg);
}
}
s_logger.debug("snapshot: " + vmSnapshotName + " is removed");
// after removed snapshot, the volumes' paths have been changed for the VM, needs to report new paths to manager
VirtualDisk[] vdisks = vmMo.getAllDiskDevice();
for (int i = 0; i < vdisks.length; i++) {
@SuppressWarnings("deprecation")
List<Pair<String, ManagedObjectReference>> vmdkFiles = vmMo.getDiskDatastorePathChain(vdisks[i], false);
for (Pair<String, ManagedObjectReference> fileItem : vmdkFiles) {
String vmdkName = fileItem.first().split(" ")[1];
if (vmdkName.endsWith(".vmdk")) {
vmdkName = vmdkName.substring(0, vmdkName.length() - (".vmdk").length());
}
String[] s = vmdkName.split("-");
mapNewDisk.put(s[0], vmdkName);
}
}
for (VolumeTO volumeTo : listVolumeTo) {
String key = null;
String parentUUID = volumeTo.getPath();
String[] s = parentUUID.split("-");
key = s[0];
volumeTo.setPath(mapNewDisk.get(key));
}
return new DeleteVMSnapshotAnswer(cmd, listVolumeTo);
}
} catch (Exception e) {
String msg = e.getMessage();
s_logger.error("failed to delete vm snapshot " + vmSnapshotName + " of vm " + vmName + " due to " + msg);
return new DeleteVMSnapshotAnswer(cmd, false, msg);
}
}
@Override
public RevertToVMSnapshotAnswer execute(VmwareHostService hostService, RevertToVMSnapshotCommand cmd) {
String snapshotName = cmd.getTarget().getSnapshotName();
String vmName = cmd.getVmName();
Boolean snapshotMemory = cmd.getTarget().getType() == VMSnapshot.Type.DiskAndMemory;
List<VolumeTO> listVolumeTo = cmd.getVolumeTOs();
VirtualMachine.State vmState = VirtualMachine.State.Running;
VirtualMachineMO vmMo = null;
VmwareContext context = hostService.getServiceContext(cmd);
Map<String, String> mapNewDisk = new HashMap<String, String>();
try {
VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
// wait if there are already VM revert task running
ManagedObjectReference taskmgr = context.getServiceContent().getTaskManager();
ManagedObjectReference[] tasks = (ManagedObjectReference[]) context.getServiceUtil().getDynamicProperty(taskmgr, "recentTask");
for (ManagedObjectReference taskMor : tasks) {
TaskInfo info = (TaskInfo) (context.getServiceUtil().getDynamicProperty(taskMor, "info"));
if(info.getEntityName().equals(cmd.getVmName()) && info.getName().equalsIgnoreCase("RevertToSnapshot_Task")){
s_logger.debug("There is already a VM snapshot task running, wait for it");
context.getServiceUtil().waitForTask(taskMor);
}
}
HostMO hostMo = (HostMO) hyperHost;
vmMo = hyperHost.findVmOnHyperHost(vmName);
if(vmMo == null)
vmMo = hyperHost.findVmOnPeerHyperHost(vmName);
if (vmMo == null) {
String msg = "Unable to find VM for RevertToVMSnapshotCommand";
s_logger.debug(msg);
return new RevertToVMSnapshotAnswer(cmd, false, msg);
} else {
boolean result = false;
if (snapshotName != null) {
ManagedObjectReference morSnapshot = vmMo.getSnapshotMor(snapshotName);
result = hostMo.revertToSnapshot(morSnapshot);
} else {
return new RevertToVMSnapshotAnswer(cmd, false, "Unable to find the snapshot by name " + snapshotName);
}
if (result) {
VirtualDisk[] vdisks = vmMo.getAllDiskDevice();
// build a map<volumeName, vmdk>
for (int i = 0; i < vdisks.length; i++) {
@SuppressWarnings("deprecation")
List<Pair<String, ManagedObjectReference>> vmdkFiles = vmMo.getDiskDatastorePathChain(
vdisks[i], false);
for (Pair<String, ManagedObjectReference> fileItem : vmdkFiles) {
String vmdkName = fileItem.first().split(" ")[1];
if (vmdkName.endsWith(".vmdk")) {
vmdkName = vmdkName.substring(0, vmdkName.length() - (".vmdk").length());
}
String[] s = vmdkName.split("-");
mapNewDisk.put(s[0], vmdkName);
}
}
String key = null;
for (VolumeTO volumeTo : listVolumeTo) {
String parentUUID = volumeTo.getPath();
String[] s = parentUUID.split("-");
key = s[0];
volumeTo.setPath(mapNewDisk.get(key));
}
if (!snapshotMemory) {
vmState = VirtualMachine.State.Stopped;
}
return new RevertToVMSnapshotAnswer(cmd, listVolumeTo, vmState);
} else {
return new RevertToVMSnapshotAnswer(cmd, false,
"Error while reverting to snapshot due to execute in esxi");
}
}
} catch (Exception e) {
String msg = "revert vm " + vmName + " to snapshot " + snapshotName + " failed due to " + e.getMessage();
s_logger.error(msg);
return new RevertToVMSnapshotAnswer(cmd, false, msg);
}
}
private VirtualMachineMO createWorkingVM(DatastoreMO dsMo, VmwareHypervisorHost hyperHost) throws Exception {
String uniqueName = UUID.randomUUID().toString();
VirtualMachineMO workingVM = null;
VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec();
vmConfig.setName(uniqueName);
vmConfig.setMemoryMB((long) 4);
vmConfig.setNumCPUs(1);
vmConfig.setGuestId(VirtualMachineGuestOsIdentifier._otherGuest.toString());
VirtualMachineFileInfo fileInfo = new VirtualMachineFileInfo();
fileInfo.setVmPathName(String.format("[%s]", dsMo.getName()));
vmConfig.setFiles(fileInfo);
VirtualLsiLogicController scsiController = new VirtualLsiLogicController();
scsiController.setSharedBus(VirtualSCSISharing.noSharing);
scsiController.setBusNumber(0);
scsiController.setKey(1);
VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec();
scsiControllerSpec.setDevice(scsiController);
scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.add);
vmConfig.setDeviceChange(new VirtualDeviceConfigSpec[] { scsiControllerSpec });
hyperHost.createVm(vmConfig);
workingVM = hyperHost.findVmOnHyperHost(uniqueName);
return workingVM;
}
}

View File

@ -65,9 +65,13 @@ import com.cloud.agent.api.Command;
import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
import com.cloud.agent.api.CreateStoragePoolCommand;
import com.cloud.agent.api.CreateVMSnapshotAnswer;
import com.cloud.agent.api.CreateVMSnapshotCommand;
import com.cloud.agent.api.CreateVolumeFromSnapshotAnswer;
import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
import com.cloud.agent.api.DeleteStoragePoolCommand;
import com.cloud.agent.api.DeleteVMSnapshotAnswer;
import com.cloud.agent.api.DeleteVMSnapshotCommand;
import com.cloud.agent.api.GetDomRVersionAnswer;
import com.cloud.agent.api.GetDomRVersionCmd;
import com.cloud.agent.api.GetHostStatsAnswer;
@ -103,6 +107,8 @@ import com.cloud.agent.api.ReadyCommand;
import com.cloud.agent.api.RebootAnswer;
import com.cloud.agent.api.RebootCommand;
import com.cloud.agent.api.RebootRouterCommand;
import com.cloud.agent.api.RevertToVMSnapshotAnswer;
import com.cloud.agent.api.RevertToVMSnapshotCommand;
import com.cloud.agent.api.SetupAnswer;
import com.cloud.agent.api.SetupCommand;
import com.cloud.agent.api.SetupGuestNetworkAnswer;
@ -138,8 +144,8 @@ import com.cloud.agent.api.routing.SetNetworkACLCommand;
import com.cloud.agent.api.routing.SetPortForwardingRulesAnswer;
import com.cloud.agent.api.routing.SetPortForwardingRulesCommand;
import com.cloud.agent.api.routing.SetPortForwardingRulesVpcCommand;
import com.cloud.agent.api.routing.SetSourceNatCommand;
import com.cloud.agent.api.routing.SetSourceNatAnswer;
import com.cloud.agent.api.routing.SetSourceNatCommand;
import com.cloud.agent.api.routing.SetStaticNatRulesAnswer;
import com.cloud.agent.api.routing.SetStaticNatRulesCommand;
import com.cloud.agent.api.routing.Site2SiteVpnCfgCommand;
@ -442,7 +448,13 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
answer = execute((SetSourceNatCommand) cmd);
} else if (clz == SetNetworkACLCommand.class) {
answer = execute((SetNetworkACLCommand) cmd);
} else if (clz == SetPortForwardingRulesVpcCommand.class) {
} else if (cmd instanceof CreateVMSnapshotCommand) {
return execute((CreateVMSnapshotCommand)cmd);
} else if(cmd instanceof DeleteVMSnapshotCommand){
return execute((DeleteVMSnapshotCommand)cmd);
} else if(cmd instanceof RevertToVMSnapshotCommand){
return execute((RevertToVMSnapshotCommand)cmd);
}else if (clz == SetPortForwardingRulesVpcCommand.class) {
answer = execute((SetPortForwardingRulesVpcCommand) cmd);
} else if (clz == Site2SiteVpnCfgCommand.class) {
answer = execute((Site2SiteVpnCfgCommand) cmd);
@ -2718,7 +2730,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
// before we stop VM, remove all possible snapshots on the VM to let
// disk chain be collapsed
s_logger.info("Remove all snapshot before stopping VM " + cmd.getVmName());
vmMo.removeAllSnapshots();
if (vmMo.safePowerOff(_shutdown_waitMs)) {
state = State.Stopped;
return new StopAnswer(cmd, "Stop VM " + cmd.getVmName() + " Succeed", 0, true);
@ -3270,7 +3281,42 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
}
}
protected Answer execute(CreateVMSnapshotCommand cmd) {
try {
VmwareContext context = getServiceContext();
VmwareManager mgr = context
.getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
return mgr.getStorageManager().execute(this, cmd);
} catch (Exception e) {
e.printStackTrace();
return new CreateVMSnapshotAnswer(cmd, false, "");
}
}
protected Answer execute(DeleteVMSnapshotCommand cmd) {
try {
VmwareContext context = getServiceContext();
VmwareManager mgr = context
.getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
return mgr.getStorageManager().execute(this, cmd);
} catch (Exception e) {
e.printStackTrace();
return new DeleteVMSnapshotAnswer(cmd, false, "");
}
}
protected Answer execute(RevertToVMSnapshotCommand cmd){
try{
VmwareContext context = getServiceContext();
VmwareManager mgr = context.getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
return mgr.getStorageManager().execute(this, cmd);
}catch (Exception e){
e.printStackTrace();
return new RevertToVMSnapshotAnswer(cmd,false,"");
}
}
protected Answer execute(CreateVolumeFromSnapshotCommand cmd) {
if (s_logger.isInfoEnabled()) {
s_logger.info("Executing resource CreateVolumeFromSnapshotCommand: " + _gson.toJson(cmd));

View File

@ -87,9 +87,13 @@ import com.cloud.agent.api.Command;
import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
import com.cloud.agent.api.CreateStoragePoolCommand;
import com.cloud.agent.api.CreateVMSnapshotAnswer;
import com.cloud.agent.api.CreateVMSnapshotCommand;
import com.cloud.agent.api.CreateVolumeFromSnapshotAnswer;
import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
import com.cloud.agent.api.DeleteStoragePoolCommand;
import com.cloud.agent.api.DeleteVMSnapshotAnswer;
import com.cloud.agent.api.DeleteVMSnapshotCommand;
import com.cloud.agent.api.GetDomRVersionAnswer;
import com.cloud.agent.api.GetDomRVersionCmd;
import com.cloud.agent.api.GetHostStatsAnswer;
@ -126,6 +130,8 @@ import com.cloud.agent.api.ReadyCommand;
import com.cloud.agent.api.RebootAnswer;
import com.cloud.agent.api.RebootCommand;
import com.cloud.agent.api.RebootRouterCommand;
import com.cloud.agent.api.RevertToVMSnapshotAnswer;
import com.cloud.agent.api.RevertToVMSnapshotCommand;
import com.cloud.agent.api.SecurityGroupRuleAnswer;
import com.cloud.agent.api.SecurityGroupRulesCmd;
import com.cloud.agent.api.SetupAnswer;
@ -234,6 +240,7 @@ import com.cloud.utils.net.NetUtils;
import com.cloud.vm.DiskProfile;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachine.State;
import com.cloud.vm.snapshot.VMSnapshot;
import com.trilead.ssh2.SCPClient;
import com.xensource.xenapi.Bond;
import com.xensource.xenapi.Connection;
@ -253,6 +260,10 @@ import com.xensource.xenapi.Types;
import com.xensource.xenapi.Types.BadServerResponse;
import com.xensource.xenapi.Types.ConsoleProtocol;
import com.xensource.xenapi.Types.IpConfigurationMode;
import com.xensource.xenapi.Types.OperationNotAllowed;
import com.xensource.xenapi.Types.SrFull;
import com.xensource.xenapi.Types.VbdType;
import com.xensource.xenapi.Types.VmBadPowerState;
import com.xensource.xenapi.Types.VmPowerState;
import com.xensource.xenapi.Types.XenAPIException;
import com.xensource.xenapi.VBD;
@ -570,11 +581,109 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
return execute((Site2SiteVpnCfgCommand) cmd);
} else if (clazz == CheckS2SVpnConnectionsCommand.class) {
return execute((CheckS2SVpnConnectionsCommand) cmd);
} else if (clazz == CreateVMSnapshotCommand.class) {
return execute((CreateVMSnapshotCommand)cmd);
} else if (clazz == DeleteVMSnapshotCommand.class) {
return execute((DeleteVMSnapshotCommand)cmd);
} else if (clazz == RevertToVMSnapshotCommand.class) {
return execute((RevertToVMSnapshotCommand)cmd);
} else {
return Answer.createUnsupportedCommandAnswer(cmd);
}
}
private Answer execute(RevertToVMSnapshotCommand cmd) {
String vmName = cmd.getVmName();
List<VolumeTO> listVolumeTo = cmd.getVolumeTOs();
VMSnapshot.Type vmSnapshotType = cmd.getTarget().getType();
Boolean snapshotMemory = vmSnapshotType == VMSnapshot.Type.DiskAndMemory;
Connection conn = getConnection();
VirtualMachine.State vmState = null;
VM vm = null;
try {
// remove vm from s_vms, for delta sync
s_vms.remove(_cluster, _name, vmName);
Set<VM> vmSnapshots = VM.getByNameLabel(conn, cmd.getTarget().getSnapshotName());
if(vmSnapshots.size() == 0)
return new RevertToVMSnapshotAnswer(cmd, false, "Cannot find vmSnapshot with name: " + cmd.getTarget().getSnapshotName());
VM vmSnapshot = vmSnapshots.iterator().next();
// find target VM or creating a work VM
try {
vm = getVM(conn, vmName);
} catch (Exception e) {
vm = createWorkingVM(conn, vmName, cmd.getGuestOSType(), listVolumeTo);
}
if (vm == null) {
return new RevertToVMSnapshotAnswer(cmd, false,
"Revert to VM Snapshot Failed due to can not find vm: " + vmName);
}
// call plugin to execute revert
revertToSnapshot(conn, vmSnapshot, vmName, vm.getUuid(conn), snapshotMemory, _host.uuid);
vm = getVM(conn, vmName);
Set<VBD> vbds = vm.getVBDs(conn);
Map<String, VDI> vdiMap = new HashMap<String, VDI>();
// get vdi:vbdr to a map
for (VBD vbd : vbds) {
VBD.Record vbdr = vbd.getRecord(conn);
if (vbdr.type == Types.VbdType.DISK) {
VDI vdi = vbdr.VDI;
vdiMap.put(vbdr.userdevice, vdi);
}
}
if (!snapshotMemory) {
vm.destroy(conn);
vmState = VirtualMachine.State.Stopped;
} else {
s_vms.put(_cluster, _name, vmName, State.Running);
vmState = VirtualMachine.State.Running;
}
// after revert, VM's volumes path have been changed, need to report to manager
for (VolumeTO volumeTo : listVolumeTo) {
Long deviceId = volumeTo.getDeviceId();
VDI vdi = vdiMap.get(deviceId.toString());
volumeTo.setPath(vdi.getUuid(conn));
}
return new RevertToVMSnapshotAnswer(cmd, listVolumeTo,vmState);
} catch (Exception e) {
s_logger.error("revert vm " + vmName
+ " to snapshot " + cmd.getTarget().getSnapshotName() + " failed due to " + e.getMessage());
return new RevertToVMSnapshotAnswer(cmd, false, e.getMessage());
}
}
private String revertToSnapshot(Connection conn, VM vmSnapshot,
String vmName, String oldVmUuid, Boolean snapshotMemory, String hostUUID)
throws XenAPIException, XmlRpcException {
String results = callHostPluginAsync(conn, "vmopsSnapshot",
"revert_memory_snapshot", 10 * 60 * 1000, "snapshotUUID",
vmSnapshot.getUuid(conn), "vmName", vmName, "oldVmUuid",
oldVmUuid, "snapshotMemory", snapshotMemory.toString(), "hostUUID", hostUUID);
String errMsg = null;
if (results == null || results.isEmpty()) {
errMsg = "revert_memory_snapshot return null";
} else {
if (results.equals("0")) {
return results;
} else {
errMsg = "revert_memory_snapshot exception";
}
}
s_logger.warn(errMsg);
throw new CloudRuntimeException(errMsg);
}
protected XsLocalNetwork getNativeNetworkForTraffic(Connection conn, TrafficType type, String name) throws XenAPIException, XmlRpcException {
if (name != null) {
if (s_logger.isDebugEnabled()) {
@ -6138,6 +6247,199 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
}
protected Answer execute(final CreateVMSnapshotCommand cmd) {
String vmName = cmd.getVmName();
String vmSnapshotName = cmd.getTarget().getSnapshotName();
List<VolumeTO> listVolumeTo = cmd.getVolumeTOs();
VirtualMachine.State vmState = cmd.getVmState();
String guestOSType = cmd.getGuestOSType();
boolean snapshotMemory = cmd.getTarget().getType() == VMSnapshot.Type.DiskAndMemory;
long timeout = 600;
Connection conn = getConnection();
VM vm = null;
VM vmSnapshot = null;
boolean success = false;
try {
// check if VM snapshot already exists
Set<VM> vmSnapshots = VM.getByNameLabel(conn, cmd.getTarget().getSnapshotName());
if(vmSnapshots.size() > 0)
return new CreateVMSnapshotAnswer(cmd, cmd.getTarget(), cmd.getVolumeTOs());
// check if there is already a task for this VM snapshot
Task task = null;
Set<Task> tasks = Task.getByNameLabel(conn, "Async.VM.snapshot");
tasks.addAll(Task.getByNameLabel(conn, "Async.VM.checkpoint"));
for (Task taskItem : tasks) {
if(taskItem.getOtherConfig(conn).containsKey("CS_VM_SNAPSHOT_KEY")){
String vmSnapshotTaskName = taskItem.getOtherConfig(conn).get("CS_VM_SNAPSHOT_KEY");
if(vmSnapshotTaskName != null && vmSnapshotTaskName.equals(cmd.getTarget().getSnapshotName())){
task = taskItem;
}
}
}
// create a new task if there is no existing task for this VM snapshot
if(task == null){
try {
vm = getVM(conn, vmName);
} catch (Exception e) {
if (!snapshotMemory) {
vm = createWorkingVM(conn, vmName, guestOSType, listVolumeTo);
}
}
if (vm == null) {
return new CreateVMSnapshotAnswer(cmd, false,
"Creating VM Snapshot Failed due to can not find vm: "
+ vmName);
}
// call Xenserver API
if (!snapshotMemory) {
task = vm.snapshotAsync(conn, vmSnapshotName);
} else {
Set<VBD> vbds = vm.getVBDs(conn);
Pool pool = Pool.getByUuid(conn, _host.pool);
for (VBD vbd: vbds){
VBD.Record vbdr = vbd.getRecord(conn);
if (vbdr.userdevice.equals("0")){
VDI vdi = vbdr.VDI;
SR sr = vdi.getSR(conn);
// store memory image on the same SR with ROOT volume
pool.setSuspendImageSR(conn, sr);
}
}
task = vm.checkpointAsync(conn, vmSnapshotName);
}
task.addToOtherConfig(conn, "CS_VM_SNAPSHOT_KEY", vmSnapshotName);
}
waitForTask(conn, task, 1000, timeout * 1000);
checkForSuccess(conn, task);
String result = task.getResult(conn);
// extract VM snapshot ref from result
String ref = result.substring("<value>".length(), result.length() - "</value>".length());
vmSnapshot = Types.toVM(ref);
success = true;
return new CreateVMSnapshotAnswer(cmd, cmd.getTarget(), cmd.getVolumeTOs());
} catch (Exception e) {
String msg = e.getMessage();
s_logger.error("Creating VM Snapshot " + cmd.getTarget().getSnapshotName() + " failed due to: " + msg);
return new CreateVMSnapshotAnswer(cmd, false, msg);
} finally {
try {
if (!success) {
if (vmSnapshot != null) {
s_logger.debug("Delete exsisting VM Snapshot "
+ vmSnapshotName
+ " after making VolumeTO failed");
Set<VBD> vbds = vmSnapshot.getVBDs(conn);
for (VBD vbd : vbds) {
VBD.Record vbdr = vbd.getRecord(conn);
if (vbdr.type == VbdType.DISK) {
VDI vdi = vbdr.VDI;
vdi.destroy(conn);
}
}
vmSnapshot.destroy(conn);
}
}
if (vmState == VirtualMachine.State.Stopped) {
if (vm != null) {
vm.destroy(conn);
}
}
} catch (Exception e2) {
s_logger.error("delete snapshot error due to "
+ e2.getMessage());
}
}
}
private VM createWorkingVM(Connection conn, String vmName,
String guestOSType, List<VolumeTO> listVolumeTo)
throws BadServerResponse, VmBadPowerState, SrFull,
OperationNotAllowed, XenAPIException, XmlRpcException {
String guestOsTypeName = getGuestOsType(guestOSType, false);
if (guestOsTypeName == null) {
String msg = " Hypervisor " + this.getClass().getName()
+ " doesn't support guest OS type " + guestOSType
+ ". you can choose 'Other install media' to run it as HVM";
s_logger.warn(msg);
throw new CloudRuntimeException(msg);
}
VM template = getVM(conn, guestOsTypeName);
VM vm = template.createClone(conn, vmName);
vm.setIsATemplate(conn, false);
Map<VDI, VolumeTO> vdiMap = new HashMap<VDI, VolumeTO>();
for (VolumeTO volume : listVolumeTo) {
String vdiUuid = volume.getPath();
try {
VDI vdi = VDI.getByUuid(conn, vdiUuid);
vdiMap.put(vdi, volume);
} catch (Types.UuidInvalid e) {
s_logger.warn("Unable to find vdi by uuid: " + vdiUuid
+ ", skip it");
}
}
for (VDI vdi : vdiMap.keySet()) {
VolumeTO volumeTO = vdiMap.get(vdi);
VBD.Record vbdr = new VBD.Record();
vbdr.VM = vm;
vbdr.VDI = vdi;
if (volumeTO.getType() == Volume.Type.ROOT) {
vbdr.bootable = true;
vbdr.unpluggable = false;
} else {
vbdr.bootable = false;
vbdr.unpluggable = true;
}
vbdr.userdevice = new Long(volumeTO.getDeviceId()).toString();
vbdr.mode = Types.VbdMode.RW;
vbdr.type = Types.VbdType.DISK;
VBD.create(conn, vbdr);
}
return vm;
}
protected Answer execute(final DeleteVMSnapshotCommand cmd) {
String snapshotName = cmd.getTarget().getSnapshotName();
Connection conn = getConnection();
try {
List<VDI> vdiList = new ArrayList<VDI>();
Set<VM> snapshots = VM.getByNameLabel(conn, snapshotName);
if(snapshots.size() == 0){
s_logger.warn("VM snapshot with name " + snapshotName + " does not exist, assume it is already deleted");
return new DeleteVMSnapshotAnswer(cmd, cmd.getVolumeTOs());
}
VM snapshot = snapshots.iterator().next();
Set<VBD> vbds = snapshot.getVBDs(conn);
for (VBD vbd : vbds) {
if (vbd.getType(conn) == VbdType.DISK) {
VDI vdi = vbd.getVDI(conn);
vdiList.add(vdi);
}
}
if(cmd.getTarget().getType() == VMSnapshot.Type.DiskAndMemory)
vdiList.add(snapshot.getSuspendVDI(conn));
snapshot.destroy(conn);
for (VDI vdi : vdiList) {
vdi.destroy(conn);
}
return new DeleteVMSnapshotAnswer(cmd, cmd.getVolumeTOs());
} catch (Exception e) {
s_logger.warn("Catch Exception: " + e.getClass().toString()
+ " due to " + e.toString(), e);
return new DeleteVMSnapshotAnswer(cmd, false, e.getMessage());
}
}
protected Answer execute(final AttachIsoCommand cmd) {
Connection conn = getConnection();
boolean attach = cmd.isAttach();

View File

@ -556,6 +556,33 @@ def deleteSnapshotBackup(session, args):
return "1"
if __name__ == "__main__":
XenAPIPlugin.dispatch({"getVhdParent":getVhdParent, "create_secondary_storage_folder":create_secondary_storage_folder, "delete_secondary_storage_folder":delete_secondary_storage_folder, "post_create_private_template":post_create_private_template, "backupSnapshot": backupSnapshot, "deleteSnapshotBackup": deleteSnapshotBackup, "unmountSnapshotsDir": unmountSnapshotsDir})
@echo
def revert_memory_snapshot(session, args):
util.SMlog("Calling revert_memory_snapshot with " + str(args))
vmName = args['vmName']
snapshotUUID = args['snapshotUUID']
oldVmUuid = args['oldVmUuid']
snapshotMemory = args['snapshotMemory']
hostUUID = args['hostUUID']
try:
cmd = '''xe vbd-list vm-uuid=%s | grep 'vdi-uuid' | grep -v 'not in database' | sed -e 's/vdi-uuid ( RO)://g' ''' % oldVmUuid
vdiUuids = os.popen(cmd).read().split()
cmd2 = '''xe vm-param-get param-name=power-state uuid=''' + oldVmUuid
if os.popen(cmd2).read().split()[0] != 'halted':
os.system("xe vm-shutdown force=true vm=" + vmName)
os.system("xe vm-destroy uuid=" + oldVmUuid)
os.system("xe snapshot-revert snapshot-uuid=" + snapshotUUID)
if snapshotMemory == 'true':
os.system("xe vm-resume vm=" + vmName + " on=" + hostUUID)
for vdiUuid in vdiUuids:
os.system("xe vdi-destroy uuid=" + vdiUuid)
except OSError, (errno, strerror):
errMsg = "OSError while reverting vm " + vmName + " to snapshot " + snapshotUUID + " with errno: " + str(errno) + " and strerr: " + strerror
util.SMlog(errMsg)
raise xs_errors.XenError(errMsg)
return "0"
if __name__ == "__main__":
XenAPIPlugin.dispatch({"getVhdParent":getVhdParent, "create_secondary_storage_folder":create_secondary_storage_folder, "delete_secondary_storage_folder":delete_secondary_storage_folder, "post_create_private_template":post_create_private_template, "backupSnapshot": backupSnapshot, "deleteSnapshotBackup": deleteSnapshotBackup, "unmountSnapshotsDir": unmountSnapshotsDir, "revert_memory_snapshot":revert_memory_snapshot})

View File

@ -21,6 +21,7 @@ import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Hashtable;
import org.apache.cloudstack.api.ApiConstants.HostDetails;
import org.apache.cloudstack.api.ApiConstants.VMDetails;
@ -259,6 +260,8 @@ import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.UserVmDetailsDao;
import com.cloud.vm.dao.VMInstanceDao;
import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.vm.snapshot.VMSnapshot;
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
public class ApiDBUtils {
private static ManagementServer _ms;
@ -357,7 +360,7 @@ public class ApiDBUtils {
private static VpcOfferingDao _vpcOfferingDao;
private static SnapshotPolicyDao _snapshotPolicyDao;
private static AsyncJobDao _asyncJobDao;
private static VMSnapshotDao _vmSnapshotDao;
static {
_ms = (ManagementServer) ComponentLocator.getComponent(ManagementServer.Name);
ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name);
@ -454,7 +457,7 @@ public class ApiDBUtils {
_diskOfferingJoinDao = locator.getDao(DiskOfferingJoinDao.class);
_srvOfferingJoinDao = locator.getDao(ServiceOfferingJoinDao.class);
_dcJoinDao = locator.getDao(DataCenterJoinDao.class);
_vmSnapshotDao = locator.getDao(VMSnapshotDao.class);
// Note: stats collector should already have been initialized by this time, otherwise a null instance is returned
_statsCollector = StatsCollector.getInstance();
}
@ -1003,6 +1006,11 @@ public class ApiDBUtils {
return _networkModel.canUseForDeploy(network);
}
public static VMSnapshot getVMSnapshotById(Long vmSnapshotId) {
VMSnapshot vmSnapshot = _vmSnapshotDao.findById(vmSnapshotId);
return vmSnapshot;
}
public static String getUuid(String resourceId, TaggedResourceType resourceType) {
return _taggedResourceService.getUuid(resourceId, resourceType);
}

View File

@ -133,7 +133,6 @@ import org.apache.cloudstack.api.response.VpcOfferingResponse;
import org.apache.cloudstack.api.response.VpcResponse;
import org.apache.cloudstack.api.response.VpnUsersResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.cloudstack.api.response.S3Response;
import com.cloud.async.AsyncJob;
import com.cloud.capacity.Capacity;
@ -242,8 +241,15 @@ import com.cloud.vm.InstanceGroup;
import com.cloud.vm.NicProfile;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachine.Type;
import com.cloud.vm.VmStats;
import com.cloud.vm.dao.UserVmData;
import com.cloud.vm.dao.UserVmData.NicData;
import com.cloud.vm.dao.UserVmData.SecurityGroupData;
import com.cloud.vm.snapshot.VMSnapshot;
import org.apache.cloudstack.api.ResponseGenerator;
import org.apache.cloudstack.api.response.VMSnapshotResponse;
public class ApiResponseHelper implements ResponseGenerator {
public class ApiResponseHelper implements ResponseGenerator{
public final Logger s_logger = Logger.getLogger(ApiResponseHelper.class);
private static final DecimalFormat s_percentFormat = new DecimalFormat("##.##");
@ -393,6 +399,25 @@ public class ApiResponseHelper implements ResponseGenerator {
return snapshotResponse;
}
@Override
public VMSnapshotResponse createVMSnapshotResponse(VMSnapshot vmSnapshot) {
VMSnapshotResponse vmSnapshotResponse = new VMSnapshotResponse();
vmSnapshotResponse.setId(vmSnapshot.getUuid());
vmSnapshotResponse.setName(vmSnapshot.getName());
vmSnapshotResponse.setState(vmSnapshot.getState());
vmSnapshotResponse.setCreated(vmSnapshot.getCreated());
vmSnapshotResponse.setDescription(vmSnapshot.getDescription());
vmSnapshotResponse.setDisplayName(vmSnapshot.getDisplayName());
UserVm vm = ApiDBUtils.findUserVmById(vmSnapshot.getVmId());
if(vm!=null)
vmSnapshotResponse.setVirtualMachineid(vm.getUuid());
if(vmSnapshot.getParent() != null)
vmSnapshotResponse.setParentName(ApiDBUtils.getVMSnapshotById(vmSnapshot.getParent()).getDisplayName());
vmSnapshotResponse.setCurrent(vmSnapshot.getCurrent());
vmSnapshotResponse.setType(vmSnapshot.getType().toString());
return vmSnapshotResponse;
}
@Override
public SnapshotPolicyResponse createSnapshotPolicyResponse(SnapshotPolicy policy) {
SnapshotPolicyResponse policyResponse = new SnapshotPolicyResponse();
@ -3098,7 +3123,6 @@ public class ApiResponseHelper implements ResponseGenerator {
}
@Override
public GuestOSResponse createGuestOSResponse(GuestOS guestOS) {
GuestOSResponse response = new GuestOSResponse();
@ -3136,6 +3160,4 @@ public class ApiResponseHelper implements ResponseGenerator {
response.setObjectName("snapshot");
return response;
}
}

View File

@ -60,9 +60,11 @@ import com.cloud.storage.VMTemplateHostVO;
import com.cloud.storage.VMTemplateStoragePoolVO;
import com.cloud.storage.VMTemplateSwiftVO;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.VMTemplatePoolDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.storage.swift.SwiftManager;
import com.cloud.uservm.UserVm;
import com.cloud.utils.DateUtil;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.Pair;
@ -76,7 +78,11 @@ import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachine.Event;
import com.cloud.vm.VirtualMachine.State;
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.VMInstanceDao;
import com.cloud.vm.snapshot.VMSnapshot;
import com.cloud.vm.snapshot.VMSnapshotVO;
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
@Local(value = CapacityManager.class)
public class CapacityManagerImpl implements CapacityManager, StateListener<State, VirtualMachine.Event, VirtualMachine>, Listener, ResourceListener {
@ -108,7 +114,11 @@ public class CapacityManagerImpl implements CapacityManager, StateListener<State
ConfigurationManager _configMgr;
@Inject
HypervisorCapabilitiesDao _hypervisorCapabilitiesDao;
@Inject
protected VMSnapshotDao _vmSnapshotDao;
@Inject
protected UserVmDao _userVMDao;
private int _vmCapacityReleaseInterval;
private ScheduledExecutorService _executor;
private boolean _stopped;
@ -441,12 +451,43 @@ public class CapacityManagerImpl implements CapacityManager, StateListener<State
}
private long getVMSnapshotAllocatedCapacity(StoragePoolVO pool){
List<VolumeVO> volumes = _volumeDao.findByPoolId(pool.getId());
long totalSize = 0;
for (VolumeVO volume : volumes) {
if(volume.getInstanceId() == null)
continue;
Long vmId = volume.getInstanceId();
UserVm vm = _userVMDao.findById(vmId);
if(vm == null)
continue;
ServiceOffering offering = _offeringsDao.findById(vm.getServiceOfferingId());
List<VMSnapshotVO> vmSnapshots = _vmSnapshotDao.findByVm(vmId);
long pathCount = 0;
long memorySnapshotSize = 0;
for (VMSnapshotVO vmSnapshotVO : vmSnapshots) {
if(_vmSnapshotDao.listByParent(vmSnapshotVO.getId()).size() == 0)
pathCount++;
if(vmSnapshotVO.getType() == VMSnapshot.Type.DiskAndMemory)
memorySnapshotSize += (offering.getRamSize() * 1024 * 1024);
}
if(pathCount <= 1)
totalSize = totalSize + memorySnapshotSize;
else
totalSize = totalSize + volume.getSize() * (pathCount - 1) + memorySnapshotSize;
}
return totalSize;
}
@Override
public long getAllocatedPoolCapacity(StoragePoolVO pool, VMTemplateVO templateForVmCreation){
// Get size for all the volumes
Pair<Long, Long> sizes = _volumeDao.getCountAndTotalByPool(pool.getId());
long totalAllocatedSize = sizes.second() + sizes.first() * _extraBytesPerVolume;
// Get size for VM Snapshots
totalAllocatedSize = totalAllocatedSize + getVMSnapshotAllocatedCapacity(pool);
// Iterate through all templates on this storage pool
boolean tmpinstalled = false;

View File

@ -33,6 +33,7 @@ import com.cloud.storage.secondary.SecondaryStorageVmManager;
import com.cloud.storage.snapshot.SnapshotManager;
import com.cloud.template.TemplateManager;
import com.cloud.vm.UserVmManager;
import com.cloud.vm.snapshot.VMSnapshotManager;
public enum Config {
@ -364,8 +365,17 @@ public enum Config {
ExternalBaremetalResourceClassName("Advanced", ManagementServer.class, String.class, "external,baremetal.resource.classname", null, "class name for handling external baremetal resource", null),
EnableBaremetalSecurityGroupAgentEcho("Advanced", ManagementServer.class, Boolean.class, "enable.baremetal.securitygroup.agent.echo", "false", "After starting provision process, periodcially echo security agent installed in the template. Treat provisioning as success only if echo successfully", null),
IntervalToEchoBaremetalSecurityGroupAgent("Advanced", ManagementServer.class, Integer.class, "interval.baremetal.securitygroup.agent.echo", "10", "Interval to echo baremetal security group agent, in seconds", null),
TimeoutToEchoBaremetalSecurityGroupAgent("Advanced", ManagementServer.class, Integer.class, "timeout.baremetal.securitygroup.agent.echo", "3600", "Timeout to echo baremetal security group agent, in seconds, the provisioning process will be treated as a failure", null);
TimeoutToEchoBaremetalSecurityGroupAgent("Advanced", ManagementServer.class, Integer.class, "timeout.baremetal.securitygroup.agent.echo", "3600", "Timeout to echo baremetal security group agent, in seconds, the provisioning process will be treated as a failure", null),
// VMSnapshots
VMSnapshotMax("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.max", "10", "Maximum vm snapshots for a vm", null),
VMSnapshotCreateWait("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.create.wait", "600", "In second, timeout for create vm snapshot", null),
VMSnapshotExpungeInterval("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.expunge.interval", "60", "The interval (in seconds) to wait before running the expunge thread.", null),
VMSnapshotExpungeWorkers("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.expunge.workers", "1", "Number of workers performing expunge ", null);
private final String _category;
private final Class<?> _componentClass;
private final Class<?> _type;

View File

@ -229,8 +229,8 @@ import com.cloud.vm.dao.UserVmDaoImpl;
import com.cloud.vm.dao.UserVmDetailsDaoImpl;
import com.cloud.vm.dao.VMInstanceDaoImpl;
import com.cloud.event.dao.EventJoinDaoImpl;
import com.cloud.vm.snapshot.VMSnapshotManagerImpl;
import com.cloud.vm.snapshot.dao.VMSnapshotDaoImpl;
public class DefaultComponentLibrary extends ComponentLibraryBase implements ComponentLibrary {
protected void populateDaos() {
@ -380,7 +380,6 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com
addDao("Site2SiteVpnGatewayDao", Site2SiteVpnGatewayDaoImpl.class);
addDao("Site2SiteCustomerGatewayDao", Site2SiteCustomerGatewayDaoImpl.class);
addDao("Site2SiteVpnConnnectionDao", Site2SiteVpnConnectionDaoImpl.class);
addDao("UserVmJoinDao", UserVmJoinDaoImpl.class);
addDao("DomainRouterJoinDao", DomainRouterJoinDaoImpl.class);
addDao("SecurityGroupJoinDao", SecurityGroupJoinDaoImpl.class);
@ -398,6 +397,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com
addDao("DiskOfferingJoinDao", DiskOfferingJoinDaoImpl.class);
addDao("ServiceOfferingJoinDao", ServiceOfferingJoinDaoImpl.class);
addDao("DataCenterJoinDao", DataCenterJoinDaoImpl.class);
addDao("VMSnapshotDao", VMSnapshotDaoImpl.class);
}
@Override
@ -462,6 +462,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com
addManager("TaggedResourcesManager", TaggedResourceManagerImpl.class);
addManager("Site2SiteVpnManager", Site2SiteVpnManagerImpl.class);
addManager("QueryManager", QueryManagerImpl.class);
addManager("VMSnapshot Manager", VMSnapshotManagerImpl.class);
}
@Override

View File

@ -1292,7 +1292,7 @@ public class ResourceManagerImpl implements ResourceManager, ResourceService, Ma
for (ClusterVO cluster : clustersForZone) {
HypervisorType hType = cluster.getHypervisorType();
if (!forVirtualRouter || (forVirtualRouter && hType != HypervisorType.BareMetal && hType != HypervisorType.Ovm)) {
if ( (cluster.getAllocationState() == AllocationState.Enabled) && (!forVirtualRouter || (forVirtualRouter && hType != HypervisorType.BareMetal && hType != HypervisorType.Ovm))) {
hypervisorTypes.add(hType);
}
}

View File

@ -128,6 +128,9 @@ import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachine.State;
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;
@Local(value = { SnapshotManager.class, SnapshotService.class })
public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Manager {
@ -188,7 +191,8 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
private VolumeDao _volumeDao;
@Inject
private ResourceTagDao _resourceTagDao;
@Inject
protected VMSnapshotDao _vmSnapshotDao;
String _name;
private int _totalRetries;
private int _pauseInterval;
@ -401,6 +405,7 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
}
// if volume is attached to a vm in destroyed or expunging state; disallow
// if volume is attached to a vm in taking vm snapshot; disallow
if (volume.getInstanceId() != null) {
UserVmVO userVm = _vmDao.findById(volume.getInstanceId());
if (userVm != null) {
@ -414,6 +419,12 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
if(activeSnapshots.size() > 1)
throw new CloudRuntimeException("There is other active snapshot tasks on the instance to which the volume is attached, please try again later");
}
List<VMSnapshotVO> activeVMSnapshots = _vmSnapshotDao.listByInstanceId(userVm.getId(),
VMSnapshot.State.Creating, VMSnapshot.State.Reverting, VMSnapshot.State.Expunging);
if (activeVMSnapshots.size() > 0) {
throw new CloudRuntimeException(
"There is other active vm snapshot tasks on the instance to which the volume is attached, please try again later");
}
}
}

View File

@ -71,6 +71,7 @@ import com.cloud.utils.db.Transaction;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.uuididentity.dao.IdentityDao;
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
@Local(value = { TaggedResourceService.class})
@ -119,6 +120,8 @@ public class TaggedResourceManagerImpl implements TaggedResourceService, Manager
VpcDao _vpcDao;
@Inject
StaticRouteDao _staticRouteDao;
@Inject
VMSnapshotDao _vmSnapshotDao;
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
@ -138,6 +141,7 @@ public class TaggedResourceManagerImpl implements TaggedResourceService, Manager
_daoMap.put(TaggedResourceType.Vpc, _vpcDao);
_daoMap.put(TaggedResourceType.NetworkACL, _firewallDao);
_daoMap.put(TaggedResourceType.StaticRoute, _staticRouteDao);
_daoMap.put(TaggedResourceType.VMSnapshot, _vmSnapshotDao);
return true;
}

View File

@ -142,6 +142,16 @@ import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.cloud.vm.dao.InstanceGroupDao;
import com.cloud.vm.dao.InstanceGroupVMMapDao;
import com.cloud.vm.dao.NicDao;
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.UserVmDetailsDao;
import com.cloud.vm.dao.VMInstanceDao;
import com.cloud.vm.snapshot.VMSnapshot;
import com.cloud.vm.snapshot.VMSnapshotManager;
import com.cloud.vm.snapshot.VMSnapshotVO;
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
@Local(value = { UserVmManager.class, UserVmService.class })
public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager {
@ -275,7 +285,11 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
PhysicalNetworkDao _physicalNetworkDao;
@Inject
VpcManager _vpcMgr;
@Inject
VMSnapshotDao _vmSnapshotDao;
@Inject
protected VMSnapshotManager _vmSnapshotMgr;
protected ScheduledExecutorService _executor = null;
protected int _expungeInterval;
protected int _expungeDelay;
@ -517,6 +531,9 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
//permission check
_accountMgr.checkAccess(caller, null, true, volume, vm);
//check if vm has snapshot, if true: can't attache volume
boolean attach = true;
checkVMSnapshots(vm, volumeId, attach);
//Check if volume is stored on secondary Storage.
boolean isVolumeOnSec = false;
VolumeHostVO volHostVO = _volumeHostDao.findByVolumeId(volume.getId());
@ -725,8 +742,19 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
}
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_DETACH, eventDescription = "detaching volume", async = true)
private void checkVMSnapshots(UserVmVO vm, Long volumeId, boolean attach) {
// Check that if vm has any VM snapshot
Long vmId = vm.getId();
List<VMSnapshotVO> listSnapshot = _vmSnapshotDao.listByInstanceId(vmId,
VMSnapshot.State.Ready, VMSnapshot.State.Creating, VMSnapshot.State.Reverting, VMSnapshot.State.Expunging);
if (listSnapshot != null && listSnapshot.size() != 0) {
throw new InvalidParameterValueException(
"The VM has VM snapshots, do not allowed to attach volume. Please delete the VM snapshots first.");
}
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_VOLUME_DETACH, eventDescription = "event_detaching_volume1", async = true)
public Volume detachVolumeFromVM(DetachVolumeCmd cmmd) {
Account caller = UserContext.current().getCaller();
if ((cmmd.getId() == null && cmmd.getDeviceId() == null && cmmd.getVirtualMachineId() == null) || (cmmd.getId() != null && (cmmd.getDeviceId() != null || cmmd.getVirtualMachineId() != null))
@ -775,6 +803,9 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
throw new InvalidParameterValueException("Please specify a VM that is either running or stopped.");
}
// Check that if the volume has snapshot
boolean attach = false;
checkVMSnapshots(vm, volumeId, attach);
AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor.getCurrentExecutor();
if (asyncExecutor != null) {
AsyncJobVO job = asyncExecutor.getJob();
@ -905,12 +936,25 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
if (vmInstance == null) {
throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId);
}
_accountMgr.checkAccess(caller, null, true, vmInstance);
// Check that the specified service offering ID is valid
_itMgr.checkIfCanUpgrade(vmInstance, svcOffId);
// remove diskAndMemory VM snapshots
List<VMSnapshotVO> vmSnapshots = _vmSnapshotDao.findByVm(vmId);
for (VMSnapshotVO vmSnapshotVO : vmSnapshots) {
if(vmSnapshotVO.getType() == VMSnapshot.Type.DiskAndMemory){
if(!_vmSnapshotMgr.deleteAllVMSnapshots(vmId, VMSnapshot.Type.DiskAndMemory)){
String errMsg = "Failed to remove VM snapshot during upgrading, snapshot id " + vmSnapshotVO.getId();
s_logger.debug(errMsg);
throw new CloudRuntimeException(errMsg);
}
}
}
_itMgr.upgradeVmDb(vmId, svcOffId);
return _vmDao.findById(vmInstance.getId());
@ -1293,7 +1337,6 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
}
volume = _volsDao.findById(snapshot.getVolumeId());
VolumeVO snapshotVolume = _volsDao.findByIdIncludingRemoved(snapshot.getVolumeId());
//check permissions
_accountMgr.checkAccess(caller, null, true, snapshot);

View File

@ -154,12 +154,17 @@ import com.cloud.utils.fsm.StateMachine2;
import com.cloud.vm.ItWorkVO.Step;
import com.cloud.vm.VirtualMachine.Event;
import com.cloud.vm.VirtualMachine.State;
import com.cloud.vm.VirtualMachineProfile.Param;
import com.cloud.vm.dao.ConsoleProxyDao;
import com.cloud.vm.dao.DomainRouterDao;
import com.cloud.vm.dao.NicDao;
import com.cloud.vm.dao.SecondaryStorageVmDao;
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.VMInstanceDao;
import com.cloud.vm.snapshot.VMSnapshot;
import com.cloud.vm.snapshot.VMSnapshotManager;
import com.cloud.vm.snapshot.VMSnapshotVO;
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
@Local(value = VirtualMachineManager.class)
public class VirtualMachineManagerImpl implements VirtualMachineManager, Listener {
@ -230,6 +235,8 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
protected HypervisorGuruManager _hvGuruMgr;
@Inject
protected NetworkDao _networkDao;
@Inject
protected VMSnapshotDao _vmSnapshotDao;
@Inject(adapter = DeploymentPlanner.class)
protected Adapters<DeploymentPlanner> _planners;
@ -239,6 +246,9 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
@Inject
protected ResourceManager _resourceMgr;
@Inject
protected VMSnapshotManager _vmSnapshotMgr = null;
Map<VirtualMachine.Type, VirtualMachineGuru<? extends VMInstanceVO>> _vmGurus = new HashMap<VirtualMachine.Type, VirtualMachineGuru<? extends VMInstanceVO>>();
protected StateMachine2<State, VirtualMachine.Event, VirtualMachine> _stateMachine;
@ -599,7 +609,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
ConcurrentOperationException, ResourceUnavailableException {
return advanceStart(vm, params, caller, account, null);
}
@Override
public <T extends VMInstanceVO> T advanceStart(T vm, Map<VirtualMachineProfile.Param, Object> params, User caller, Account account, DeploymentPlan planToDeploy)
throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException {
@ -1186,7 +1196,12 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
s_logger.debug("Unable to stop " + vm);
return false;
}
if (!_vmSnapshotMgr.deleteAllVMSnapshots(vm.getId(),null)){
s_logger.debug("Unable to delete all snapshots for " + vm);
return false;
}
try {
if (!stateTransitTo(vm, VirtualMachine.Event.DestroyRequested, vm.getHostId())) {
s_logger.debug("Unable to destroy the vm because it is not in the correct state: " + vm);
@ -1633,6 +1648,23 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
s_logger.debug("Found " + vms.size() + " VMs for host " + hostId);
for (VMInstanceVO vm : vms) {
AgentVmInfo info = infos.remove(vm.getId());
// sync VM Snapshots related transient states
List<VMSnapshotVO> expungingVMSnapshot = _vmSnapshotDao.listByInstanceId(vm.getId(), VMSnapshot.State.Expunging);
if( vm.getState() == State.RevertingToRunning || vm.getState() == State.RevertingToStopped
|| vm.getState() == State.RunningSnapshotting || vm.getState() == State.StoppedSnapshotting
|| expungingVMSnapshot.size() == 1){
s_logger.info("Found vm " + vm.getInstanceName() + " in state. " + vm.getState() + ", needs to sync VM snapshot state");
if(!_vmSnapshotMgr.syncVMSnapshot(vm, null, hostId)){
s_logger.warn("Failed to sync VM in a transient snapshot related state: " + vm.getInstanceName());
continue;
}else{
s_logger.info("Successfully sync VM in a transient snapshot related state: " + vm.getInstanceName() + " to " + vm.getState());
}
if(expungingVMSnapshot.size() == 1)
_vmSnapshotMgr.syncVMSnapshot(vm, expungingVMSnapshot.get(0), hostId);
}
VMInstanceVO castedVm = null;
if (info == null) {
info = new AgentVmInfo(vm.getInstanceName(), getVmGuru(vm), vm, State.Stopped);
@ -1750,8 +1782,31 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
for (VMInstanceVO vm : set_vms) {
AgentVmInfo info = infos.remove(vm.getId());
VMInstanceVO castedVm = null;
if ((info == null && (vm.getState() == State.Running || vm.getState() == State.Starting))
|| (info != null && (info.state == State.Running && vm.getState() == State.Starting)))
// sync VM Snapshots related transient states
List<VMSnapshotVO> expungingVMSnapshot = _vmSnapshotDao.listByInstanceId(vm.getId(), VMSnapshot.State.Expunging);
if( vm.getState() == State.RevertingToRunning || vm.getState() == State.RevertingToStopped
|| vm.getState() == State.RunningSnapshotting || vm.getState() == State.StoppedSnapshotting
|| expungingVMSnapshot.size() == 1){
s_logger.info("Found vm " + vm.getInstanceName() + " in state. " + vm.getState() + ", needs to sync VM snapshot state");
Long hostId = null;
if(info != null && info.getHostUuid() != null){
Host host = _hostDao.findByGuid(info.getHostUuid());
hostId = host == null ? (vm.getHostId() == null ? vm.getLastHostId() : vm.getHostId()) : host.getId();
}
if(!_vmSnapshotMgr.syncVMSnapshot(vm, null, hostId)){
s_logger.warn("Failed to sync VM in a transient snapshot related state: " + vm.getInstanceName());
continue;
}else{
s_logger.info("Successfully sync VM in a transient snapshot related state: " + vm.getInstanceName() + " to " + vm.getState());
}
if(expungingVMSnapshot.size() == 1)
_vmSnapshotMgr.syncVMSnapshot(vm, expungingVMSnapshot.get(0), hostId);
}
if ((info == null && (vm.getState() == State.Running || vm.getState() == State.Starting ))
|| (info != null && (info.state == State.Running && vm.getState() == State.Starting))
|| (info != null && (info.state == State.Running && vm.getState() == State.RevertingToRunning)))
{
s_logger.info("Found vm " + vm.getInstanceName() + " in inconsistent state. " + vm.getState() + " on CS while " + (info == null ? "Stopped" : "Running") + " on agent");
info = new AgentVmInfo(vm.getInstanceName(), getVmGuru(vm), vm, State.Stopped);
@ -1792,7 +1847,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
}
}
else if (info != null && (vm.getState() == State.Stopped || vm.getState() == State.Stopping
|| vm.isRemoved() || vm.getState() == State.Destroyed || vm.getState() == State.Expunging)) {
|| vm.isRemoved() || vm.getState() == State.Destroyed || vm.getState() == State.Expunging )) {
Host host = _hostDao.findByGuid(info.getHostUuid());
if (host != null){
s_logger.warn("Stopping a VM which is stopped/stopping/destroyed/expunging " + info.name);
@ -2267,6 +2322,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
Long clusterId = agent.getClusterId();
long agentId = agent.getId();
if (agent.getHypervisorType() == HypervisorType.XenServer) { // only for Xen
StartupRoutingCommand startup = (StartupRoutingCommand) cmd;
HashMap<String, Pair<String, State>> allStates = startup.getClusterVMStateChanges();
@ -2383,7 +2439,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
if (newServiceOffering == null) {
throw new InvalidParameterValueException("Unable to find a service offering with id " + newServiceOfferingId);
}
// Check that the VM is stopped
if (!vmInstance.getState().equals(State.Stopped)) {
s_logger.warn("Unable to upgrade virtual machine " + vmInstance.toString() + " in state " + vmInstance.getState());

View File

@ -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;
import com.cloud.utils.component.Manager;
import com.cloud.vm.VMInstanceVO;
public interface VMSnapshotManager extends VMSnapshotService, Manager {
public static final int VMSNAPSHOTMAX = 10;
/**
* Delete all VM snapshots belonging to one VM
* @param id, VM id
* @param type,
* @return true for success, false for failure
*/
boolean deleteAllVMSnapshots(long id, VMSnapshot.Type type);
/**
* Sync VM's state when VM in reverting or snapshotting, or VM snapshot in expunging state
* Used for fullsync after agent connects
*
* @param vm, the VM in question
* @param vmSnapshot, if this is not null, sync an VM snapshot in expunging state
* @param hostId
* @return true if succeeds, false if fails
*/
boolean syncVMSnapshot(VMInstanceVO vm, VMSnapshotVO vmSnapshot, Long hostId);
}

View File

@ -0,0 +1,828 @@
// 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 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.naming.ConfigurationException;
import org.apache.cloudstack.api.command.user.vmsnapshot.CreateVMSnapshotCmd;
import org.apache.cloudstack.api.command.user.vmsnapshot.DeleteVMSnapshotCmd;
import org.apache.cloudstack.api.command.user.vmsnapshot.ListVMSnapshotCmd;
import org.apache.cloudstack.api.command.user.vmsnapshot.RevertToSnapshotCmd;
import org.apache.log4j.Logger;
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.configuration.dao.ConfigurationDao;
import com.cloud.event.ActionEvent;
import com.cloud.event.EventTypes;
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.projects.Project.ListProjectResourcesCriteria;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.GuestOSVO;
import com.cloud.storage.Snapshot;
import com.cloud.storage.SnapshotVO;
import com.cloud.storage.StoragePoolVO;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.GuestOSDao;
import com.cloud.storage.dao.SnapshotDao;
import com.cloud.storage.dao.StoragePoolDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.UserContext;
import com.cloud.user.UserVO;
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.Pair;
import com.cloud.utils.Ternary;
import com.cloud.utils.component.ComponentLocator;
import com.cloud.utils.component.Inject;
import com.cloud.utils.component.Manager;
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;
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;
@Local(value = { VMSnapshotManager.class, VMSnapshotService.class })
public class VMSnapshotManagerImpl implements VMSnapshotManager, VMSnapshotService, Manager {
private static final Logger s_logger = Logger.getLogger(VMSnapshotManagerImpl.class);
String _name;
@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 StoragePoolDao _storagePoolDao;
@Inject SnapshotDao _snapshotDao;
@Inject VirtualMachineManager _itMgr;
@Inject ConfigurationDao _configDao;
int _vmSnapshotMax;
StateMachine2<State, VirtualMachine.Event, VirtualMachine> _stateMachine;
StateMachine2<VMSnapshot.State, VMSnapshot.Event, VMSnapshot> _vmSnapshottateMachine ;
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
_name = name;
ComponentLocator locator = ComponentLocator.getCurrentLocator();
_configDao = locator.getDao(ConfigurationDao.class);
if (_configDao == null) {
throw new ConfigurationException("Unable to get the configuration dao.");
}
s_logger.info("Snapshot Manager is configured.");
_vmSnapshotMax = NumbersUtil.parseInt(_configDao.getValue("vmsnapshot.max"), VMSNAPSHOTMAX);
_stateMachine = VirtualMachine.State.getStateMachine();
_vmSnapshottateMachine = VMSnapshot.State.getStateMachine();
return true;
}
@Override
public boolean start() {
return true;
}
@Override
public boolean stop() {
return true;
}
@Override
public List<VMSnapshotVO> listVMSnapshots(ListVMSnapshotCmd cmd) {
Account caller = getCaller();
List<Long> permittedAccounts = new ArrayList<Long>();
boolean listAll = cmd.listAll();
Long id = cmd.getId();
Long vmId = cmd.getVmId();
String state = cmd.getState();
String keyword = cmd.getKeyword();
String name = cmd.getVmSnapshotName();
String accountName = cmd.getAccountName();
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(
cmd.getDomainId(), cmd.isRecursive(), null);
_accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, listAll,
false);
Long domainId = domainIdRecursiveListProject.first();
Boolean isRecursive = domainIdRecursiveListProject.second();
ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
Filter searchFilter = new Filter(VMSnapshotVO.class, "created", false, cmd.getStartIndex(), cmd.getPageSizeVal());
SearchBuilder<VMSnapshotVO> sb = _vmSnapshotDao.createSearchBuilder();
_accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
sb.and("vm_id", sb.entity().getVmId(), SearchCriteria.Op.EQ);
sb.and("domain_id", sb.entity().getDomainId(), SearchCriteria.Op.EQ);
sb.and("status", sb.entity().getState(), SearchCriteria.Op.IN);
sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ);
sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
sb.and("display_name", sb.entity().getDisplayName(), SearchCriteria.Op.EQ);
sb.and("account_id", sb.entity().getAccountId(), SearchCriteria.Op.EQ);
sb.done();
SearchCriteria<VMSnapshotVO> sc = sb.create();
_accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
if (accountName != null && cmd.getDomainId() != null) {
Account account = _accountMgr.getActiveAccountByName(accountName, cmd.getDomainId());
sc.setParameters("account_id", account.getId());
}
if (vmId != null) {
sc.setParameters("vm_id", vmId);
}
if (domainId != null) {
sc.setParameters("domain_id", domainId);
}
if (state == null) {
VMSnapshot.State[] status = { VMSnapshot.State.Ready, VMSnapshot.State.Creating, VMSnapshot.State.Allocated,
VMSnapshot.State.Error, VMSnapshot.State.Expunging, VMSnapshot.State.Reverting };
sc.setParameters("status", (Object[]) status);
} else {
sc.setParameters("state", state);
}
if (name != null) {
sc.setParameters("display_name", name);
}
if (keyword != null) {
SearchCriteria<VMSnapshotVO> ssc = _vmSnapshotDao.createSearchCriteria();
ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%");
ssc.addOr("display_name", SearchCriteria.Op.LIKE, "%" + keyword + "%");
ssc.addOr("description", SearchCriteria.Op.LIKE, "%" + keyword + "%");
sc.addAnd("name", SearchCriteria.Op.SC, ssc);
}
if (id != null) {
sc.setParameters("id", id);
}
return _vmSnapshotDao.search(sc, searchFilter);
}
protected Account getCaller(){
return UserContext.current().getCaller();
}
@Override
public VMSnapshot allocVMSnapshot(CreateVMSnapshotCmd cmd) throws ResourceAllocationException {
Long vmId = cmd.getVmId();
String vsDisplayName = cmd.getDisplayName();
String vsDescription = cmd.getDescription();
Boolean snapshotMemory = cmd.snapshotMemory();
Account caller = getCaller();
// check if VM exists
UserVmVO userVmVo = _userVMDao.findById(vmId);
if (userVmVo == null) {
throw new InvalidParameterValueException("Creating VM snapshot failed due to VM:" + vmId + " is a system VM or does not exist");
}
// VM snapshot display name must be unique for a VM
String timeString = DateUtil.getDateDisplayString(DateUtil.GMT_TIMEZONE, new Date(), DateUtil.YYYYMMDD_FORMAT);
String vmSnapshotName = userVmVo.getInstanceName() + "_VS_" + timeString;
if (vsDisplayName == null) {
vsDisplayName = vmSnapshotName;
}
if(_vmSnapshotDao.findByName(vmId,vsDisplayName) != null){
throw new InvalidParameterValueException("Creating VM snapshot failed due to VM snapshot with name" + vsDisplayName + " already exists");
}
// check VM state
if (userVmVo.getState() != VirtualMachine.State.Running && userVmVo.getState() != VirtualMachine.State.Stopped) {
throw new InvalidParameterValueException("Creating vm snapshot failed due to VM:" + vmId + " is not in the running or Stopped state");
}
// for KVM, only allow snapshot with memory when VM is in running state
if(userVmVo.getHypervisorType() == HypervisorType.KVM && userVmVo.getState() == State.Running && !snapshotMemory){
throw new InvalidParameterValueException("KVM VM does not allow to take a disk-only snapshot when VM is in running state");
}
// check access
_accountMgr.checkAccess(caller, null, true, userVmVo);
// check max snapshot limit for per VM
if (_vmSnapshotDao.findByVm(vmId).size() >= _vmSnapshotMax) {
throw new CloudRuntimeException("Creating vm snapshot failed due to a VM can just have : " + _vmSnapshotMax
+ " VM snapshots. Please delete old ones");
}
// check if there are active volume snapshots tasks
List<VolumeVO> listVolumes = _volumeDao.findByInstance(vmId);
for (VolumeVO volume : listVolumes) {
List<SnapshotVO> activeSnapshots = _snapshotDao.listByInstanceId(volume.getInstanceId(), Snapshot.Status.Creating,
Snapshot.Status.CreatedOnPrimary, Snapshot.Status.BackingUp);
if (activeSnapshots.size() > 0) {
throw new CloudRuntimeException(
"There is other active volume snapshot tasks on the instance to which the volume is attached, please try again later.");
}
}
// check if there are other active VM snapshot tasks
if (checkActiveVMSnapshotTasks(vmId)) {
throw new CloudRuntimeException("There is other active vm snapshot tasks on the instance, please try again later");
}
VMSnapshot.Type vmSnapshotType = VMSnapshot.Type.Disk;
if(snapshotMemory && userVmVo.getState() == VirtualMachine.State.Running)
vmSnapshotType = VMSnapshot.Type.DiskAndMemory;
try {
VMSnapshotVO vmSnapshotVo = new VMSnapshotVO(userVmVo.getAccountId(), userVmVo.getDomainId(), vmId, vsDescription, vmSnapshotName,
vsDisplayName, userVmVo.getServiceOfferingId(), vmSnapshotType, null);
VMSnapshot vmSnapshot = _vmSnapshotDao.persist(vmSnapshotVo);
if (vmSnapshot == null) {
throw new CloudRuntimeException("Failed to create snapshot for vm: " + vmId);
}
return vmSnapshot;
} catch (Exception e) {
String msg = e.getMessage();
s_logger.error("Create vm snapshot record failed for vm: " + vmId + " due to: " + msg);
}
return null;
}
@Override
public String getName() {
return _name;
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_VM_SNAPSHOT_CREATE, eventDescription = "creating_vm_snapshot", async = true)
public VMSnapshot creatVMSnapshot(CreateVMSnapshotCmd cmd) {
Long vmId = cmd.getVmId();
UserVmVO userVm = _userVMDao.findById(vmId);
VMSnapshotVO vmSnapshot = _vmSnapshotDao.findById(cmd.getEntityId());
Long hostId = pickRunningHost(vmId);
transitState(vmSnapshot, VMSnapshot.Event.CreateRequested, userVm, VirtualMachine.Event.SnapshotRequested, hostId);
return createVmSnapshotInternal(userVm, vmSnapshot, hostId);
}
protected VMSnapshot createVmSnapshotInternal(UserVmVO userVm, VMSnapshotVO vmSnapshot, Long hostId) {
try {
CreateVMSnapshotAnswer answer = null;
GuestOSVO guestOS = _guestOSDao.findById(userVm.getGuestOSId());
// prepare snapshotVolumeTos
List<VolumeTO> 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());
answer = (CreateVMSnapshotAnswer) sendToPool(hostId, ccmd);
if (answer != null && answer.getResult()) {
processAnswer(vmSnapshot, userVm, answer, hostId);
transitState(vmSnapshot, VMSnapshot.Event.OperationSucceeded, userVm, VirtualMachine.Event.OperationSucceeded, hostId);
s_logger.debug("Create vm snapshot " + vmSnapshot.getName() + " succeeded for vm: " + userVm.getInstanceName());
}else{
String errMsg = answer.getDetails();
s_logger.error("Agent reports creating vm snapshot " + vmSnapshot.getName() + " failed for vm: " + userVm.getInstanceName() + " due to " + errMsg);
transitState(vmSnapshot, VMSnapshot.Event.OperationFailed, userVm, VirtualMachine.Event.OperationFailed, hostId);
}
return vmSnapshot;
} catch (Exception e) {
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());
}
}
}
protected List<VolumeTO> getVolumeTOList(Long vmId) {
List<VolumeTO> volumeTOs = new ArrayList<VolumeTO>();
List<VolumeVO> volumeVos = _volumeDao.findByInstance(vmId);
for (VolumeVO volume : volumeVos) {
StoragePoolVO pool = _storagePoolDao.findById(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<Long, VMSnapshotVO> snapshotMap = new HashMap<Long, VMSnapshotVO>();
List<VMSnapshotVO> 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);
}
private boolean vmSnapshotStateTransitTo(VMSnapshotVO vsnp, VMSnapshot.Event event) throws NoTransitionException {
return _vmSnapshottateMachine.transitTo(vsnp, event, null, _vmSnapshotDao);
}
@DB
protected boolean transitState(VMSnapshotVO vsnp, VMSnapshot.Event e1, VMInstanceVO vm, VirtualMachine.Event e2, Long hostId){
final Transaction txn = Transaction.currentTxn();
try {
txn.start();
vmSnapshotStateTransitTo(vsnp, e1);
vmStateTransitTo(vm, e2, hostId, null);
return txn.commit();
} catch (NoTransitionException e) {
txn.rollback();
return false;
}finally{
txn.close();
}
}
@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());
} else if (as instanceof RevertToVMSnapshotAnswer) {
RevertToVMSnapshotAnswer answer = (RevertToVMSnapshotAnswer) as;
finalizeRevert(vmSnapshot, answer.getVolumeTOs());
} 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<VolumeTO> VolumeTOs) {
// update volumes path
updateVolumePath(VolumeTOs);
// update children's parent snapshots
List<VMSnapshotVO> 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<VolumeTO> 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<VolumeTO> volumeToList) {
// update volumes path
updateVolumePath(volumeToList);
// update current snapshot, current snapshot is the one reverted to
VMSnapshotVO previousCurrent = _vmSnapshotDao.findCurrentSnapshotByVmId(vmSnapshot.getVmId());
previousCurrent.setCurrent(false);
vmSnapshot.setCurrent(true);
_vmSnapshotDao.persist(previousCurrent);
_vmSnapshotDao.persist(vmSnapshot);
}
private void updateVolumePath(List<VolumeTO> volumeTOs) {
for (VolumeTO volume : volumeTOs) {
if (volume.getPath() != null) {
VolumeVO volumeVO = _volumeDao.findById(volume.getId());
volumeVO.setPath(volume.getPath());
_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;
}
private boolean checkActiveVMSnapshotTasks(Long vmId){
List<VMSnapshotVO> activeVMSnapshots = _vmSnapshotDao.listByInstanceId(vmId,
VMSnapshot.State.Creating, VMSnapshot.State.Expunging,VMSnapshot.State.Reverting,VMSnapshot.State.Allocated);
return activeVMSnapshots.size() > 0;
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_VM_SNAPSHOT_DELETE, eventDescription = "delete_vm_snapshot", async=true)
public boolean deleteVMSnapshot(DeleteVMSnapshotCmd cmd) {
Long vmSnapshotId = cmd.getId();
Account caller = getCaller();
VMSnapshotVO vmSnapshot = _vmSnapshotDao.findById(vmSnapshotId);
if (vmSnapshot == null) {
throw new InvalidParameterValueException("unable to find the vm snapshot with id " + vmSnapshotId);
}
_accountMgr.checkAccess(caller, null, true, vmSnapshot);
// check VM snapshot states, only allow to delete vm snapshots in created and error state
if (VMSnapshot.State.Ready != vmSnapshot.getState() && VMSnapshot.State.Error != vmSnapshot.getState()) {
throw new InvalidParameterValueException("Can't delete the vm snapshotshot " + vmSnapshotId + " due to it is not in Created or Error State");
}
// check if there are other active VM snapshot tasks
if (checkActiveVMSnapshotTasks(vmSnapshot.getVmId())) {
throw new InvalidParameterValueException("There is other active vm snapshot tasks on the instance, please try again later");
}
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());
try {
vmSnapshotStateTransitTo(vmSnapshot,VMSnapshot.Event.ExpungeRequested);
Long hostId = pickRunningHost(vmSnapshot.getVmId());
// prepare snapshotVolumeTos
List<VolumeTO> 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());
DeleteVMSnapshotAnswer answer = (DeleteVMSnapshotAnswer) sendToPool(hostId, deleteSnapshotCommand);
if (answer != null && answer.getResult()) {
processAnswer(vmSnapshot, userVm, answer, hostId);
return true;
} else {
s_logger.error("Delete vm snapshot " + vmSnapshot.getName() + " of vm " + userVm.getInstanceName() + " failed due to " + answer.getDetails());
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());
}
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_VM_SNAPSHOT_REVERT, eventDescription = "revert_vm", async = true)
public UserVm revertToSnapshot(RevertToSnapshotCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException {
Long vmSnapshotId = cmd.getVmSnapShotId();
// check if VM snapshot exists in DB
VMSnapshotVO vmSnapshotVo = _vmSnapshotDao.findById(vmSnapshotId);
if (vmSnapshotVo == null) {
throw new InvalidParameterValueException(
"unable to find the vm snapshot with id " + vmSnapshotId);
}
Long vmId = vmSnapshotVo.getVmId();
UserVmVO userVm = _userVMDao.findById(vmId);
// check if VM exists
if (userVm == null) {
throw new InvalidParameterValueException("Revert vm to snapshot: "
+ vmSnapshotId + " failed due to vm: " + vmId
+ " is not found");
}
// check if there are other active VM snapshot tasks
if (checkActiveVMSnapshotTasks(vmId)) {
throw new InvalidParameterValueException("There is other active vm snapshot tasks on the instance, please try again later");
}
Account caller = getCaller();
_accountMgr.checkAccess(caller, null, true, vmSnapshotVo);
// VM should be in running or stopped states
if (userVm.getState() != VirtualMachine.State.Running
&& userVm.getState() != VirtualMachine.State.Stopped) {
throw new InvalidParameterValueException(
"VM Snapshot reverting failed due to vm is not in the state of Running or Stopped.");
}
// if snapshot is not created, error out
if (vmSnapshotVo.getState() != VMSnapshot.State.Ready) {
throw new InvalidParameterValueException(
"VM Snapshot reverting failed due to vm snapshot is not in the state of Created.");
}
UserVO callerUser = _userDao.findById(UserContext.current().getCallerUserId());
UserVmVO vm = null;
Long hostId = null;
Account owner = _accountDao.findById(vmSnapshotVo.getAccountId());
// start or stop VM first, if revert from stopped state to running state, or from running to stopped
if(userVm.getState() == VirtualMachine.State.Stopped && vmSnapshotVo.getType() == VMSnapshot.Type.DiskAndMemory){
try {
vm = _itMgr.advanceStart(userVm, new HashMap<VirtualMachineProfile.Param, Object>(), callerUser, owner);
hostId = vm.getHostId();
} catch (Exception e) {
s_logger.error("Start VM " + userVm.getInstanceName() + " before reverting failed due to " + e.getMessage());
throw new CloudRuntimeException(e.getMessage());
}
}else {
if(userVm.getState() == VirtualMachine.State.Running && vmSnapshotVo.getType() == VMSnapshot.Type.Disk){
try {
_itMgr.advanceStop(userVm, true, callerUser, owner);
} catch (Exception e) {
s_logger.error("Stop VM " + userVm.getInstanceName() + " before reverting failed due to " + e.getMessage());
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 (checkActiveVMSnapshotTasks(userVm.getId())) {
throw new InvalidParameterValueException("There is other active vm snapshot tasks on the instance, please try again later");
}
userVm = _userVMDao.findById(userVm.getId());
transitState(vmSnapshotVo, VMSnapshot.Event.RevertRequested, userVm, VirtualMachine.Event.RevertRequested, hostId);
return revertInternal(userVm, vmSnapshotVo, hostId);
}
private UserVm revertInternal(UserVmVO userVm, VMSnapshotVO vmSnapshotVo, Long hostId) {
try {
VMSnapshotVO snapshot = _vmSnapshotDao.findById(vmSnapshotVo.getId());
// prepare RevertToSnapshotCommand
List<VolumeTO> 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.getResult()) {
processAnswer(vmSnapshotVo, userVm, answer, hostId);
transitState(vmSnapshotVo, VMSnapshot.Event.OperationSucceeded, userVm, VirtualMachine.Event.OperationSucceeded, hostId);
} 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
transitState(vmSnapshotVo, VMSnapshot.Event.OperationFailed, userVm, VirtualMachine.Event.OperationFailed, hostId);
throw new CloudRuntimeException(errMsg);
}
} catch (Exception e) {
// for other exceptions, do not change VM 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());
}
return userVm;
}
private boolean vmStateTransitTo(VMInstanceVO vm, VirtualMachine.Event e, Long hostId, String reservationId) throws NoTransitionException {
vm.setReservationId(reservationId);
return _stateMachine.transitTo(vm, e, new Pair<Long, Long>(vm.getHostId(), hostId), _vmInstanceDao);
}
@Override
public VMSnapshot getVMSnapshotById(long id) {
VMSnapshotVO vmSnapshot = _vmSnapshotDao.findById(id);
return vmSnapshot;
}
private 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<VolumeVO> 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<HostVO> 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) {
VMSnapshotVO vmSnapshot = _vmSnapshotDao.findById(id);
Long vmId = vmSnapshot.getVmId();
UserVmVO vm = _userVMDao.findById(vmId);
return vm;
}
@Override
public boolean deleteAllVMSnapshots(long vmId, VMSnapshot.Type type) {
boolean result = true;
List<VMSnapshotVO> listVmSnapshots = _vmSnapshotDao.findByVm(vmId);
if (listVmSnapshots == null || listVmSnapshots.isEmpty()) {
return true;
}
for (VMSnapshotVO snapshot : listVmSnapshots) {
if(type != null && snapshot.getType() != type)
continue;
if (!deleteSnapshotInternal(snapshot)) {
result = false;
break;
}
}
return result;
}
@Override
public boolean syncVMSnapshot(VMInstanceVO vm, VMSnapshotVO vmSnapshot, Long hostId) {
try{
UserVmVO userVm = _userVMDao.findById(vm.getId());
if(userVm == null)
return false;
if(userVm.getState() == State.RevertingToRunning || userVm.getState() == State.RevertingToStopped){
List<VMSnapshotVO> vmSnapshots = _vmSnapshotDao.listByInstanceId(userVm.getId(), VMSnapshot.State.Reverting);
if(vmSnapshots.size() != 1){
s_logger.error("VM:" + userVm.getInstanceName()+ " expected to have one VM snapshot in reverting state but actually has " + vmSnapshots.size());
return false;
}
// send revert command again
revertInternal(userVm, vmSnapshots.get(0), hostId);
return true;
}else if (userVm.getState() == State.RunningSnapshotting || userVm.getState() == State.StoppedSnapshotting){
List<VMSnapshotVO> vmSnapshots = _vmSnapshotDao.listByInstanceId(userVm.getId(), VMSnapshot.State.Creating);
if(vmSnapshots.size() != 1){
s_logger.error("VM:" + userVm.getInstanceName()+ " expected to have one VM snapshot in creating state but actually has " + vmSnapshots.size());
return false;
}
// send create vm snapshot command again
createVmSnapshotInternal(userVm, vmSnapshots.get(0), hostId);
return true;
}else if (vmSnapshot != null && vmSnapshot.getState() == VMSnapshot.State.Expunging){
return deleteSnapshotInternal(vmSnapshot);
}
}catch (Exception e) {
s_logger.error(e.getMessage(),e);
return false;
}
return false;
}
}

View File

@ -0,0 +1,39 @@
// 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 java.util.List;
import com.cloud.utils.db.GenericDao;
import com.cloud.utils.fsm.StateDao;
import com.cloud.vm.snapshot.VMSnapshot;
import com.cloud.vm.snapshot.VMSnapshotVO;
public interface VMSnapshotDao extends GenericDao<VMSnapshotVO, Long>, StateDao<VMSnapshot.State, VMSnapshot.Event, VMSnapshot> {
List<VMSnapshotVO> findByVm(Long vmId);
List<VMSnapshotVO> listExpungingSnapshot();
List<VMSnapshotVO> listByInstanceId(Long vmId, VMSnapshot.State... status);
VMSnapshotVO findCurrentSnapshotByVmId(Long vmId);
List<VMSnapshotVO> listByParent(Long vmSnapshotId);
VMSnapshotVO findByName(Long vm_id, String name);
}

View File

@ -0,0 +1,160 @@
// 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 java.util.Date;
import java.util.List;
import javax.ejb.Local;
import org.apache.log4j.Logger;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.SearchCriteria.Op;
import com.cloud.utils.db.UpdateBuilder;
import com.cloud.vm.snapshot.VMSnapshot;
import com.cloud.vm.snapshot.VMSnapshot.Event;
import com.cloud.vm.snapshot.VMSnapshot.State;
import com.cloud.vm.snapshot.VMSnapshotVO;
@Local(value = { VMSnapshotDao.class })
public class VMSnapshotDaoImpl extends GenericDaoBase<VMSnapshotVO, Long>
implements VMSnapshotDao {
private static final Logger s_logger = Logger.getLogger(VMSnapshotDaoImpl.class);
private final SearchBuilder<VMSnapshotVO> SnapshotSearch;
private final SearchBuilder<VMSnapshotVO> ExpungingSnapshotSearch;
private final SearchBuilder<VMSnapshotVO> SnapshotStatusSearch;
private final SearchBuilder<VMSnapshotVO> AllFieldsSearch;
protected VMSnapshotDaoImpl() {
AllFieldsSearch = createSearchBuilder();
AllFieldsSearch.and("state", AllFieldsSearch.entity().getState(), Op.EQ);
AllFieldsSearch.and("accountId", AllFieldsSearch.entity().getAccountId(), Op.EQ);
AllFieldsSearch.and("vm_id", AllFieldsSearch.entity().getVmId(), Op.EQ);
AllFieldsSearch.and("deviceId", AllFieldsSearch.entity().getVmId(), Op.EQ);
AllFieldsSearch.and("id", AllFieldsSearch.entity().getId(), Op.EQ);
AllFieldsSearch.and("removed", AllFieldsSearch.entity().getState(), Op.EQ);
AllFieldsSearch.and("parent", AllFieldsSearch.entity().getParent(), Op.EQ);
AllFieldsSearch.and("current", AllFieldsSearch.entity().getCurrent(), Op.EQ);
AllFieldsSearch.and("vm_snapshot_type", AllFieldsSearch.entity().getType(), Op.EQ);
AllFieldsSearch.and("updatedCount", AllFieldsSearch.entity().getUpdatedCount(), Op.EQ);
AllFieldsSearch.and("display_name", AllFieldsSearch.entity().getDisplayName(), SearchCriteria.Op.EQ);
AllFieldsSearch.and("name", AllFieldsSearch.entity().getName(), SearchCriteria.Op.EQ);
AllFieldsSearch.done();
SnapshotSearch = createSearchBuilder();
SnapshotSearch.and("vm_id", SnapshotSearch.entity().getVmId(),
SearchCriteria.Op.EQ);
SnapshotSearch.done();
ExpungingSnapshotSearch = createSearchBuilder();
ExpungingSnapshotSearch.and("state", ExpungingSnapshotSearch.entity()
.getState(), SearchCriteria.Op.EQ);
ExpungingSnapshotSearch.and("removed", ExpungingSnapshotSearch.entity()
.getRemoved(), SearchCriteria.Op.NULL);
ExpungingSnapshotSearch.done();
SnapshotStatusSearch = createSearchBuilder();
SnapshotStatusSearch.and("vm_id", SnapshotStatusSearch.entity()
.getVmId(), SearchCriteria.Op.EQ);
SnapshotStatusSearch.and("state", SnapshotStatusSearch.entity()
.getState(), SearchCriteria.Op.IN);
SnapshotStatusSearch.done();
}
@Override
public List<VMSnapshotVO> findByVm(Long vmId) {
SearchCriteria<VMSnapshotVO> sc = SnapshotSearch.create();
sc.setParameters("vm_id", vmId);
return listBy(sc, null);
}
@Override
public List<VMSnapshotVO> listExpungingSnapshot() {
SearchCriteria<VMSnapshotVO> sc = ExpungingSnapshotSearch.create();
sc.setParameters("state", State.Expunging);
return listBy(sc, null);
}
@Override
public List<VMSnapshotVO> listByInstanceId(Long vmId, State... status) {
SearchCriteria<VMSnapshotVO> sc = SnapshotStatusSearch.create();
sc.setParameters("vm_id", vmId);
sc.setParameters("state", (Object[]) status);
return listBy(sc, null);
}
@Override
public VMSnapshotVO findCurrentSnapshotByVmId(Long vmId) {
SearchCriteria<VMSnapshotVO> sc = AllFieldsSearch.create();
sc.setParameters("vm_id", vmId);
sc.setParameters("current", 1);
return findOneBy(sc);
}
@Override
public List<VMSnapshotVO> listByParent(Long vmSnapshotId) {
SearchCriteria<VMSnapshotVO> sc = AllFieldsSearch.create();
sc.setParameters("parent", vmSnapshotId);
sc.setParameters("state", State.Ready );
return listBy(sc, null);
}
@Override
public VMSnapshotVO findByName(Long vm_id, String name) {
SearchCriteria<VMSnapshotVO> sc = AllFieldsSearch.create();
sc.setParameters("vm_id", vm_id);
sc.setParameters("display_name", name );
return null;
}
@Override
public boolean updateState(State currentState, Event event, State nextState, VMSnapshot vo, Object data) {
Long oldUpdated = vo.getUpdatedCount();
Date oldUpdatedTime = vo.getUpdated();
SearchCriteria<VMSnapshotVO> sc = AllFieldsSearch.create();
sc.setParameters("id", vo.getId());
sc.setParameters("state", currentState);
sc.setParameters("updatedCount", vo.getUpdatedCount());
vo.incrUpdatedCount();
UpdateBuilder builder = getUpdateBuilder(vo);
builder.set(vo, "state", nextState);
builder.set(vo, "updated", new Date());
int rows = update((VMSnapshotVO)vo, sc);
if (rows == 0 && s_logger.isDebugEnabled()) {
VMSnapshotVO dbVol = findByIdIncludingRemoved(vo.getId());
if (dbVol != null) {
StringBuilder str = new StringBuilder("Unable to update ").append(vo.toString());
str.append(": DB Data={id=").append(dbVol.getId()).append("; state=").append(dbVol.getState()).append("; updatecount=").append(dbVol.getUpdatedCount()).append(";updatedTime=").append(dbVol.getUpdated());
str.append(": New Data={id=").append(vo.getId()).append("; state=").append(nextState).append("; event=").append(event).append("; updatecount=").append(vo.getUpdatedCount()).append("; updatedTime=").append(vo.getUpdated());
str.append(": stale Data={id=").append(vo.getId()).append("; state=").append(currentState).append("; event=").append(event).append("; updatecount=").append(oldUpdated).append("; updatedTime=").append(oldUpdatedTime);
} else {
s_logger.debug("Unable to update VM snapshot: id=" + vo.getId() + ", as there is no such snapshot exists in the database anymore");
}
}
return rows > 0;
}
}

View File

@ -0,0 +1,182 @@
// 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 java.util.ArrayList;
import java.util.List;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.command.user.vmsnapshot.CreateVMSnapshotCmd;
import org.junit.Test;
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.to.VolumeTO;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.exception.AgentUnavailableException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.OperationTimedoutException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.HypervisorGuruManager;
import com.cloud.storage.GuestOSVO;
import com.cloud.storage.Snapshot;
import com.cloud.storage.SnapshotVO;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.GuestOSDao;
import com.cloud.storage.dao.SnapshotDao;
import com.cloud.storage.dao.StoragePoolDao;
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.exception.CloudRuntimeException;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachine.Event;
import com.cloud.vm.VirtualMachine.State;
import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.VMInstanceDao;
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import org.junit.Before;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
public class VMSnapshotManagerTest {
@Spy VMSnapshotManagerImpl _vmSnapshotMgr = new VMSnapshotManagerImpl();
@Mock Account admin;
@Mock VMSnapshotDao _vmSnapshotDao;
@Mock VolumeDao _volumeDao;
@Mock AccountDao _accountDao;
@Mock VMInstanceDao _vmInstanceDao;
@Mock UserVmDao _userVMDao;
@Mock HostDao _hostDao;
@Mock UserDao _userDao;
@Mock AgentManager _agentMgr;
@Mock HypervisorGuruManager _hvGuruMgr;
@Mock AccountManager _accountMgr;
@Mock GuestOSDao _guestOSDao;
@Mock StoragePoolDao _storagePoolDao;
@Mock SnapshotDao _snapshotDao;
@Mock VirtualMachineManager _itMgr;
@Mock ConfigurationDao _configDao;
int _vmSnapshotMax = 10;
@Mock CreateVMSnapshotCmd createCmd;
private static long TEST_VM_ID = 3L;
@Mock UserVmVO vmMock;
@Mock VolumeVO volumeMock;
@Before
public void setup(){
MockitoAnnotations.initMocks(this);
doReturn(admin).when(_vmSnapshotMgr).getCaller();
_vmSnapshotMgr._accountDao = _accountDao;
_vmSnapshotMgr._userVMDao = _userVMDao;
_vmSnapshotMgr._vmSnapshotDao = _vmSnapshotDao;
_vmSnapshotMgr._volumeDao = _volumeDao;
_vmSnapshotMgr._accountMgr = _accountMgr;
_vmSnapshotMgr._snapshotDao = _snapshotDao;
_vmSnapshotMgr._guestOSDao = _guestOSDao;
doNothing().when(_accountMgr).checkAccess(any(Account.class), any(AccessType.class),
any(Boolean.class), any(ControlledEntity.class));
_vmSnapshotMgr._vmSnapshotMax = _vmSnapshotMax;
when(createCmd.getVmId()).thenReturn(TEST_VM_ID);
when(createCmd.getDisplayName()).thenReturn("testName");
when(_userVMDao.findById(anyLong())).thenReturn(vmMock);
when(_vmSnapshotDao.findByName(anyLong(), anyString())).thenReturn(null);
when(_vmSnapshotDao.findByVm(anyLong())).thenReturn(new ArrayList<VMSnapshotVO>());
List<VolumeVO> mockVolumeList = new ArrayList<VolumeVO>();
mockVolumeList.add(volumeMock);
when(volumeMock.getInstanceId()).thenReturn(TEST_VM_ID);
when(_volumeDao.findByInstance(anyLong())).thenReturn(mockVolumeList);
when(vmMock.getInstanceName()).thenReturn("i-3-VM-TEST");
when(vmMock.getState()).thenReturn(State.Running);
when(_guestOSDao.findById(anyLong())).thenReturn(mock(GuestOSVO.class));
}
// vmId null case
@Test(expected=InvalidParameterValueException.class)
public void testAllocVMSnapshotF1() throws ResourceAllocationException{
when(_userVMDao.findById(TEST_VM_ID)).thenReturn(null);
_vmSnapshotMgr.allocVMSnapshot(createCmd);
}
// vm state not in [running, stopped] case
@Test(expected=InvalidParameterValueException.class)
public void testAllocVMSnapshotF2() throws ResourceAllocationException{
when(vmMock.getState()).thenReturn(State.Starting);
_vmSnapshotMgr.allocVMSnapshot(createCmd);
}
// max snapshot limit case
@SuppressWarnings("unchecked")
@Test(expected=CloudRuntimeException.class)
public void testAllocVMSnapshotF3() throws ResourceAllocationException{
List<VMSnapshotVO> mockList = mock(List.class);
when(mockList.size()).thenReturn(10);
when(_vmSnapshotDao.findByVm(TEST_VM_ID)).thenReturn(mockList);
_vmSnapshotMgr.allocVMSnapshot(createCmd);
}
// active volume snapshots case
@SuppressWarnings("unchecked")
@Test(expected=CloudRuntimeException.class)
public void testAllocVMSnapshotF4() throws ResourceAllocationException{
List<SnapshotVO> mockList = mock(List.class);
when(mockList.size()).thenReturn(1);
when(_snapshotDao.listByInstanceId(TEST_VM_ID,Snapshot.Status.Creating,
Snapshot.Status.CreatedOnPrimary, Snapshot.Status.BackingUp)).thenReturn(mockList);
_vmSnapshotMgr.allocVMSnapshot(createCmd);
}
@Test
public void testCreateVMSnapshot() throws AgentUnavailableException, OperationTimedoutException{
when(_vmSnapshotDao.findCurrentSnapshotByVmId(anyLong())).thenReturn(null);
doReturn(new ArrayList<VolumeTO>()).when(_vmSnapshotMgr).getVolumeTOList(anyLong());
doReturn(new CreateVMSnapshotAnswer(null,true,"")).when(_vmSnapshotMgr).sendToPool(anyLong(), any(CreateVMSnapshotCommand.class));
doReturn(true).when(_vmSnapshotMgr).transitState(any(VMSnapshotVO.class),
any(VMSnapshot.Event.class), any(VMInstanceVO.class), any(VirtualMachine.Event.class), anyLong());
doNothing().when(_vmSnapshotMgr).processAnswer(any(VMSnapshotVO.class),
any(UserVmVO.class), any(Answer.class), anyLong());
_vmSnapshotMgr.createVmSnapshotInternal(vmMock, mock(VMSnapshotVO.class), 5L);
}
}

View File

@ -2543,6 +2543,36 @@ INSERT INTO `cloud`.`counter` (id, uuid, source, name, value,created) VALUES (1,
INSERT INTO `cloud`.`counter` (id, uuid, source, name, value,created) VALUES (2, UUID(), 'snmp','Linux System CPU - percentage', '1.3.6.1.4.1.2021.11.10.0', now());
INSERT INTO `cloud`.`counter` (id, uuid, source, name, value,created) VALUES (3, UUID(), 'snmp','Linux CPU Idle - percentage', '1.3.6.1.4.1.2021.11.11.0', now());
INSERT INTO `cloud`.`counter` (id, uuid, source, name, value,created) VALUES (100, UUID(), 'netscaler','Response Time - microseconds', 'RESPTIME', now());
CREATE TABLE `cloud`.`vm_snapshots` (
`id` bigint(20) unsigned NOT NULL auto_increment COMMENT 'Primary Key',
`uuid` varchar(40) NOT NULL,
`name` varchar(255) NOT NULL,
`display_name` varchar(255) default NULL,
`description` varchar(255) default NULL,
`vm_id` bigint(20) unsigned NOT NULL,
`account_id` bigint(20) unsigned NOT NULL,
`domain_id` bigint(20) unsigned NOT NULL,
`vm_snapshot_type` varchar(32) default NULL,
`state` varchar(32) NOT NULL,
`parent` bigint unsigned default NULL,
`current` int(1) unsigned default NULL,
`update_count` bigint unsigned NOT NULL DEFAULT 0,
`updated` datetime default NULL,
`created` datetime default NULL,
`removed` datetime default NULL,
PRIMARY KEY (`id`),
CONSTRAINT UNIQUE KEY `uc_vm_snapshots_uuid` (`uuid`),
INDEX `vm_snapshots_name` (`name`),
INDEX `vm_snapshots_vm_id` (`vm_id`),
INDEX `vm_snapshots_account_id` (`account_id`),
INDEX `vm_snapshots_display_name` (`display_name`),
INDEX `vm_snapshots_removed` (`removed`),
INDEX `vm_snapshots_parent` (`parent`),
CONSTRAINT `fk_vm_snapshots_vm_id__vm_instance_id` FOREIGN KEY `fk_vm_snapshots_vm_id__vm_instance_id` (`vm_id`) REFERENCES `vm_instance` (`id`),
CONSTRAINT `fk_vm_snapshots_account_id__account_id` FOREIGN KEY `fk_vm_snapshots_account_id__account_id` (`account_id`) REFERENCES `account` (`id`),
CONSTRAINT `fk_vm_snapshots_domain_id__domain_id` FOREIGN KEY `fk_vm_snapshots_domain_id__domain_id` (`domain_id`) REFERENCES `domain` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SET foreign_key_checks = 1;

View File

@ -1931,7 +1931,7 @@ div.detail-group.actions td {
}
.detail-group table td.detail-actions {
width: 55%;
width: 59%;
height: 26px;
}

View File

@ -1431,6 +1431,16 @@ dictionary = {
'label.delete.NiciraNvp': '<fmt:message key="label.delete.NiciraNvp" />',
'label.nicira.controller.address': '<fmt:message key="label.nicira.controller.address" />',
'label.nicira.transportzoneuuid': '<fmt:message key="label.nicira.transportzoneuuid" />',
'label.nicira.l3gatewayserviceuuid': '<fmt:message key="label.nicira.l3gatewayserviceuuid" />'
'label.nicira.l3gatewayserviceuuid': '<fmt:message key="label.nicira.l3gatewayserviceuuid" />',
'label.vmsnapshot.current': '<fmt:message key="label.vmsnapshot.current" />',
'label.vmsnapshot.parentname': '<fmt:message key="label.vmsnapshot.parentname" />',
'label.vmsnapshot.type': '<fmt:message key="label.vmsnapshot.type" />',
'label.vmsnapshot.memory': '<fmt:message key="label.vmsnapshot.memory" />',
'label.vmsnapshot': '<fmt:message key="label.vmsnapshot" />',
'label.action.vmsnapshot.create': '<fmt:message key="label.action.vmsnapshot.create" />',
'label.action.vmsnapshot.delete': '<fmt:message key="label.action.vmsnapshot.delete" />',
'label.action.vmsnapshot.revert': '<fmt:message key="label.action.vmsnapshot.revert" />',
'message.action.vmsnapshot.delete': '<fmt:message key="message.action.vmsnapshot.delete" />',
'message.action.vmsnapshot.revert': '<fmt:message key="message.action.vmsnapshot.revert" />'
};
</script>

View File

@ -1673,6 +1673,8 @@ under the License.
<script type="text/javascript" src="scripts/system.js?t=<%=now%>"></script>
<script type="text/javascript" src="scripts/domains.js?t=<%=now%>"></script>
<script type="text/javascript" src="scripts/docs.js?t=<%=now%>"></script>
<script type="text/javascript" src="scripts/domains.js?t=<%=now%>"></script>
<script type="text/javascript" src="scripts/vm_snapshots.js?t=<%=now%>"></script>
</body>
</html>
<jsp:include page="dictionary.jsp" />

View File

@ -48,7 +48,15 @@
displayname: { label: 'label.display.name' },
zonename: { label: 'label.zone.name' },
state: {
label: 'label.state',
label: 'label.state',
converter: function(data) {
if(data == 'StoppedSnapshotting' || data == 'RunningSnapshotting')
return 'Snapshotting';
if(data == 'RevertingToRunning' || data == 'RevertingToStopped'){
return 'Reverting';
}
return data;
},
indicator: {
'Running': 'on',
'Stopped': 'off',
@ -153,6 +161,194 @@
notification: {
poll: pollAsyncJobResult
}
},
start: {
label: 'label.action.start.instance' ,
action: function(args) {
$.ajax({
url: createURL("startVirtualMachine&id=" + args.context.instances[0].id),
dataType: "json",
async: true,
success: function(json) {
var jid = json.startvirtualmachineresponse.jobid;
args.response.success(
{_custom:
{jobId: jid,
getUpdatedItem: function(json) {
return json.queryasyncjobresultresponse.jobresult.virtualmachine;
},
getActionFilter: function() {
return vmActionfilter;
}
}
}
);
}
});
},
messages: {
confirm: function(args) {
return 'message.action.start.instance';
},
notification: function(args) {
return 'label.action.start.instance';
},
complete: function(args) {
if(args.password != null) {
alert('Password of the VM is ' + args.password);
}
return 'label.action.start.instance';
}
},
notification: {
poll: pollAsyncJobResult
}
},
stop: {
label: 'label.action.stop.instance',
addRow: 'false',
createForm: {
title: 'label.action.stop.instance',
desc: 'message.action.stop.instance',
fields: {
forced: {
label: 'force.stop',
isBoolean: true,
isChecked: false
}
}
},
action: function(args) {
var array1 = [];
array1.push("&forced=" + (args.data.forced == "on"));
$.ajax({
url: createURL("stopVirtualMachine&id=" + args.context.instances[0].id + array1.join("")),
dataType: "json",
async: true,
success: function(json) {
var jid = json.stopvirtualmachineresponse.jobid;
args.response.success(
{_custom:
{jobId: jid,
getUpdatedItem: function(json) {
return json.queryasyncjobresultresponse.jobresult.virtualmachine;
},
getActionFilter: function() {
return vmActionfilter;
}
}
}
);
}
});
},
messages: {
confirm: function(args) {
return 'message.action.stop.instance';
},
notification: function(args) {
return 'label.action.stop.instance';
}
},
notification: {
poll: pollAsyncJobResult
}
},
restart: {
label: 'instances.actions.reboot.label',
action: function(args) {
$.ajax({
url: createURL("rebootVirtualMachine&id=" + args.context.instances[0].id),
dataType: "json",
async: true,
success: function(json) {
var jid = json.rebootvirtualmachineresponse.jobid;
args.response.success(
{_custom:
{jobId: jid,
getUpdatedItem: function(json) {
return json.queryasyncjobresultresponse.jobresult.virtualmachine;
},
getActionFilter: function() {
return vmActionfilter;
}
}
}
);
}
});
},
messages: {
confirm: function(args) {
return 'message.action.reboot.instance';
},
notification: function(args) {
return 'instances.actions.reboot.label';
}
},
notification: {
poll: pollAsyncJobResult
}
},
destroy: {
label: 'label.action.destroy.instance',
messages: {
confirm: function(args) {
return 'message.action.destroy.instance';
},
notification: function(args) {
return 'label.action.destroy.instance';
}
},
action: function(args) {
$.ajax({
url: createURL("destroyVirtualMachine&id=" + args.context.instances[0].id),
dataType: "json",
async: true,
success: function(json) {
var jid = json.destroyvirtualmachineresponse.jobid;
args.response.success(
{_custom:
{jobId: jid,
getUpdatedItem: function(json) {
return json.queryasyncjobresultresponse.jobresult.virtualmachine;
},
getActionFilter: function() {
return vmActionfilter;
}
}
}
);
}
});
},
notification: {
poll: pollAsyncJobResult
}
},
restore: {
label: 'label.action.restore.instance',
messages: {
confirm: function(args) {
return 'message.action.restore.instance';
},
notification: function(args) {
return 'label.action.restore.instance';
}
},
action: function(args) {
$.ajax({
url: createURL("recoverVirtualMachine&id=" + args.context.instances[0].id),
dataType: "json",
async: true,
success: function(json) {
var item = json.recovervirtualmachineresponse.virtualmachine;
args.response.success({data:item});
}
});
}
}
},
@ -231,7 +427,7 @@
detailView: {
name: 'Instance details',
viewAll: { path: 'storage.volumes', label: 'label.volumes' },
viewAll: [{ path: 'storage.volumes', label: 'label.volumes' }, { path: 'vmsnapshots', label: 'Snapshots' } ],
tabFilter: function(args) {
var hiddenTabs = [];
@ -409,6 +605,70 @@
poll: pollAsyncJobResult
}
},
snapshot: {
messages: {
notification: function(args) {
return 'label.action.vmsnapshot.create';
}
},
label: 'label.action.vmsnapshot.create',
addRow: 'false',
createForm: {
title: 'label.action.vmsnapshot.create',
fields: {
name: {
label: 'label.name',
isInput: true
},
description: {
label: 'label.description',
isTextarea: true
},
snapshotMemory: {
label: 'label.vmsnapshot.memory',
isBoolean: true,
isChecked: false
}
}
},
action: function(args) {
var array1 = [];
array1.push("&snapshotmemory=" + (args.data.snapshotMemory == "on"));
var displayname = args.data.name;
if (displayname != null && displayname.length > 0) {
array1.push("&name=" + todb(displayname));
}
var description = args.data.description;
if (description != null && description.length > 0) {
array1.push("&description=" + todb(description));
}
$.ajax({
url: createURL("createVMSnapshot&virtualmachineid=" + args.context.instances[0].id + array1.join("")),
dataType: "json",
async: true,
success: function(json) {
var jid = json.createvmsnapshotresponse.jobid;
args.response.success({
_custom: {
jobId: jid,
getUpdatedItem: function(json) {
return json.queryasyncjobresultresponse.jobresult.virtualmachine;
},
getActionFilter: function() {
return vmActionfilter;
}
}
});
}
});
},
notification: {
pool: pollAsyncJobResult
}
},
destroy: {
label: 'label.action.destroy.instance',
compactLabel: 'label.destroy',
@ -1232,6 +1492,7 @@
else if (jsonObj.state == 'Running') {
allowedActions.push("stop");
allowedActions.push("restart");
allowedActions.push("snapshot");
allowedActions.push("destroy");
allowedActions.push("changeService");
allowedActions.push("reset");
@ -1257,7 +1518,7 @@
allowedActions.push("start");
allowedActions.push("destroy");
allowedActions.push("reset");
allowedActions.push("snapshot");
if(isAdmin())
allowedActions.push("migrateToAnotherStorage");

View File

@ -892,14 +892,20 @@
$actions.prependTo($firstRow.closest('div.detail-group').closest('.details'));
}
if (detailViewArgs.viewAll && showViewAll) {
if( !(detailViewArgs.viewAll instanceof Array)){
detailViewArgs.viewAll = [detailViewArgs.viewAll];
}
$.each(detailViewArgs.viewAll, function(n, view){
$('<div>')
.addClass('view-all')
.append(
$('<a>')
.attr({ href: '#' })
.data('detail-view-link-view-all', detailViewArgs.viewAll)
.css('padding','0 1px')
.data('detail-view-link-view-all', view)
.append(
$('<span>').html(_l('label.view') + ' ' + _l(detailViewArgs.viewAll.label))
$('<span>').html(_l('label.view') + ' ' + _l(view.label))
)
)
.append(
@ -908,9 +914,10 @@
.appendTo(
$('<td>')
.addClass('view-all')
.css('padding','9px 3px 8px 0')
.appendTo($actions.find('tr'))
);
});
}
}

196
ui/scripts/vm_snapshots.js Normal file
View File

@ -0,0 +1,196 @@
// 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.
(function($, cloudStack) {
cloudStack.sections.vmsnapshots = {
title: 'label.vmsnapshot',
id: 'vmsnapshots',
listView: {
id: 'vmsnapshots',
isMaximized: true,
fields: {
displayname: {
label: 'label.name'
},
state: {
label: 'label.state',
indicator: {
'Ready': 'on',
'Error': 'off'
}
},
type:{
label: 'label.vmsnapshot.type'
},
current:{
label: 'label.vmsnapshot.current',
converter: cloudStack.converters.toBooleanText
},
parentName:{
label: 'label.vmsnapshot.parentname'
},
created: {
label: 'label.date',
converter: cloudStack.converters.toLocalDate
}
},
dataProvider: function(args) {
var apiCmd = "listVMSnapshot&listAll=true";
if (args.context != null) {
if ("instances" in args.context) {
apiCmd += "&virtualmachineid=" + args.context.instances[0].id;
}
}
$.ajax({
url: createURL(apiCmd),
dataType: "json",
async: true,
success: function(json) {
var jsonObj;
jsonObj = json.listvmsnapshotresponse.vmSnapshot;
args.response.success({
data: jsonObj
});
}
});
},
//dataProvider end
detailView: {
tabs: {
details: {
title: 'label.details',
fields: {
id: {
label: 'label.id'
},
name: {
label: 'label.name'
},
displayname: {
label: 'label.display.name',
},
type: {
label: 'label.vmsnapshot.type',
},
description: {
label: 'label.description',
},
state: {
label: 'label.state',
indicator: {
'Ready': 'on',
'Error': 'off'
}
},
current: {
label: 'label.vmsnapshot.current',
converter: cloudStack.converters.toBooleanText
},
parentName: {
label: 'label.vmsnapshot.parentname'
},
created: {
label: 'label.date',
converter: cloudStack.converters.toLocalDate
},
},
dataProvider: function(args) {
$.ajax({
url: createURL("listVMSnapshot&listAll=true&id=" + args.context.vmsnapshots[0].id),
dataType: "json",
async: true,
success: function(json) {
var jsonObj;
jsonObj = json.listvmsnapshotresponse.vmSnapshot[0];
args.response.success({
//actionFilter: vmActionfilter,
data: jsonObj
});
}
});
},
tags: cloudStack.api.tags({ resourceType: 'VMSnapshot', contextId: 'vmsnapshots' })
}
},
actions: {
//delete a snapshot
remove: {
label: 'label.action.vmsnapshot.delete',
messages: {
confirm: function(args) {
return 'message.action.vmsnapshot.delete';
},
notification: function(args) {
return 'label.action.vmsnapshot.delete';
}
},
action: function(args) {
$.ajax({
url: createURL("deleteVMSnapshot&vmsnapshotid=" + args.context.vmsnapshots[0].id),
dataType: "json",
async: true,
success: function(json) {
var jid = json.deletevmsnapshotresponse.jobid;
args.response.success(
{_custom:
{jobId: jid}
}
);
}
});
},
notification: {
poll: pollAsyncJobResult
}
},
restart: {
label: 'label.action.vmsnapshot.revert',
messages: {
confirm: function(args) {
return 'label.action.vmsnapshot.revert';
},
notification: function(args) {
return 'message.action.vmsnapshot.revert';
}
},
action: function(args) {
$.ajax({
url: createURL("revertToSnapshot&vmsnapshotid=" + args.context.vmsnapshots[0].id),
dataType: "json",
async: true,
success: function(json) {
var jid = json.reverttosnapshotresponse.jobid;
args.response.success({
_custom: {
jobId: jid
}
});
}
});
},
notification: {
poll: pollAsyncJobResult
}
}
}
}
//detailview end
}
}
})(jQuery, cloudStack);

View File

@ -58,6 +58,8 @@ import com.vmware.vim25.PropertySpec;
import com.vmware.vim25.SelectionSpec;
import com.vmware.vim25.TraversalSpec;
import com.vmware.vim25.VirtualMachineConfigSpec;
import com.vmware.vim25.VirtualMachineSnapshotInfo;
import com.vmware.vim25.VirtualMachineSnapshotTree;
import com.vmware.vim25.VirtualNicManagerNetConfig;
import com.vmware.vim25.NasDatastoreInfo;
@ -949,4 +951,20 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
HostRuntimeInfo runtimeInfo = (HostRuntimeInfo)_context.getServiceUtil().getDynamicProperty(_mor, "runtime");
return runtimeInfo.getConnectionState() == HostSystemConnectionState.connected;
}
public boolean revertToSnapshot(ManagedObjectReference morSnapshot)
throws Exception {
ManagedObjectReference morTask = _context.getService()
.revertToSnapshot_Task(morSnapshot, _mor, false);
String result = _context.getServiceUtil().waitForTask(morTask);
if (result.equals("sucess")) {
_context.waitForTaskProgressDone(morTask);
return true;
} else {
s_logger.error("VMware revert to snapshot failed due to "
+ TaskMO.getTaskFailureInfo(_context, morTask));
}
return false;
}
}

View File

@ -405,6 +405,26 @@ public class VirtualMachineMO extends BaseMO {
return false;
}
public boolean revertToSnapshot(String snapshotName) throws Exception {
ManagedObjectReference morSnapshot = getSnapshotMor(snapshotName);
if (morSnapshot == null) {
s_logger.warn("Unable to find snapshot: " + snapshotName);
return false;
}
ManagedObjectReference morTask = _context.getService()
.revertToSnapshot_Task(morSnapshot, _mor, null);
String result = _context.getServiceUtil().waitForTask(morTask);
if (result.equals("sucess")) {
_context.waitForTaskProgressDone(morTask);
return true;
} else {
s_logger.error("VMware revert to snapshot failed due to "
+ TaskMO.getTaskFailureInfo(_context, morTask));
}
return false;
}
public boolean removeAllSnapshots() throws Exception {
VirtualMachineSnapshotInfo snapshotInfo = getSnapshotInfo();