From 42e25a22fc7140c68cda589e95d98dda3ba6cdf3 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Thu, 9 May 2013 23:09:42 -0700 Subject: [PATCH] refactor kvm/vmware resource code --- .../cloud/agent/api/to}/DataObjectType.java | 2 +- .../src/com/cloud/agent/api/to}/DataTO.java | 4 +- api/src/com/cloud/agent/api/to/DiskTO.java | 60 + .../cloud/agent/api/to/VirtualMachineTO.java | 6 +- .../com/cloud/vm/VirtualMachineProfile.java | 8 +- core/src/com/cloud/serializer/GsonHelper.java | 2 +- .../resource/NfsSecondaryStorageResource.java | 4 +- .../storage/resource/StorageProcessor.java | 45 + .../StorageSubsystemCommandHandler.java | 27 + .../StorageSubsystemCommandHandlerBase.java | 135 ++ .../subsystem/api/storage/DataObject.java | 2 + .../api/storage/DataStoreDriver.java | 1 + .../storage/command/AttachAnswer.java | 44 + .../storage/command/AttachCommand.java | 52 + .../storage/command/CopyCmdAnswer.java | 2 +- .../storage/command/CopyCommand.java | 2 +- .../storage/command/CreateObjectAnswer.java | 2 +- .../storage/command/CreateObjectCommand.java | 2 +- .../storage/command/DeleteCommand.java | 2 +- .../storage/command/DettachAnswer.java | 44 + .../storage/command/DettachCommand.java | 52 + .../storage/to/PrimaryDataStoreTO.java | 41 + .../storage/to/SnapshotObjectTO.java | 4 +- .../storage/to/TemplateObjectTO.java | 11 +- .../cloudstack/storage/to/VolumeObjectTO.java | 30 +- .../motion/AncientDataMotionStrategy.java | 4 +- .../image/TemplateDataFactoryImpl.java | 11 + .../storage/image/store/TemplateObject.java | 4 +- .../storage/snapshot/SnapshotObject.java | 4 +- .../datastore/ObjectInDataStoreManager.java | 2 +- .../ObjectInDataStoreManagerImpl.java | 2 +- .../storage/db/ObjectInDataStoreVO.java | 2 +- .../datastore/PrimaryDataStoreImpl.java | 2 +- .../storage/volume/VolumeObject.java | 4 +- .../resource/LibvirtComputingResource.java | 190 +-- .../hypervisor/kvm/resource/LibvirtVMDef.java | 6 +- .../kvm/storage/KVMStorageProcessor.java | 879 +++++++++++++ .../kvm/storage/KVMStorageResource.java | 87 ++ .../vmware/resource/VmwareResource.java | 207 +-- ...VmwareSecondaryStorageResourceHandler.java | 10 + .../resource/VmwareStorageProcessor.java | 1144 +++++++++++++++++ .../xen/resource/CitrixResourceBase.java | 84 +- .../xen/resource/XenServer56FP1Resource.java | 21 +- ...ce.java => XenServerStorageProcessor.java} | 621 +++++---- .../CloudStackImageStoreDriverImpl.java | 4 +- .../driver/S3ImageStoreDriverImpl.java | 4 +- .../driver/SampleImageStoreDriverImpl.java | 4 +- .../driver/SwiftImageStoreDriverImpl.java | 4 +- .../CloudStackPrimaryDataStoreDriverImpl.java | 4 +- .../SamplePrimaryDataStoreDriverImpl.java | 2 +- .../SolidfirePrimaryDataStoreDriver.java | 2 +- .../cloud/hypervisor/HypervisorGuruBase.java | 3 +- .../com/cloud/storage/VolumeManagerImpl.java | 70 +- .../storage/download/DownloadListener.java | 2 +- .../cloud/template/TemplateManagerImpl.java | 37 +- .../src/com/cloud/vm/UserVmManagerImpl.java | 52 +- .../cloud/vm/VirtualMachineProfileImpl.java | 11 +- 57 files changed, 3397 insertions(+), 670 deletions(-) rename {engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage => api/src/com/cloud/agent/api/to}/DataObjectType.java (93%) rename {engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage => api/src/com/cloud/agent/api/to}/DataTO.java (90%) create mode 100644 api/src/com/cloud/agent/api/to/DiskTO.java create mode 100644 core/src/com/cloud/storage/resource/StorageProcessor.java create mode 100644 core/src/com/cloud/storage/resource/StorageSubsystemCommandHandler.java create mode 100644 core/src/com/cloud/storage/resource/StorageSubsystemCommandHandlerBase.java create mode 100644 engine/api/src/org/apache/cloudstack/storage/command/AttachAnswer.java create mode 100644 engine/api/src/org/apache/cloudstack/storage/command/AttachCommand.java create mode 100644 engine/api/src/org/apache/cloudstack/storage/command/DettachAnswer.java create mode 100644 engine/api/src/org/apache/cloudstack/storage/command/DettachCommand.java create mode 100644 plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java create mode 100644 plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageResource.java create mode 100644 plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java rename plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/{XenServerStorageResource.java => XenServerStorageProcessor.java} (77%) diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataObjectType.java b/api/src/com/cloud/agent/api/to/DataObjectType.java similarity index 93% rename from engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataObjectType.java rename to api/src/com/cloud/agent/api/to/DataObjectType.java index b4d1a57c88c..6f7a007e787 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataObjectType.java +++ b/api/src/com/cloud/agent/api/to/DataObjectType.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.cloudstack.engine.subsystem.api.storage; +package com.cloud.agent.api.to; public enum DataObjectType { VOLUME, diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataTO.java b/api/src/com/cloud/agent/api/to/DataTO.java similarity index 90% rename from engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataTO.java rename to api/src/com/cloud/agent/api/to/DataTO.java index 9e268943865..bd9a16bfc11 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataTO.java +++ b/api/src/com/cloud/agent/api/to/DataTO.java @@ -16,9 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.cloudstack.engine.subsystem.api.storage; - -import com.cloud.agent.api.to.DataStoreTO; +package com.cloud.agent.api.to; public interface DataTO { public DataObjectType getObjectType(); diff --git a/api/src/com/cloud/agent/api/to/DiskTO.java b/api/src/com/cloud/agent/api/to/DiskTO.java new file mode 100644 index 00000000000..7b32f006606 --- /dev/null +++ b/api/src/com/cloud/agent/api/to/DiskTO.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.agent.api.to; + +import com.cloud.storage.Volume; + +public class DiskTO { + private DataTO data; + private Long diskSeq; + private Volume.Type type; + public DiskTO() { + + } + + public DiskTO(DataTO data, Long diskSeq, Volume.Type type) { + this.data = data; + this.diskSeq = diskSeq; + this.type = type; + } + + public DataTO getData() { + return data; + } + + public void setData(DataTO data) { + this.data = data; + } + + public Long getDiskSeq() { + return diskSeq; + } + + public void setDiskSeq(Long diskSeq) { + this.diskSeq = diskSeq; + } + + public Volume.Type getType() { + return type; + } + + public void setType(Volume.Type type) { + this.type = type; + } +} diff --git a/api/src/com/cloud/agent/api/to/VirtualMachineTO.java b/api/src/com/cloud/agent/api/to/VirtualMachineTO.java index b84d20a9239..2644c04971b 100644 --- a/api/src/com/cloud/agent/api/to/VirtualMachineTO.java +++ b/api/src/com/cloud/agent/api/to/VirtualMachineTO.java @@ -57,7 +57,7 @@ public class VirtualMachineTO { Map params; String uuid; - VolumeTO[] disks; + DiskTO[] disks; NicTO[] nics; public VirtualMachineTO(long id, String instanceName, VirtualMachine.Type type, int cpus, Integer speed, long minRam, long maxRam, BootloaderType bootloader, String os, boolean enableHA, boolean limitCpuUse, String vncPassword) { @@ -206,11 +206,11 @@ public class VirtualMachineTO { this.bootupScripts = bootupScripts; } - public VolumeTO[] getDisks() { + public DiskTO[] getDisks() { return disks; } - public void setDisks(VolumeTO[] disks) { + public void setDisks(DiskTO[] disks) { this.disks = disks; } diff --git a/api/src/com/cloud/vm/VirtualMachineProfile.java b/api/src/com/cloud/vm/VirtualMachineProfile.java index 33a9171e732..6861372d08d 100644 --- a/api/src/com/cloud/vm/VirtualMachineProfile.java +++ b/api/src/com/cloud/vm/VirtualMachineProfile.java @@ -19,6 +19,8 @@ package com.cloud.vm; import java.util.List; import java.util.Map; +import com.cloud.agent.api.to.DataTO; +import com.cloud.agent.api.to.DiskTO; import com.cloud.agent.api.to.VolumeTO; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.offering.ServiceOffering; @@ -111,11 +113,11 @@ public interface VirtualMachineProfile { List getNics(); - List getDisks(); + List getDisks(); void addNic(int index, NicProfile nic); - void addDisk(int index, VolumeTO disk); + void addDisk(int index, DiskTO disk); StringBuilder getBootArgsBuilder(); @@ -125,7 +127,7 @@ public interface VirtualMachineProfile { void addNic(NicProfile nic); - void addDisk(VolumeTO disk); + void addDisk(DiskTO disk); VirtualMachine.Type getType(); diff --git a/core/src/com/cloud/serializer/GsonHelper.java b/core/src/com/cloud/serializer/GsonHelper.java index 5656d910959..54b6b171712 100644 --- a/core/src/com/cloud/serializer/GsonHelper.java +++ b/core/src/com/cloud/serializer/GsonHelper.java @@ -20,13 +20,13 @@ package com.cloud.serializer; import java.util.List; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; import com.cloud.agent.api.SecStorageFirewallCfgCommand.PortConfig; import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; import com.cloud.agent.transport.ArrayTypeAdaptor; import com.cloud.agent.transport.InterfaceTypeAdaptor; import com.cloud.agent.transport.LoggingExclusionStrategy; diff --git a/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java b/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java index 03e822ebbe7..36b6bbc7ca6 100755 --- a/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java +++ b/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java @@ -46,8 +46,6 @@ import java.util.concurrent.Callable; import javax.naming.ConfigurationException; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import org.apache.cloudstack.storage.command.CopyCmdAnswer; import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.command.DownloadCommand; @@ -97,7 +95,9 @@ import com.cloud.agent.api.storage.ListVolumeAnswer; import com.cloud.agent.api.storage.ListVolumeCommand; import com.cloud.agent.api.storage.UploadCommand; import com.cloud.agent.api.storage.ssCommand; +import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.NfsTO; import com.cloud.agent.api.to.S3TO; import com.cloud.agent.api.to.SwiftTO; diff --git a/core/src/com/cloud/storage/resource/StorageProcessor.java b/core/src/com/cloud/storage/resource/StorageProcessor.java new file mode 100644 index 00000000000..ec965ed424b --- /dev/null +++ b/core/src/com/cloud/storage/resource/StorageProcessor.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.storage.resource; + +import org.apache.cloudstack.storage.command.AttachCommand; +import org.apache.cloudstack.storage.command.CopyCommand; +import org.apache.cloudstack.storage.command.CreateObjectCommand; +import org.apache.cloudstack.storage.command.DeleteCommand; +import org.apache.cloudstack.storage.command.DettachCommand; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.to.DataTO; +import com.cloud.agent.api.to.DiskTO; + +public interface StorageProcessor { + public Answer copyTemplateToPrimaryStorage(CopyCommand cmd); + public Answer cloneVolumeFromBaseTemplate(CopyCommand cmd); + public Answer copyVolumeFromImageCacheToPrimary(CopyCommand cmd); + public Answer copyVolumeFromPrimaryToSecondary(CopyCommand cmd); + public Answer createTemplateFromVolume(CopyCommand cmd); + public Answer backupSnasphot(CopyCommand cmd); + public Answer attachIso(AttachCommand cmd); + public Answer attachVolume(AttachCommand cmd); + public Answer dettachIso(DettachCommand cmd); + public Answer dettachVolume(DettachCommand cmd); + public Answer createVolume(CreateObjectCommand cmd); + public Answer createSnapshot(CreateObjectCommand cmd); + public Answer deleteVolume(DeleteCommand cmd); +} diff --git a/core/src/com/cloud/storage/resource/StorageSubsystemCommandHandler.java b/core/src/com/cloud/storage/resource/StorageSubsystemCommandHandler.java new file mode 100644 index 00000000000..5ac601e38a9 --- /dev/null +++ b/core/src/com/cloud/storage/resource/StorageSubsystemCommandHandler.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.storage.resource; + +import org.apache.cloudstack.storage.command.StorageSubSystemCommand; + +import com.cloud.agent.api.Answer; + +public interface StorageSubsystemCommandHandler { + public Answer handleStorageCommands(StorageSubSystemCommand command); +} diff --git a/core/src/com/cloud/storage/resource/StorageSubsystemCommandHandlerBase.java b/core/src/com/cloud/storage/resource/StorageSubsystemCommandHandlerBase.java new file mode 100644 index 00000000000..8f76e93d180 --- /dev/null +++ b/core/src/com/cloud/storage/resource/StorageSubsystemCommandHandlerBase.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.storage.resource; + +import org.apache.cloudstack.storage.command.AttachCommand; +import org.apache.cloudstack.storage.command.CopyCommand; +import org.apache.cloudstack.storage.command.CreateObjectAnswer; +import org.apache.cloudstack.storage.command.CreateObjectCommand; +import org.apache.cloudstack.storage.command.DeleteCommand; +import org.apache.cloudstack.storage.command.DettachCommand; +import org.apache.cloudstack.storage.command.StorageSubSystemCommand; +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.to.DataObjectType; +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; +import com.cloud.agent.api.to.DiskTO; +import com.cloud.agent.api.to.NfsTO; +import com.cloud.storage.DataStoreRole; +import com.cloud.storage.Volume; + +public class StorageSubsystemCommandHandlerBase implements StorageSubsystemCommandHandler { + private static final Logger s_logger = Logger.getLogger(StorageSubsystemCommandHandlerBase.class); + private StorageProcessor processor; + public StorageSubsystemCommandHandlerBase(StorageProcessor processor) { + this.processor = processor; + } + @Override + public Answer handleStorageCommands(StorageSubSystemCommand command) { + if (command instanceof CopyCommand) { + return this.execute((CopyCommand)command); + } else if (command instanceof CreateObjectCommand) { + return execute((CreateObjectCommand) command); + } else if (command instanceof DeleteCommand) { + return execute((DeleteCommand)command); + } else if (command instanceof AttachCommand) { + return execute((AttachCommand)command); + } else if (command instanceof DettachCommand) { + return execute((DettachCommand)command); + } + return new Answer((Command)command, false, "not implemented yet"); + } + + protected Answer execute(CopyCommand cmd) { + DataTO srcData = cmd.getSrcTO(); + DataTO destData = cmd.getDestTO(); + DataStoreTO srcDataStore = srcData.getDataStore(); + DataStoreTO destDataStore = destData.getDataStore(); + + if ((srcData.getObjectType() == DataObjectType.TEMPLATE) && (srcDataStore instanceof NfsTO) && (destData.getDataStore().getRole() == DataStoreRole.Primary)) { + //copy template to primary storage + return processor.copyTemplateToPrimaryStorage(cmd); + } else if (srcData.getObjectType() == DataObjectType.TEMPLATE && srcDataStore.getRole() == DataStoreRole.Primary && destDataStore.getRole() == DataStoreRole.Primary) { + //clone template to a volume + return processor.cloneVolumeFromBaseTemplate(cmd); + } else if (srcData.getObjectType() == DataObjectType.VOLUME && (srcData.getDataStore().getRole() == DataStoreRole.ImageCache || srcDataStore.getRole() == DataStoreRole.Image)) { + //copy volume from image cache to primary + return processor.copyVolumeFromImageCacheToPrimary(cmd); + } else if (srcData.getObjectType() == DataObjectType.VOLUME && srcData.getDataStore().getRole() == DataStoreRole.Primary) { + if (destData.getObjectType() == DataObjectType.VOLUME) { + return processor.copyVolumeFromPrimaryToSecondary(cmd); + } else if (destData.getObjectType() == DataObjectType.TEMPLATE) { + return processor.createTemplateFromVolume(cmd); + } + } else if (srcData.getObjectType() == DataObjectType.SNAPSHOT && srcData.getDataStore().getRole() == DataStoreRole.Primary) { + return processor.backupSnasphot(cmd); + } + + return new Answer(cmd, false, "not implemented yet"); + } + + + protected Answer execute(CreateObjectCommand cmd) { + DataTO data = cmd.getData(); + try { + if (data.getObjectType() == DataObjectType.VOLUME) { + return processor.createVolume(cmd); + } else if (data.getObjectType() == DataObjectType.SNAPSHOT) { + return processor.createSnapshot(cmd); + } + return new CreateObjectAnswer("not supported type"); + } catch (Exception e) { + s_logger.debug("Failed to create object: " + data.getObjectType() + ": " + e.toString()); + return new CreateObjectAnswer(e.toString()); + } + } + + protected Answer execute(DeleteCommand cmd) { + DataTO data = cmd.getData(); + Answer answer = null; + if (data.getObjectType() == DataObjectType.VOLUME) { + answer = processor.deleteVolume(cmd); + } else { + answer = new Answer(cmd, false, "unsupported type"); + } + + return answer; + } + + protected Answer execute(AttachCommand cmd) { + DiskTO disk = cmd.getDisk(); + if (disk.getType() == Volume.Type.ISO) { + return processor.attachIso(cmd); + } else { + return processor.attachVolume(cmd); + } + } + + protected Answer execute(DettachCommand cmd) { + DiskTO disk = cmd.getDisk(); + if (disk.getType() == Volume.Type.ISO) { + return processor.dettachIso(cmd); + } else { + return processor.dettachVolume(cmd); + } + } +} diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataObject.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataObject.java index 8ffed6a1fcd..7610bca59bb 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataObject.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataObject.java @@ -19,6 +19,8 @@ package org.apache.cloudstack.engine.subsystem.api.storage; import com.cloud.agent.api.Answer; +import com.cloud.agent.api.to.DataObjectType; +import com.cloud.agent.api.to.DataTO; public interface DataObject { public long getId(); diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java index cd2dc86cddc..5ed9ab838ad 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java @@ -24,6 +24,7 @@ import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.storage.command.CommandResult; import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; public interface DataStoreDriver { public String grantAccess(DataObject data, EndPoint ep); diff --git a/engine/api/src/org/apache/cloudstack/storage/command/AttachAnswer.java b/engine/api/src/org/apache/cloudstack/storage/command/AttachAnswer.java new file mode 100644 index 00000000000..1ddcd7dce8d --- /dev/null +++ b/engine/api/src/org/apache/cloudstack/storage/command/AttachAnswer.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cloudstack.storage.command; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.to.DiskTO; + +public class AttachAnswer extends Answer { + private DiskTO disk; + public AttachAnswer() { + super(null); + } + + public AttachAnswer(DiskTO disk) { + this.setDisk(disk); + } + public AttachAnswer(String errMsg) { + super(null, false, errMsg); + } + + public DiskTO getDisk() { + return disk; + } + + public void setDisk(DiskTO disk) { + this.disk = disk; + } +} diff --git a/engine/api/src/org/apache/cloudstack/storage/command/AttachCommand.java b/engine/api/src/org/apache/cloudstack/storage/command/AttachCommand.java new file mode 100644 index 00000000000..fa205a55eb6 --- /dev/null +++ b/engine/api/src/org/apache/cloudstack/storage/command/AttachCommand.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cloudstack.storage.command; + + +import com.cloud.agent.api.Command; +import com.cloud.agent.api.to.DiskTO; + +public class AttachCommand extends Command implements StorageSubSystemCommand { + private DiskTO disk; + private String vmName; + + public AttachCommand(DiskTO disk, String vmName) { + this.disk = disk; + this.vmName = vmName; + } + + @Override + public boolean executeInSequence() { + // TODO Auto-generated method stub + return false; + } + public DiskTO getDisk() { + return disk; + } + public void setDisk(DiskTO disk) { + this.disk = disk; + } + public String getVmName() { + return vmName; + } + public void setVmName(String vmName) { + this.vmName = vmName; + } + +} diff --git a/engine/api/src/org/apache/cloudstack/storage/command/CopyCmdAnswer.java b/engine/api/src/org/apache/cloudstack/storage/command/CopyCmdAnswer.java index 3612cf98e5c..132832af636 100644 --- a/engine/api/src/org/apache/cloudstack/storage/command/CopyCmdAnswer.java +++ b/engine/api/src/org/apache/cloudstack/storage/command/CopyCmdAnswer.java @@ -17,9 +17,9 @@ package org.apache.cloudstack.storage.command; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import com.cloud.agent.api.Answer; +import com.cloud.agent.api.to.DataTO; public class CopyCmdAnswer extends Answer { private DataTO newData; diff --git a/engine/api/src/org/apache/cloudstack/storage/command/CopyCommand.java b/engine/api/src/org/apache/cloudstack/storage/command/CopyCommand.java index 5bceb03ff93..d512e60d779 100644 --- a/engine/api/src/org/apache/cloudstack/storage/command/CopyCommand.java +++ b/engine/api/src/org/apache/cloudstack/storage/command/CopyCommand.java @@ -16,9 +16,9 @@ // under the License. package org.apache.cloudstack.storage.command; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import com.cloud.agent.api.Command; +import com.cloud.agent.api.to.DataTO; public class CopyCommand extends Command implements StorageSubSystemCommand { private DataTO srcTO; diff --git a/engine/api/src/org/apache/cloudstack/storage/command/CreateObjectAnswer.java b/engine/api/src/org/apache/cloudstack/storage/command/CreateObjectAnswer.java index ddd678a0927..2c174e4687f 100644 --- a/engine/api/src/org/apache/cloudstack/storage/command/CreateObjectAnswer.java +++ b/engine/api/src/org/apache/cloudstack/storage/command/CreateObjectAnswer.java @@ -18,9 +18,9 @@ */ package org.apache.cloudstack.storage.command; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import com.cloud.agent.api.Answer; +import com.cloud.agent.api.to.DataTO; public class CreateObjectAnswer extends Answer { private DataTO data; diff --git a/engine/api/src/org/apache/cloudstack/storage/command/CreateObjectCommand.java b/engine/api/src/org/apache/cloudstack/storage/command/CreateObjectCommand.java index 6a722773592..dfadc2dcea1 100644 --- a/engine/api/src/org/apache/cloudstack/storage/command/CreateObjectCommand.java +++ b/engine/api/src/org/apache/cloudstack/storage/command/CreateObjectCommand.java @@ -18,9 +18,9 @@ */ package org.apache.cloudstack.storage.command; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import com.cloud.agent.api.Command; +import com.cloud.agent.api.to.DataTO; public class CreateObjectCommand extends Command implements StorageSubSystemCommand { private DataTO data; diff --git a/engine/api/src/org/apache/cloudstack/storage/command/DeleteCommand.java b/engine/api/src/org/apache/cloudstack/storage/command/DeleteCommand.java index 3335848bd86..41af42c58ab 100644 --- a/engine/api/src/org/apache/cloudstack/storage/command/DeleteCommand.java +++ b/engine/api/src/org/apache/cloudstack/storage/command/DeleteCommand.java @@ -18,9 +18,9 @@ */ package org.apache.cloudstack.storage.command; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import com.cloud.agent.api.Command; +import com.cloud.agent.api.to.DataTO; public class DeleteCommand extends Command implements StorageSubSystemCommand { private DataTO data; diff --git a/engine/api/src/org/apache/cloudstack/storage/command/DettachAnswer.java b/engine/api/src/org/apache/cloudstack/storage/command/DettachAnswer.java new file mode 100644 index 00000000000..9019e0cfa59 --- /dev/null +++ b/engine/api/src/org/apache/cloudstack/storage/command/DettachAnswer.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cloudstack.storage.command; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.to.DiskTO; + +public class DettachAnswer extends Answer { + private DiskTO disk; + public DettachAnswer() { + super(null); + } + + public DettachAnswer(DiskTO disk) { + this.setDisk(disk); + } + public DettachAnswer(String errMsg) { + super(null, false, errMsg); + } + + public DiskTO getDisk() { + return disk; + } + + public void setDisk(DiskTO disk) { + this.disk = disk; + } +} diff --git a/engine/api/src/org/apache/cloudstack/storage/command/DettachCommand.java b/engine/api/src/org/apache/cloudstack/storage/command/DettachCommand.java new file mode 100644 index 00000000000..c435e102120 --- /dev/null +++ b/engine/api/src/org/apache/cloudstack/storage/command/DettachCommand.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cloudstack.storage.command; + +import com.cloud.agent.api.Command; +import com.cloud.agent.api.to.DiskTO; + +public class DettachCommand extends Command implements StorageSubSystemCommand { + private DiskTO disk; + private String vmName; + + public DettachCommand(DiskTO disk, String vmName) { + this.disk = disk; + this.vmName = vmName; + } + + @Override + public boolean executeInSequence() { + // TODO Auto-generated method stub + return false; + } + public DiskTO getDisk() { + return disk; + } + public void setDisk(DiskTO disk) { + this.disk = disk; + } + public String getVmName() { + return vmName; + } + public void setVmName(String vmName) { + this.vmName = vmName; + } + + +} diff --git a/engine/api/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java b/engine/api/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java index cf9448bd73a..c3ce0e086d4 100644 --- a/engine/api/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java +++ b/engine/api/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java @@ -20,17 +20,26 @@ import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreInfo; import com.cloud.agent.api.to.DataStoreTO; import com.cloud.storage.DataStoreRole; +import com.cloud.storage.Storage.StoragePoolType; public class PrimaryDataStoreTO implements DataStoreTO { private final String uuid; private final String name; private String type; private final long id; + private StoragePoolType poolType; + private String host; + private String path; + private int port; public PrimaryDataStoreTO(PrimaryDataStoreInfo dataStore) { this.uuid = dataStore.getUuid(); this.name = dataStore.getName(); // this.type = dataStore.getType(); this.id = dataStore.getId(); + this.setPoolType(dataStore.getPoolType()); + this.setHost(dataStore.getHostAddress()); + this.setPath(dataStore.getPath()); + this.setPort(dataStore.getPort()); } public long getId() { @@ -53,4 +62,36 @@ public class PrimaryDataStoreTO implements DataStoreTO { public DataStoreRole getRole() { return DataStoreRole.Primary; } + + public StoragePoolType getPoolType() { + return poolType; + } + + public void setPoolType(StoragePoolType poolType) { + this.poolType = poolType; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } } diff --git a/engine/api/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java b/engine/api/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java index ed4cbe1756a..a2e336b53e9 100644 --- a/engine/api/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java +++ b/engine/api/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java @@ -1,10 +1,10 @@ package org.apache.cloudstack.storage.to; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; +import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; import com.cloud.hypervisor.Hypervisor.HypervisorType; public class SnapshotObjectTO implements DataTO { diff --git a/engine/api/src/org/apache/cloudstack/storage/to/TemplateObjectTO.java b/engine/api/src/org/apache/cloudstack/storage/to/TemplateObjectTO.java index aad5d71536a..14577ef2c00 100644 --- a/engine/api/src/org/apache/cloudstack/storage/to/TemplateObjectTO.java +++ b/engine/api/src/org/apache/cloudstack/storage/to/TemplateObjectTO.java @@ -16,13 +16,13 @@ // under the License. package org.apache.cloudstack.storage.to; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; import org.apache.cloudstack.engine.subsystem.api.storage.disktype.DiskFormat; import org.apache.cloudstack.storage.image.datastore.ImageStoreInfo; +import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; import com.cloud.storage.Storage.ImageFormat; import com.cloud.template.VirtualMachineTemplate; @@ -38,6 +38,7 @@ public class TemplateObjectTO implements DataTO { private String displayText; private DataStoreTO imageDataStore; private String name; + private String guestOsType; public TemplateObjectTO() { @@ -151,5 +152,11 @@ public class TemplateObjectTO implements DataTO { this.imageDataStore = imageDataStore; } + public String getGuestOsType() { + return guestOsType; + } + public void setGuestOsType(String guestOsType) { + this.guestOsType = guestOsType; + } } diff --git a/engine/api/src/org/apache/cloudstack/storage/to/VolumeObjectTO.java b/engine/api/src/org/apache/cloudstack/storage/to/VolumeObjectTO.java index 3f7a8f1d335..0ae2bc78132 100644 --- a/engine/api/src/org/apache/cloudstack/storage/to/VolumeObjectTO.java +++ b/engine/api/src/org/apache/cloudstack/storage/to/VolumeObjectTO.java @@ -16,17 +16,18 @@ // under the License. package org.apache.cloudstack.storage.to; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.engine.subsystem.api.storage.disktype.DiskFormat; import org.apache.cloudstack.engine.subsystem.api.storage.type.VolumeType; +import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; +import com.cloud.storage.Volume; public class VolumeObjectTO implements DataTO { private String uuid; - private VolumeType volumeType; + private Volume.Type volumeType; private DiskFormat diskType; private DataStoreTO dataStore; private String name; @@ -35,6 +36,8 @@ public class VolumeObjectTO implements DataTO { private Long volumeId; private String vmName; private long accountId; + private String chainInfo; + private long id; public VolumeObjectTO() { @@ -52,6 +55,9 @@ public class VolumeObjectTO implements DataTO { this.vmName = volume.getAttachedVmName(); this.size = volume.getSize(); this.setVolumeId(volume.getId()); + this.chainInfo = volume.getChainInfo(); + this.volumeType = volume.getVolumeType(); + this.setId(volume.getId()); } public String getUuid() { @@ -62,7 +68,7 @@ public class VolumeObjectTO implements DataTO { return this.path; } - public VolumeType getVolumeType() { + public Volume.Type getVolumeType() { return this.volumeType; } @@ -130,5 +136,21 @@ public class VolumeObjectTO implements DataTO { this.vmName = vmName; } + public String getChainInfo() { + return chainInfo; + } + + public void setChainInfo(String chainInfo) { + this.chainInfo = chainInfo; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + } diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java index 560c34f0a3f..4e9b02bd7f9 100644 --- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java +++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java @@ -26,10 +26,8 @@ import javax.inject.Inject; import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; @@ -61,7 +59,9 @@ import com.cloud.agent.api.UpgradeSnapshotCommand; 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.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.NfsTO; import com.cloud.agent.api.to.S3TO; import com.cloud.agent.api.to.SwiftTO; diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateDataFactoryImpl.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateDataFactoryImpl.java index 6f204266679..09c79b22f98 100644 --- a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateDataFactoryImpl.java +++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateDataFactoryImpl.java @@ -33,6 +33,7 @@ import org.springframework.stereotype.Component; import com.cloud.storage.DataStoreRole; import com.cloud.storage.VMTemplateStoragePoolVO; +import com.cloud.storage.VMTemplateStorageResourceAssoc; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplatePoolDao; @@ -89,6 +90,16 @@ public class TemplateDataFactoryImpl implements TemplateDataFactory { } return this.getTemplate(templateId, store); } + + @Override + public TemplateInfo getTemplate(long templateId, long zoneId) { + TemplateDataStoreVO tmplStore = templateStoreDao.findByTemplateZoneDownloadStatus(templateId, zoneId, VMTemplateStorageResourceAssoc.Status.DOWNLOADED); + if (tmplStore != null) { + DataStore store = this.storeMgr.getDataStore(tmplStore.getDataStoreId(), DataStoreRole.Image); + return this.getTemplate(templateId, store); + } + return null; + } @Override diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java index daf85681848..12e29e78163 100644 --- a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java +++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java @@ -24,9 +24,7 @@ import java.util.Map; import javax.inject.Inject; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateEvent; @@ -40,6 +38,8 @@ import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; +import com.cloud.agent.api.to.DataObjectType; +import com.cloud.agent.api.to.DataTO; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.DataStoreRole; import com.cloud.storage.Storage.ImageFormat; diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java index f5800c19bcd..423d8c9f48d 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java @@ -22,9 +22,7 @@ import java.util.Date; import javax.inject.Inject; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; @@ -39,6 +37,8 @@ import org.apache.cloudstack.storage.to.SnapshotObjectTO; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; +import com.cloud.agent.api.to.DataObjectType; +import com.cloud.agent.api.to.DataTO; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.Snapshot; import com.cloud.storage.SnapshotVO; diff --git a/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManager.java b/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManager.java index ca9f22c7cb4..f49929f2421 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManager.java +++ b/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManager.java @@ -18,10 +18,10 @@ package org.apache.cloudstack.storage.datastore; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; +import com.cloud.agent.api.to.DataObjectType; import com.cloud.storage.DataStoreRole; import com.cloud.utils.fsm.NoTransitionException; diff --git a/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java b/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java index f0b54e1cb3c..4816e51192a 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java @@ -20,7 +20,6 @@ import javax.inject.Inject; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; @@ -42,6 +41,7 @@ import org.apache.cloudstack.storage.db.ObjectInDataStoreVO; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; +import com.cloud.agent.api.to.DataObjectType; import com.cloud.storage.DataStoreRole; import com.cloud.storage.VMTemplateStoragePoolVO; import com.cloud.storage.dao.SnapshotDao; diff --git a/engine/storage/src/org/apache/cloudstack/storage/db/ObjectInDataStoreVO.java b/engine/storage/src/org/apache/cloudstack/storage/db/ObjectInDataStoreVO.java index 5b69251f161..ecf03913c3e 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/db/ObjectInDataStoreVO.java +++ b/engine/storage/src/org/apache/cloudstack/storage/db/ObjectInDataStoreVO.java @@ -30,10 +30,10 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State; +import com.cloud.agent.api.to.DataObjectType; import com.cloud.storage.DataStoreRole; import com.cloud.storage.Storage; import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java index 49bfa653114..55c5b911051 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java @@ -25,7 +25,6 @@ import javax.inject.Inject; import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider; import org.apache.cloudstack.engine.subsystem.api.storage.HostScope; @@ -46,6 +45,7 @@ import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; import org.apache.cloudstack.storage.volume.VolumeObject; import org.apache.log4j.Logger; +import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.Storage.StoragePoolType; diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java index be7bd12104d..6754a1dd540 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java @@ -21,9 +21,7 @@ import java.util.Date; import javax.inject.Inject; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.storage.command.CopyCmdAnswer; @@ -36,6 +34,8 @@ import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; import com.cloud.agent.api.storage.DownloadAnswer; +import com.cloud.agent.api.to.DataObjectType; +import com.cloud.agent.api.to.DataTO; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.DataStoreRole; import com.cloud.storage.Volume; diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 706c9ef5670..dd92a3138cf 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -39,11 +39,11 @@ import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.HashSet; import java.util.Properties; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; @@ -56,11 +56,13 @@ import java.util.regex.Pattern; import javax.ejb.Local; import javax.naming.ConfigurationException; -import org.apache.log4j.Logger; -import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import org.apache.cloudstack.storage.command.StorageSubSystemCommand; +import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; import org.apache.cloudstack.utils.qemu.QemuImg; -import org.apache.cloudstack.utils.qemu.QemuImgFile; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; import org.apache.cloudstack.utils.qemu.QemuImgException; +import org.apache.cloudstack.utils.qemu.QemuImgFile; +import org.apache.log4j.Logger; import org.libvirt.Connect; import org.libvirt.Domain; import org.libvirt.DomainInfo; @@ -166,9 +168,13 @@ import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; import com.cloud.agent.api.storage.DestroyCommand; import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; -import com.cloud.agent.api.storage.ResizeVolumeCommand; import com.cloud.agent.api.storage.ResizeVolumeAnswer; +import com.cloud.agent.api.storage.ResizeVolumeCommand; +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; +import com.cloud.agent.api.to.DiskTO; import com.cloud.agent.api.to.IpAddressTO; +import com.cloud.agent.api.to.NfsTO; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.agent.api.to.VirtualMachineTO; @@ -194,11 +200,12 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InputDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef.hostNicType; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SerialDef; -import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.VirtioSerialDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.TermPolicy; +import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.VirtioSerialDef; import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk; import com.cloud.hypervisor.kvm.storage.KVMStoragePool; import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager; +import com.cloud.hypervisor.kvm.storage.KVMStorageProcessor; import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.IsolationType; import com.cloud.network.Networks.RouterPrivateIpStrategy; @@ -212,14 +219,16 @@ import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageLayer; import com.cloud.storage.Volume; +import com.cloud.storage.resource.StorageSubsystemCommandHandler; +import com.cloud.storage.resource.StorageSubsystemCommandHandlerBase; import com.cloud.storage.template.Processor; import com.cloud.storage.template.Processor.FormatInfo; import com.cloud.storage.template.QCOW2Processor; -import com.cloud.storage.template.TemplateProp; import com.cloud.storage.template.TemplateLocation; +import com.cloud.storage.template.TemplateProp; +import com.cloud.utils.FileUtil; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; -import com.cloud.utils.FileUtil; import com.cloud.utils.PropertiesUtil; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; @@ -345,7 +354,6 @@ ServerResource { protected String _localStoragePath; protected String _localStorageUUID; private final Map _pifs = new HashMap(); - private final Map> hostNetInfo = new HashMap>(); private final Map _vmStats = new ConcurrentHashMap(); protected boolean _disconnected = true; @@ -383,6 +391,8 @@ ServerResource { } protected BridgeType _bridgeType; + + protected StorageSubsystemCommandHandler storageHandler; private String getEndIpFromStartIp(String startIp, int numIps) { String[] tokens = startIp.split("[.]"); @@ -810,6 +820,10 @@ ServerResource { params.put("libvirt.computing.resource", this); configureVifDrivers(params); + + KVMStorageProcessor storageProcessor = new KVMStorageProcessor(this._storagePoolMgr, this); + storageProcessor.configure(name, params); + storageHandler = new StorageSubsystemCommandHandlerBase(storageProcessor); return true; } @@ -958,11 +972,6 @@ ServerResource { return pif; } - - private String getOvsPif(String bridge) { - String pif = Script.runSimpleBashScript("ovs-vsctl list-ports " + bridge); - return pif; - } private String matchPifFileInDirectory(String bridgeName){ File f = new File("/sys/devices/virtual/net/" + bridgeName + "/brif"); @@ -1203,6 +1212,8 @@ ServerResource { return execute((CheckNetworkCommand) cmd); } else if (cmd instanceof NetworkRulesVmSecondaryIpCommand) { return execute((NetworkRulesVmSecondaryIpCommand) cmd); + } else if (cmd instanceof StorageSubSystemCommand) { + return this.storageHandler.handleStorageCommands((StorageSubSystemCommand)cmd); } else { s_logger.warn("Unsupported command "); return Answer.createUnsupportedCommandAnswer(cmd); @@ -1496,8 +1507,6 @@ ServerResource { vol.getPoolType(), vol.getPoolUuid()); pool.deletePhysicalDisk(vol.getPath()); - String vmName = cmd.getVmName(); - String poolPath = pool.getLocalPath(); return new Answer(cmd, true, "Success"); } catch (CloudRuntimeException e) { s_logger.debug("Failed to delete volume: " + e.toString()); @@ -1606,7 +1615,6 @@ ServerResource { try { conn = LibvirtConnection.getConnectionByVmName(routerName); - Domain vm = getDomain(conn, routerName); List pluggedNics = getInterfaces(conn, routerName); InterfaceDef routerNic = null; @@ -1621,7 +1629,6 @@ ServerResource { return new SetupGuestNetworkAnswer(cmd, false, "Can not find nic with mac " + nic.getMac() + " for VM " + routerName); } - String args = "vpc_guestnw.sh " + routerIP + " -C"; String dev = "eth" + nic.getDeviceId(); String netmask = NetUtils.getSubNet(routerGIP, nic.getNetmask()); String result = _virtRouterResource.assignGuestNetwork(dev, routerIP, @@ -1640,14 +1647,10 @@ ServerResource { private SetNetworkACLAnswer execute(SetNetworkACLCommand cmd) { String[] results = new String[cmd.getRules().length]; - String callResult; - Connect conn; - String routerName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME); + String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); try { - conn = LibvirtConnection.getConnectionByVmName(routerName); - Domain vm = getDomain(conn, routerName); String [][] rules = cmd.generateFwRules(); String[] aclRules = rules[0]; NicTO nic = cmd.getNic(); @@ -1671,7 +1674,7 @@ ServerResource { } return new SetNetworkACLAnswer(cmd, true, results); - } catch (LibvirtException e) { + } catch (Exception e) { String msg = "SetNetworkACL failed due to " + e.toString(); s_logger.error(msg, e); return new SetNetworkACLAnswer(cmd, false, results); @@ -1686,7 +1689,6 @@ ServerResource { try { conn = LibvirtConnection.getConnectionByVmName(routerName); - Domain vm = getDomain(conn, routerName); Integer devNum = 0; String pubVlan = pubIP.getVlanId(); List pluggedNics = getInterfaces(conn, routerName); @@ -1733,7 +1735,6 @@ ServerResource { try { conn = LibvirtConnection.getConnectionByVmName(routerName); IpAddressTO[] ips = cmd.getIpAddresses(); - Domain vm = getDomain(conn, routerName); Integer devNum = 0; Map vlanToNicNum = new HashMap(); List pluggedNics = getInterfaces(conn, routerName); @@ -1919,7 +1920,6 @@ ServerResource { Long volumeId = cmd.getVolumeId(); String secondaryStoragePoolUrl = cmd.getSecondaryStorageUrl(); String snapshotName = cmd.getSnapshotName(); - String snapshotPath = cmd.getVolumePath(); String snapshotDestPath = null; String snapshotRelPath = null; String vmName = cmd.getVmName(); @@ -2194,7 +2194,6 @@ ServerResource { KVMStoragePool secondaryStorage = null; KVMStoragePool primary = null; try { - Connect conn = LibvirtConnection.getConnectionByVmName(cmd.getVmName()); String templateFolder = cmd.getAccountId() + File.separator + cmd.getTemplateId() + File.separator; String templateInstallFolder = "/template/tmpl/" + templateFolder; @@ -2290,10 +2289,6 @@ ServerResource { templateInstallFolder + cmd.getUniqueName() + ".qcow2", info.virtualSize, info.size, cmd.getUniqueName(), ImageFormat.QCOW2); - } catch (LibvirtException e) { - s_logger.debug("Failed to get secondary storage pool: " - + e.toString()); - return new CreatePrivateTemplateAnswer(cmd, false, e.toString()); } catch (InternalErrorException e) { return new CreatePrivateTemplateAnswer(cmd, false, e.toString()); } catch (IOException e) { @@ -2372,7 +2367,6 @@ ServerResource { } protected Answer execute(ModifyStoragePoolCommand cmd) { - String poolType = cmd.getPool().getType().toString(); KVMStoragePool storagepool = _storagePoolMgr.createStoragePool(cmd .getPool().getUuid(), cmd.getPool().getHost(), cmd.getPool().getPort(), cmd.getPool().getPath(), @@ -2700,8 +2694,8 @@ ServerResource { } /* setup disks, e.g for iso */ - VolumeTO[] volumes = vm.getDisks(); - for (VolumeTO volume : volumes) { + DiskTO[] volumes = vm.getDisks(); + for (DiskTO volume : volumes) { if (volume.getType() == Volume.Type.ISO) { getVolumePath(conn, volume); } @@ -3279,10 +3273,14 @@ ServerResource { } } - private String getVolumePath(Connect conn, VolumeTO volume) + private String getVolumePath(Connect conn, DiskTO volume) throws LibvirtException, URISyntaxException { - if (volume.getType() == Volume.Type.ISO && volume.getPath() != null) { - String isoPath = volume.getPath(); + DataTO data = volume.getData(); + DataStoreTO store = data.getDataStore(); + + if (volume.getType() == Volume.Type.ISO && data.getPath() != null) { + NfsTO nfsStore = (NfsTO)store; + String isoPath = nfsStore.getUrl() + File.separator + data.getPath(); int index = isoPath.lastIndexOf("/"); String path = isoPath.substring(0, index); String name = isoPath.substring(index + 1); @@ -3291,26 +3289,28 @@ ServerResource { KVMPhysicalDisk isoVol = secondaryPool.getPhysicalDisk(name); return isoVol.getPath(); } else { - return volume.getPath(); + return data.getPath(); } } protected void createVbd(Connect conn, VirtualMachineTO vmSpec, String vmName, LibvirtVMDef vm) throws InternalErrorException, LibvirtException, URISyntaxException { - List disks = Arrays.asList(vmSpec.getDisks()); - Collections.sort(disks, new Comparator() { + List disks = Arrays.asList(vmSpec.getDisks()); + Collections.sort(disks, new Comparator() { @Override - public int compare(VolumeTO arg0, VolumeTO arg1) { - return arg0.getDeviceId() > arg1.getDeviceId() ? 1 : -1; + public int compare(DiskTO arg0, DiskTO arg1) { + return arg0.getDiskSeq() > arg1.getDiskSeq() ? 1 : -1; } }); - for (VolumeTO volume : disks) { + for (DiskTO volume : disks) { KVMPhysicalDisk physicalDisk = null; KVMStoragePool pool = null; - if (volume.getType() == Volume.Type.ISO && volume.getPath() != null) { - String volPath = volume.getPath(); + DataTO data = volume.getData(); + if (volume.getType() == Volume.Type.ISO && data.getPath() != null) { + NfsTO nfsStore = (NfsTO)data.getDataStore(); + String volPath = nfsStore.getUrl() + File.separator + data.getPath(); int index = volPath.lastIndexOf("/"); String volDir = volPath.substring(0, index); String volName = volPath.substring(index + 1); @@ -3318,10 +3318,11 @@ ServerResource { getStoragePoolByURI(volDir); physicalDisk = secondaryStorage.getPhysicalDisk(volName); } else if (volume.getType() != Volume.Type.ISO) { + PrimaryDataStoreTO store = (PrimaryDataStoreTO)data.getDataStore(); pool = _storagePoolMgr.getStoragePool( - volume.getPoolType(), - volume.getPoolUuid()); - physicalDisk = pool.getPhysicalDisk(volume.getPath()); + store.getPoolType(), + store.getUuid()); + physicalDisk = pool.getPhysicalDisk(data.getPath()); } String volPath = null; @@ -3339,7 +3340,7 @@ ServerResource { disk.defISODisk(volPath); } } else { - int devId = (int) volume.getDeviceId(); + int devId = volume.getDiskSeq().intValue(); if (pool.getType() == StoragePoolType.RBD) { /* @@ -3379,12 +3380,14 @@ ServerResource { // For LXC, find and add the root filesystem if (HypervisorType.LXC.toString().toLowerCase().equals(vm.getHvsType())) { - for (VolumeTO volume : disks) { + for (DiskTO volume : disks) { if (volume.getType() == Volume.Type.ROOT) { + DataTO data = volume.getData(); + PrimaryDataStoreTO store = (PrimaryDataStoreTO)data.getDataStore(); KVMStoragePool pool = _storagePoolMgr.getStoragePool( - volume.getPoolType(), - volume.getPoolUuid()); - KVMPhysicalDisk physicalDisk = pool.getPhysicalDisk(volume.getPath()); + store.getPoolType(), + store.getUuid()); + KVMPhysicalDisk physicalDisk = pool.getPhysicalDisk(data.getPath()); FilesystemDef rootFs = new FilesystemDef(physicalDisk.getPath(), "/"); vm.getDevices().addDevice(rootFs); break; @@ -3394,16 +3397,6 @@ ServerResource { } - private VolumeTO getVolume(VirtualMachineTO vmSpec, Volume.Type type) { - VolumeTO volumes[] = vmSpec.getDisks(); - for (VolumeTO volume : volumes) { - if (volume.getType() == type) { - return volume; - } - } - return null; - } - private void createVif(LibvirtVMDef vm, NicTO nic) throws InternalErrorException, LibvirtException { vm.getDevices().addDevice( @@ -3432,7 +3425,7 @@ ServerResource { return new CheckSshAnswer(cmd); } - private boolean cleanupDisk(Connect conn, DiskDef disk) { + public boolean cleanupDisk(Connect conn, DiskDef disk) { // need to umount secondary storage String path = disk.getDiskPath(); String poolUuid = null; @@ -4237,28 +4230,6 @@ ServerResource { return parser.getEmulator(); } - private String getGuestType(Connect conn, String vmName) { - LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser(); - Domain dm = null; - try { - dm = conn.domainLookupByUUID(UUID.nameUUIDFromBytes(vmName - .getBytes())); - String xmlDesc = dm.getXMLDesc(0); - parser.parseDomainXML(xmlDesc); - return parser.getDescription(); - } catch (LibvirtException e) { - s_logger.trace("Ignoring libvirt error.", e); - return null; - } finally { - try { - if (dm != null) { - dm.free(); - } - } catch (LibvirtException l) { - s_logger.trace("Ignoring libvirt error.", l); - } - } - } boolean isGuestPVEnabled(String guestOS) { if (guestOS == null) { @@ -4296,14 +4267,6 @@ ServerResource { } } - private InterfaceDef.nicModel getGuestNicModel(String guestOSType) { - if (isGuestPVEnabled(guestOSType)) { - return InterfaceDef.nicModel.VIRTIO; - } else { - return InterfaceDef.nicModel.E1000; - } - } - private DiskDef.diskBus getGuestDiskModel(String guestOSType) { if (isGuestPVEnabled(guestOSType)) { return DiskDef.diskBus.VIRTIO; @@ -4337,7 +4300,7 @@ ServerResource { } } - private Domain getDomain(Connect conn, String vmName) + public Domain getDomain(Connect conn, String vmName) throws LibvirtException { return conn .domainLookupByUUID(UUID.nameUUIDFromBytes(vmName.getBytes())); @@ -4366,7 +4329,7 @@ ServerResource { } } - protected List getDisks(Connect conn, String vmName) { + public List getDisks(Connect conn, String vmName) { LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser(); Domain dm = null; try { @@ -4396,38 +4359,7 @@ ServerResource { return command.execute(); } - private String executeBashScript(String script, OutputInterpreter parser) { - Script command = new Script("/bin/bash", _timeout, s_logger); - command.add("-c"); - command.add(script); - return command.execute(parser); - } - private void deletExitingLinkLocalRoutTable(String linkLocalBr) { - Script command = new Script("/bin/bash", _timeout); - command.add("-c"); - command.add("ip route | grep " + NetUtils.getLinkLocalCIDR()); - OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser(); - String result = command.execute(parser); - boolean foundLinkLocalBr = false; - if (result == null && parser.getLines() != null) { - String[] lines = parser.getLines().split("\\n"); - for (String line : lines) { - String[] tokens = line.split(" "); - if (!tokens[2].equalsIgnoreCase(linkLocalBr)) { - Script.runSimpleBashScript("ip route del " - + NetUtils.getLinkLocalCIDR()); - } else { - foundLinkLocalBr = true; - } - } - } - if (!foundLinkLocalBr) { - Script.runSimpleBashScript("ip route add " - + NetUtils.getLinkLocalCIDR() + " dev " + linkLocalBr - + " src " + NetUtils.getLinkLocalGateway()); - } - } private class vmStats { long _usedTime; diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java index 9cddb2e4323..345b4e8e71c 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java @@ -354,7 +354,7 @@ public class LibvirtVMDef { } public static class DiskDef { - enum deviceType { + public enum deviceType { FLOPPY("floppy"), DISK("disk"), CDROM("cdrom"); String _type; @@ -396,7 +396,7 @@ public class LibvirtVMDef { } } - enum diskBus { + public enum diskBus { IDE("ide"), SCSI("scsi"), VIRTIO("virtio"), XEN("xen"), USB("usb"), UML( "uml"), FDC("fdc"); String _bus; @@ -411,7 +411,7 @@ public class LibvirtVMDef { } } - enum diskFmtType { + public enum diskFmtType { RAW("raw"), QCOW2("qcow2"); String _fmtType; diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java new file mode 100644 index 00000000000..c3fc0ec22a4 --- /dev/null +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java @@ -0,0 +1,879 @@ +/* + * 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.hypervisor.kvm.storage; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URISyntaxException; +import java.text.DateFormat; +import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.naming.ConfigurationException; + +import org.apache.cloudstack.storage.command.AttachAnswer; +import org.apache.cloudstack.storage.command.AttachCommand; +import org.apache.cloudstack.storage.command.CopyCmdAnswer; +import org.apache.cloudstack.storage.command.CopyCommand; +import org.apache.cloudstack.storage.command.CreateObjectAnswer; +import org.apache.cloudstack.storage.command.CreateObjectCommand; +import org.apache.cloudstack.storage.command.DeleteCommand; +import org.apache.cloudstack.storage.command.DettachAnswer; +import org.apache.cloudstack.storage.command.DettachCommand; +import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; +import org.apache.cloudstack.storage.to.SnapshotObjectTO; +import org.apache.cloudstack.storage.to.TemplateObjectTO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.cloudstack.utils.qemu.QemuImg; +import org.apache.cloudstack.utils.qemu.QemuImgException; +import org.apache.cloudstack.utils.qemu.QemuImgFile; +import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import org.apache.log4j.Logger; +import org.libvirt.Connect; +import org.libvirt.Domain; +import org.libvirt.DomainInfo; +import org.libvirt.DomainSnapshot; +import org.libvirt.LibvirtException; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.AttachVolumeAnswer; +import com.cloud.agent.api.BackupSnapshotAnswer; +import com.cloud.agent.api.ManageSnapshotAnswer; +import com.cloud.agent.api.ManageSnapshotCommand; +import com.cloud.agent.api.storage.CreateAnswer; +import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; +import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; +import com.cloud.agent.api.to.DiskTO; +import com.cloud.agent.api.to.NfsTO; +import com.cloud.agent.api.to.StorageFilerTO; +import com.cloud.agent.api.to.VolumeTO; +import com.cloud.exception.InternalErrorException; +import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; +import com.cloud.hypervisor.kvm.resource.LibvirtConnection; +import com.cloud.hypervisor.kvm.resource.LibvirtDomainXMLParser; +import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef; +import com.cloud.storage.JavaStorageLayer; +import com.cloud.storage.StorageLayer; +import com.cloud.storage.Storage.ImageFormat; +import com.cloud.storage.Storage.StoragePoolType; +import com.cloud.storage.resource.StorageProcessor; +import com.cloud.storage.template.Processor; +import com.cloud.storage.template.QCOW2Processor; +import com.cloud.storage.template.TemplateLocation; +import com.cloud.storage.template.Processor.FormatInfo; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; +import com.cloud.vm.DiskProfile; + +public class KVMStorageProcessor implements StorageProcessor { + private static final Logger s_logger = Logger.getLogger(KVMStorageProcessor.class); + private KVMStoragePoolManager storagePoolMgr; + private LibvirtComputingResource resource; + private StorageLayer storageLayer; + private String _createTmplPath; + private String _manageSnapshotPath; + private int _cmdsTimeout; + public KVMStorageProcessor(KVMStoragePoolManager storagePoolMgr, LibvirtComputingResource resource) { + this.storagePoolMgr = storagePoolMgr; + this.resource = resource; + } + + protected String getDefaultStorageScriptsDir() { + return "scripts/storage/qcow2"; + } + + public boolean configure(String name, Map params) + throws ConfigurationException { + storageLayer = new JavaStorageLayer(); + storageLayer.configure("StorageLayer", params); + + String storageScriptsDir = (String) params.get("storage.scripts.dir"); + if (storageScriptsDir == null) { + storageScriptsDir = getDefaultStorageScriptsDir(); + } + + _createTmplPath = Script + .findScript(storageScriptsDir, "createtmplt.sh"); + if (_createTmplPath == null) { + throw new ConfigurationException( + "Unable to find the createtmplt.sh"); + } + + _manageSnapshotPath = Script.findScript(storageScriptsDir, + "managesnapshot.sh"); + if (_manageSnapshotPath == null) { + throw new ConfigurationException( + "Unable to find the managesnapshot.sh"); + } + + String value = (String) params.get("cmds.timeout"); + _cmdsTimeout = NumbersUtil.parseInt(value, 7200) * 1000; + return true; + } + + @Override + public Answer copyTemplateToPrimaryStorage(CopyCommand cmd) { + DataTO srcData = cmd.getSrcTO(); + DataTO destData = cmd.getDestTO(); + TemplateObjectTO template = (TemplateObjectTO)srcData; + DataStoreTO imageStore = template.getDataStore(); + VolumeObjectTO volume = (VolumeObjectTO)destData; + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volume.getDataStore(); + + if (!(imageStore instanceof NfsTO)) { + return new CopyCmdAnswer("unsupported protocol"); + } + + NfsTO nfsImageStore = (NfsTO)imageStore; + String tmplturl = nfsImageStore.getUrl() + File.separator + template.getPath(); + int index = tmplturl.lastIndexOf("/"); + String mountpoint = tmplturl.substring(0, index); + String tmpltname = null; + if (index < tmplturl.length() - 1) { + tmpltname = tmplturl.substring(index + 1); + } + + KVMPhysicalDisk tmplVol = null; + KVMStoragePool secondaryPool = null; + try { + secondaryPool = storagePoolMgr.getStoragePoolByURI(mountpoint); + + /* Get template vol */ + if (tmpltname == null) { + secondaryPool.refresh(); + List disks = secondaryPool.listPhysicalDisks(); + if (disks == null || disks.isEmpty()) { + return new PrimaryStorageDownloadAnswer( + "Failed to get volumes from pool: " + + secondaryPool.getUuid()); + } + for (KVMPhysicalDisk disk : disks) { + if (disk.getName().endsWith("qcow2")) { + tmplVol = disk; + break; + } + } + if (tmplVol == null) { + return new PrimaryStorageDownloadAnswer( + "Failed to get template from pool: " + + secondaryPool.getUuid()); + } + } else { + tmplVol = secondaryPool.getPhysicalDisk(tmpltname); + } + + /* Copy volume to primary storage */ + KVMStoragePool primaryPool = storagePoolMgr.getStoragePool( + primaryStore.getPoolType(), + primaryStore.getUuid()); + + KVMPhysicalDisk primaryVol = storagePoolMgr.copyPhysicalDisk( + tmplVol, UUID.randomUUID().toString(), primaryPool); + + VolumeObjectTO newVol = new VolumeObjectTO(); + newVol.setPath(primaryVol.getName()); + newVol.setSize(primaryVol.getSize()); + return new CopyCmdAnswer(newVol); + } catch (CloudRuntimeException e) { + return new CopyCmdAnswer(e.toString()); + } finally { + if (secondaryPool != null) { + secondaryPool.delete(); + } + } + } + + // this is much like PrimaryStorageDownloadCommand, but keeping it separate + private KVMPhysicalDisk templateToPrimaryDownload(String templateUrl, KVMStoragePool primaryPool) { + int index = templateUrl.lastIndexOf("/"); + String mountpoint = templateUrl.substring(0, index); + String templateName = null; + if (index < templateUrl.length() - 1) { + templateName = templateUrl.substring(index + 1); + } + + KVMPhysicalDisk templateVol = null; + KVMStoragePool secondaryPool = null; + try { + secondaryPool = storagePoolMgr.getStoragePoolByURI(mountpoint); + /* Get template vol */ + if (templateName == null) { + secondaryPool.refresh(); + List disks = secondaryPool.listPhysicalDisks(); + if (disks == null || disks.isEmpty()) { + s_logger.error("Failed to get volumes from pool: " + secondaryPool.getUuid()); + return null; + } + for (KVMPhysicalDisk disk : disks) { + if (disk.getName().endsWith("qcow2")) { + templateVol = disk; + break; + } + } + if (templateVol == null) { + s_logger.error("Failed to get template from pool: " + secondaryPool.getUuid()); + return null; + } + } else { + templateVol = secondaryPool.getPhysicalDisk(templateName); + } + + /* Copy volume to primary storage */ + + KVMPhysicalDisk primaryVol = storagePoolMgr.copyPhysicalDisk(templateVol, UUID.randomUUID().toString(), primaryPool); + return primaryVol; + } catch (CloudRuntimeException e) { + s_logger.error("Failed to download template to primary storage",e); + return null; + } finally { + if (secondaryPool != null) { + secondaryPool.delete(); + } + } + } + + @Override + public Answer cloneVolumeFromBaseTemplate(CopyCommand cmd) { + DataTO srcData = cmd.getSrcTO(); + DataTO destData = cmd.getDestTO(); + TemplateObjectTO template = (TemplateObjectTO)srcData; + DataStoreTO imageStore = template.getDataStore(); + VolumeObjectTO volume = (VolumeObjectTO)destData; + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volume.getDataStore(); + KVMPhysicalDisk BaseVol = null; + KVMStoragePool primaryPool = null; + KVMPhysicalDisk vol = null; + + try { + primaryPool = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), + primaryStore.getUuid()); + + String templatePath = null; + if (imageStore instanceof NfsTO) { + NfsTO nfsImageStore = (NfsTO)imageStore; + templatePath = nfsImageStore.getUrl(); + } else { + s_logger.debug("Failed to create volume: "); + return new CopyCmdAnswer("Unsupported protocol"); + } + + if(primaryPool.getType() == StoragePoolType.CLVM) { + vol = templateToPrimaryDownload(templatePath, primaryPool); + } else { + BaseVol = primaryPool.getPhysicalDisk(templatePath); + vol = storagePoolMgr.createDiskFromTemplate(BaseVol, UUID + .randomUUID().toString(), primaryPool); + } + if (vol == null) { + return new CopyCmdAnswer( + " Can't create storage volume on storage pool"); + } + + VolumeObjectTO newVol = new VolumeObjectTO(); + newVol.setPath(vol.getName()); + newVol.setSize(vol.getSize()); + + return new CopyCmdAnswer(newVol); + } catch (CloudRuntimeException e) { + s_logger.debug("Failed to create volume: " + e.toString()); + return new CopyCmdAnswer(e.toString()); + } + } + + @Override + public Answer copyVolumeFromImageCacheToPrimary(CopyCommand cmd) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Answer copyVolumeFromPrimaryToSecondary(CopyCommand cmd) { + return null; + } + + @Override + public Answer createTemplateFromVolume(CopyCommand cmd) { + DataTO srcData = cmd.getSrcTO(); + DataTO destData = cmd.getDestTO(); + int wait = cmd.getWait(); + TemplateObjectTO template = (TemplateObjectTO)srcData; + DataStoreTO imageStore = template.getDataStore(); + VolumeObjectTO volume = (VolumeObjectTO)destData; + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volume.getDataStore(); + + if (!(imageStore instanceof NfsTO)) { + return new CopyCmdAnswer("unsupported protocol"); + } + NfsTO nfsImageStore = (NfsTO)imageStore; + + KVMStoragePool secondaryStorage = null; + KVMStoragePool primary = null; + try { + String templateFolder = template.getPath(); + + secondaryStorage = storagePoolMgr.getStoragePoolByURI( + nfsImageStore.getUrl()); + + try { + primary = storagePoolMgr.getStoragePool( + primaryStore.getPoolType(), + primaryStore.getUuid()); + } catch (CloudRuntimeException e) { + if (e.getMessage().contains("not found")) { + primary = storagePoolMgr.createStoragePool(primaryStore.getUuid(), + primaryStore.getHost(), primaryStore.getPort(), + primaryStore.getPath(), null, + primaryStore.getPoolType()); + } else { + return new CopyCmdAnswer(e.getMessage()); + } + } + + KVMPhysicalDisk disk = primary.getPhysicalDisk(volume.getPath()); + String tmpltPath = secondaryStorage.getLocalPath() + File.separator + + templateFolder; + this.storageLayer.mkdirs(tmpltPath); + String templateName = UUID.randomUUID().toString(); + + if (primary.getType() != StoragePoolType.RBD) { + Script command = new Script(_createTmplPath, wait, s_logger); + command.add("-f", disk.getPath()); + command.add("-t", tmpltPath); + command.add("-n", templateName + ".qcow2"); + + String result = command.execute(); + + if (result != null) { + s_logger.debug("failed to create template: " + result); + return new CopyCmdAnswer(result); + } + } else { + s_logger.debug("Converting RBD disk " + disk.getPath() + " into template " + templateName); + + QemuImgFile srcFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(primary.getSourceHost(), + primary.getSourcePort(), + primary.getAuthUserName(), + primary.getAuthSecret(), + disk.getPath())); + srcFile.setFormat(PhysicalDiskFormat.RAW); + + QemuImgFile destFile = new QemuImgFile(tmpltPath + "/" + templateName + ".qcow2"); + destFile.setFormat(PhysicalDiskFormat.QCOW2); + + QemuImg q = new QemuImg(); + try { + q.convert(srcFile, destFile); + } catch (QemuImgException e) { + s_logger.error("Failed to create new template while converting " + + srcFile.getFileName() + " to " + destFile.getFileName() + " the error was: " + e.getMessage()); + } + + File templateProp = new File(tmpltPath + "/template.properties"); + if (!templateProp.exists()) { + templateProp.createNewFile(); + } + + String templateContent = "filename=" + templateName + ".qcow2" + System.getProperty("line.separator"); + + DateFormat dateFormat = new SimpleDateFormat("MM_dd_yyyy"); + Date date = new Date(); + templateContent += "snapshot.name=" + dateFormat.format(date) + System.getProperty("line.separator"); + + FileOutputStream templFo = new FileOutputStream(templateProp); + templFo.write(templateContent.getBytes()); + templFo.flush(); + templFo.close(); + } + + Map params = new HashMap(); + params.put(StorageLayer.InstanceConfigKey, this.storageLayer); + Processor qcow2Processor = new QCOW2Processor(); + + qcow2Processor.configure("QCOW2 Processor", params); + + FormatInfo info = qcow2Processor.process(tmpltPath, null, + templateName); + + TemplateLocation loc = new TemplateLocation(this.storageLayer, tmpltPath); + loc.create(1, true, templateName); + loc.addFormat(info); + loc.save(); + + TemplateObjectTO newTemplate = new TemplateObjectTO(); + newTemplate.setPath(templateFolder + templateName + ".qcow2"); + return new CopyCmdAnswer(newTemplate); + } catch (Exception e) { + s_logger.debug("Failed to create template from volume: " + e.toString()); + return new CopyCmdAnswer(e.toString()); + } finally { + if (secondaryStorage != null) { + secondaryStorage.delete(); + } + } + } + + @Override + public Answer backupSnasphot(CopyCommand cmd) { + DataTO srcData = cmd.getSrcTO(); + DataTO destData = cmd.getDestTO(); + int wait = cmd.getWait(); + SnapshotObjectTO snapshot = (SnapshotObjectTO)srcData; + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)snapshot.getDataStore(); + SnapshotObjectTO destSnapshot = (SnapshotObjectTO)destData; + DataStoreTO imageStore = destData.getDataStore(); + + if (!(imageStore instanceof NfsTO)) { + return new CopyCmdAnswer("unsupported protocol"); + } + NfsTO nfsImageStore = (NfsTO)imageStore; + + String secondaryStoragePoolUrl = nfsImageStore.getUrl(); + //NOTE: snapshot name is encoded in snapshot path + int index = snapshot.getPath().lastIndexOf("/"); + + String snapshotName = snapshot.getPath().substring(index + 1); + String volumePath = snapshot.getVolume().getPath(); + String snapshotDestPath = null; + String snapshotRelPath = null; + String vmName = snapshot.getVmName(); + KVMStoragePool secondaryStoragePool = null; + try { + Connect conn = LibvirtConnection.getConnectionByVmName(vmName); + + secondaryStoragePool = storagePoolMgr.getStoragePoolByURI( + secondaryStoragePoolUrl); + + String ssPmountPath = secondaryStoragePool.getLocalPath(); + snapshotRelPath = destSnapshot.getPath(); + + snapshotDestPath = ssPmountPath + File.separator + snapshotRelPath; + KVMStoragePool primaryPool = storagePoolMgr.getStoragePool( + primaryStore.getPoolType(), + primaryStore.getUuid()); + KVMPhysicalDisk snapshotDisk = primaryPool.getPhysicalDisk(volumePath); + Script command = new Script(_manageSnapshotPath, _cmdsTimeout, + s_logger); + command.add("-b", snapshotDisk.getPath()); + command.add("-n", snapshotName); + command.add("-p", snapshotDestPath); + command.add("-t", snapshotName); + String result = command.execute(); + if (result != null) { + s_logger.debug("Failed to backup snaptshot: " + result); + return new CopyCmdAnswer(result); + } + /* Delete the snapshot on primary */ + + DomainInfo.DomainState state = null; + Domain vm = null; + if (vmName != null) { + try { + vm = this.resource.getDomain(conn, vmName); + state = vm.getInfo().state; + } catch (LibvirtException e) { + s_logger.trace("Ignoring libvirt error.", e); + } + } + + KVMStoragePool primaryStorage = storagePoolMgr.getStoragePool( + primaryStore.getPoolType(), + primaryStore.getUuid()); + if (state == DomainInfo.DomainState.VIR_DOMAIN_RUNNING + && !primaryStorage.isExternalSnapshot()) { + DomainSnapshot snap = vm.snapshotLookupByName(snapshotName); + snap.delete(0); + + /* + * libvirt on RHEL6 doesn't handle resume event emitted from + * qemu + */ + vm = this.resource.getDomain(conn, vmName); + state = vm.getInfo().state; + if (state == DomainInfo.DomainState.VIR_DOMAIN_PAUSED) { + vm.resume(); + } + } else { + command = new Script(_manageSnapshotPath, _cmdsTimeout, + s_logger); + command.add("-d", snapshotDisk.getPath()); + command.add("-n", snapshotName); + result = command.execute(); + if (result != null) { + s_logger.debug("Failed to backup snapshot: " + result); + return new CopyCmdAnswer( + "Failed to backup snapshot: " + result); + } + } + + SnapshotObjectTO newSnapshot = new SnapshotObjectTO(); + newSnapshot.setPath(snapshotRelPath + + File.separator + snapshotName); + return new CopyCmdAnswer(newSnapshot); + } catch (LibvirtException e) { + s_logger.debug("Failed to backup snapshot: " + e.toString()); + return new CopyCmdAnswer(e.toString()); + } catch (CloudRuntimeException e) { + s_logger.debug("Failed to backup snapshot: " + e.toString()); + return new CopyCmdAnswer(e.toString()); + } finally { + if (secondaryStoragePool != null) { + secondaryStoragePool.delete(); + } + } + } + + protected synchronized String attachOrDetachISO(Connect conn, + String vmName, String isoPath, boolean isAttach) + throws LibvirtException, URISyntaxException, InternalErrorException { + String isoXml = null; + if (isoPath != null && isAttach) { + int index = isoPath.lastIndexOf("/"); + String path = isoPath.substring(0, index); + String name = isoPath.substring(index + 1); + KVMStoragePool secondaryPool = storagePoolMgr.getStoragePoolByURI( + path); + KVMPhysicalDisk isoVol = secondaryPool.getPhysicalDisk(name); + isoPath = isoVol.getPath(); + + DiskDef iso = new DiskDef(); + iso.defISODisk(isoPath); + isoXml = iso.toString(); + } else { + DiskDef iso = new DiskDef(); + iso.defISODisk(null); + isoXml = iso.toString(); + } + + List disks = this.resource.getDisks(conn, vmName); + String result = attachOrDetachDevice(conn, true, vmName, isoXml); + if (result == null && !isAttach) { + for (DiskDef disk : disks) { + if (disk.getDeviceType() == DiskDef.deviceType.CDROM) { + this.resource.cleanupDisk(conn, disk); + } + } + + } + return result; + } + @Override + public Answer attachIso(AttachCommand cmd) { + DiskTO disk = cmd.getDisk(); + TemplateObjectTO isoTO = (TemplateObjectTO)disk.getData(); + DataStoreTO store = isoTO.getDataStore(); + if (!(store instanceof NfsTO)) { + return new AttachAnswer("unsupported protocol"); + } + NfsTO nfsStore = (NfsTO)store; + try { + Connect conn = LibvirtConnection.getConnectionByVmName(cmd.getVmName()); + attachOrDetachISO(conn, cmd.getVmName(), nfsStore.getUrl() + File.separator + isoTO.getPath(), + true); + } catch (LibvirtException e) { + return new Answer(cmd, false, e.toString()); + } catch (URISyntaxException e) { + return new Answer(cmd, false, e.toString()); + } catch (InternalErrorException e) { + return new Answer(cmd, false, e.toString()); + } + + return new Answer(cmd); + } + + protected synchronized String attachOrDetachDevice(Connect conn, + boolean attach, String vmName, String xml) throws LibvirtException, + InternalErrorException { + Domain dm = null; + try { + dm = conn.domainLookupByUUID(UUID.nameUUIDFromBytes((vmName + .getBytes()))); + + if (attach) { + s_logger.debug("Attaching device: " + xml); + dm.attachDevice(xml); + } else { + s_logger.debug("Detaching device: " + xml); + dm.detachDevice(xml); + } + } catch (LibvirtException e) { + if (attach) { + s_logger.warn("Failed to attach device to " + vmName + ": " + + e.getMessage()); + } else { + s_logger.warn("Failed to detach device from " + vmName + ": " + + e.getMessage()); + } + throw e; + } finally { + if (dm != null) { + try { + dm.free(); + } catch (LibvirtException l) { + s_logger.trace("Ignoring libvirt error.", l); + } + } + } + + return null; + } + + protected synchronized String attachOrDetachDisk(Connect conn, + boolean attach, String vmName, KVMPhysicalDisk attachingDisk, + int devId) throws LibvirtException, InternalErrorException { + List disks = null; + Domain dm = null; + DiskDef diskdef = null; + try { + if (!attach) { + dm = conn.domainLookupByUUID(UUID.nameUUIDFromBytes(vmName + .getBytes())); + LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser(); + String xml = dm.getXMLDesc(0); + parser.parseDomainXML(xml); + disks = parser.getDisks(); + + for (DiskDef disk : disks) { + String file = disk.getDiskPath(); + if (file != null + && file.equalsIgnoreCase(attachingDisk.getPath())) { + diskdef = disk; + break; + } + } + if (diskdef == null) { + throw new InternalErrorException("disk: " + + attachingDisk.getPath() + + " is not attached before"); + } + } else { + diskdef = new DiskDef(); + if (attachingDisk.getFormat() == PhysicalDiskFormat.QCOW2) { + diskdef.defFileBasedDisk(attachingDisk.getPath(), devId, + DiskDef.diskBus.VIRTIO, DiskDef.diskFmtType.QCOW2); + } else if (attachingDisk.getFormat() == PhysicalDiskFormat.RAW) { + diskdef.defBlockBasedDisk(attachingDisk.getPath(), devId, + DiskDef.diskBus.VIRTIO); + } + } + + String xml = diskdef.toString(); + return attachOrDetachDevice(conn, attach, vmName, xml); + } finally { + if (dm != null) { + dm.free(); + } + } + } + + @Override + public Answer attachVolume(AttachCommand cmd) { + DiskTO disk = cmd.getDisk(); + VolumeObjectTO vol = (VolumeObjectTO)disk.getData(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)vol.getDataStore(); + String vmName = vol.getVmName(); + try { + Connect conn = LibvirtConnection.getConnectionByVmName(vmName); + KVMStoragePool primary = storagePoolMgr.getStoragePool( + primaryStore.getPoolType(), + primaryStore.getUuid()); + + KVMPhysicalDisk phyDisk = primary.getPhysicalDisk(vol.getPath()); + attachOrDetachDisk(conn, true, vmName, phyDisk, + disk.getDiskSeq().intValue()); + + return new AttachAnswer(disk); + } catch (LibvirtException e) { + s_logger.debug("Failed to attach volume: " + vol.getPath() + ", due to " + e.toString()); + return new AttachAnswer(e.toString()); + } catch (InternalErrorException e) { + s_logger.debug("Failed to attach volume: " + vol.getPath() + ", due to " + e.toString()); + return new AttachAnswer(e.toString()); + } + + + } + + @Override + public Answer dettachIso(DettachCommand cmd) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Answer dettachVolume(DettachCommand cmd) { + DiskTO disk = cmd.getDisk(); + VolumeObjectTO vol = (VolumeObjectTO)disk.getData(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)vol.getDataStore(); + String vmName = vol.getVmName(); + try { + Connect conn = LibvirtConnection.getConnectionByVmName(vmName); + KVMStoragePool primary = storagePoolMgr.getStoragePool( + primaryStore.getPoolType(), + primaryStore.getUuid()); + + KVMPhysicalDisk phyDisk = primary.getPhysicalDisk(vol.getPath()); + attachOrDetachDisk(conn, false, vmName, phyDisk, + disk.getDiskSeq().intValue()); + + return new DettachAnswer(disk); + } catch (LibvirtException e) { + s_logger.debug("Failed to attach volume: " + vol.getPath() + ", due to " + e.toString()); + return new DettachAnswer(e.toString()); + } catch (InternalErrorException e) { + s_logger.debug("Failed to attach volume: " + vol.getPath() + ", due to " + e.toString()); + return new DettachAnswer(e.toString()); + } + } + + @Override + public Answer createVolume(CreateObjectCommand cmd) { + VolumeObjectTO volume = (VolumeObjectTO)cmd.getData(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volume.getDataStore(); + + KVMStoragePool primaryPool = null; + KVMPhysicalDisk vol = null; + long disksize; + try { + primaryPool = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), + primaryStore.getUuid()); + disksize = volume.getSize(); + + vol = primaryPool.createPhysicalDisk(UUID.randomUUID() + .toString(), disksize); + + VolumeObjectTO newVol = new VolumeObjectTO(); + newVol.setPath(vol.getName()); + + return new CreateObjectAnswer(newVol); + } catch (Exception e) { + s_logger.debug("Failed to create volume: " + e.toString()); + return new CreateObjectAnswer(e.toString()); + } + } + + protected static MessageFormat SnapshotXML = new MessageFormat( + " " + " {0}" + " " + + " {1}" + " " + + " "); + + @Override + public Answer createSnapshot(CreateObjectCommand cmd) { + VolumeObjectTO volume = (VolumeObjectTO)cmd.getData(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volume.getDataStore(); + String snapshotName = UUID.randomUUID().toString(); + String vmName = volume.getVmName(); + try { + Connect conn = LibvirtConnection.getConnectionByVmName(vmName); + DomainInfo.DomainState state = null; + Domain vm = null; + if (vmName != null) { + try { + vm = this.resource.getDomain(conn, vmName); + state = vm.getInfo().state; + } catch (LibvirtException e) { + s_logger.trace("Ignoring libvirt error.", e); + } + } + + KVMStoragePool primaryPool = storagePoolMgr.getStoragePool( + primaryStore.getPoolType(), + primaryStore.getUuid()); + + if (primaryPool.getType() == StoragePoolType.RBD) { + s_logger.debug("Snapshots are not supported on RBD volumes"); + return new CreateObjectAnswer( + "Snapshots are not supported on RBD volumes"); + } + + KVMPhysicalDisk disk = primaryPool.getPhysicalDisk(volume.getPath()); + if (state == DomainInfo.DomainState.VIR_DOMAIN_RUNNING + && !primaryPool.isExternalSnapshot()) { + String vmUuid = vm.getUUIDString(); + Object[] args = new Object[] { snapshotName, vmUuid }; + String snapshot = SnapshotXML.format(args); + s_logger.debug(snapshot); + + vm.snapshotCreateXML(snapshot); + /* + * libvirt on RHEL6 doesn't handle resume event emitted from + * qemu + */ + vm = this.resource.getDomain(conn, vmName); + state = vm.getInfo().state; + if (state == DomainInfo.DomainState.VIR_DOMAIN_PAUSED) { + vm.resume(); + } + } else { + + /* VM is not running, create a snapshot by ourself */ + final Script command = new Script(_manageSnapshotPath, + this._cmdsTimeout, s_logger); + command.add("-c", disk.getPath()); + command.add("-n", snapshotName); + String result = command.execute(); + if (result != null) { + s_logger.debug("Failed to manage snapshot: " + result); + return new CreateObjectAnswer( + "Failed to manage snapshot: " + result); + } + } + + SnapshotObjectTO newSnapshot = new SnapshotObjectTO(); + //NOTE: sort of hack, we'd better just put snapshtoName + newSnapshot.setPath(disk.getPath() + File.separator + snapshotName); + return new CreateObjectAnswer(newSnapshot); + } catch (LibvirtException e) { + s_logger.debug("Failed to manage snapshot: " + e.toString()); + return new CreateObjectAnswer( + "Failed to manage snapshot: " + e.toString()); + } + } + + @Override + public Answer deleteVolume(DeleteCommand cmd) { + VolumeObjectTO vol = (VolumeObjectTO)cmd.getData(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)vol.getDataStore(); + try { + KVMStoragePool pool = storagePoolMgr.getStoragePool( + primaryStore.getPoolType(), + primaryStore.getUuid()); + try { + pool.getPhysicalDisk(vol.getPath()); + } catch(Exception e) { + s_logger.debug("can't find volume: " + vol.getPath() + ", return true"); + return new Answer(null); + } + pool.deletePhysicalDisk(vol.getPath()); + return new Answer(null); + } catch (CloudRuntimeException e) { + s_logger.debug("Failed to delete volume: " + e.toString()); + return new Answer(null, false, e.toString()); + } + } + +} diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageResource.java new file mode 100644 index 00000000000..52e142e4e13 --- /dev/null +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageResource.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package com.cloud.hypervisor.kvm.storage; + +import org.apache.cloudstack.storage.command.AttachCommand; +import org.apache.cloudstack.storage.command.AttachPrimaryDataStoreCmd; +import org.apache.cloudstack.storage.command.CopyCommand; +import org.apache.cloudstack.storage.command.CreateObjectCommand; +import org.apache.cloudstack.storage.command.CreatePrimaryDataStoreCmd; +import org.apache.cloudstack.storage.command.DeleteCommand; +import org.apache.cloudstack.storage.command.DettachCommand; +import org.apache.cloudstack.storage.command.StorageSubSystemCommand; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; + +public class KVMStorageResource { + private LibvirtComputingResource resource; + public KVMStorageResource(LibvirtComputingResource resource) { + this.resource = resource; + } + + public Answer handleStorageCommands(StorageSubSystemCommand command) { + if (command instanceof CopyCommand) { + return this.execute((CopyCommand)command); + } else if (command instanceof AttachPrimaryDataStoreCmd) { + return this.execute((AttachPrimaryDataStoreCmd)command); + } else if (command instanceof CreatePrimaryDataStoreCmd) { + return execute((CreatePrimaryDataStoreCmd) command); + } else if (command instanceof CreateObjectCommand) { + return execute((CreateObjectCommand) command); + } else if (command instanceof DeleteCommand) { + return execute((DeleteCommand)command); + } else if (command instanceof AttachCommand) { + return execute((AttachCommand)command); + } else if (command instanceof DettachCommand) { + return execute((DettachCommand)command); + } + return new Answer((Command)command, false, "not implemented yet"); + } + + protected Answer execute(CopyCommand cmd) { + return new Answer((Command)cmd, false, "not implemented yet"); + } + + protected Answer execute(AttachPrimaryDataStoreCmd cmd) { + return new Answer((Command)cmd, false, "not implemented yet"); + } + + protected Answer execute(CreatePrimaryDataStoreCmd cmd) { + return new Answer((Command)cmd, false, "not implemented yet"); + } + + protected Answer execute(CreateObjectCommand cmd) { + return new Answer((Command)cmd, false, "not implemented yet"); + } + + protected Answer execute(DeleteCommand cmd) { + return new Answer((Command)cmd, false, "not implemented yet"); + } + + protected Answer execute(AttachCommand cmd) { + return new Answer((Command)cmd, false, "not implemented yet"); + } + + protected Answer execute(DettachCommand cmd) { + return new Answer((Command)cmd, false, "not implemented yet"); + } + +} diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 0f85f4e71ca..f91786fd0af 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -39,6 +39,11 @@ import java.util.UUID; import javax.naming.ConfigurationException; +import org.apache.cloudstack.storage.command.DeleteCommand; +import org.apache.cloudstack.storage.command.StorageSubSystemCommand; +import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; +import org.apache.cloudstack.storage.to.TemplateObjectTO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.log4j.Logger; import org.apache.log4j.NDC; @@ -162,8 +167,11 @@ import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; import com.cloud.agent.api.storage.ResizeVolumeAnswer; import com.cloud.agent.api.storage.ResizeVolumeCommand; +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DiskTO; import com.cloud.agent.api.to.FirewallRuleTO; import com.cloud.agent.api.to.IpAddressTO; +import com.cloud.agent.api.to.NfsTO; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.PortForwardingRuleTO; import com.cloud.agent.api.to.StaticNatRuleTO; @@ -177,6 +185,7 @@ import com.cloud.host.Host.Type; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.vmware.manager.VmwareHostService; import com.cloud.hypervisor.vmware.manager.VmwareManager; +import com.cloud.hypervisor.vmware.manager.VmwareStorageMount; import com.cloud.hypervisor.vmware.mo.ClusterMO; import com.cloud.hypervisor.vmware.mo.CustomFieldConstants; import com.cloud.hypervisor.vmware.mo.CustomFieldsManagerMO; @@ -210,8 +219,12 @@ import com.cloud.storage.Storage; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.Volume; import com.cloud.storage.resource.StoragePoolResource; +import com.cloud.storage.resource.StorageSubsystemCommandHandler; +import com.cloud.storage.resource.StorageSubsystemCommandHandlerBase; +import com.cloud.storage.resource.VmwareStorageProcessor; import com.cloud.storage.template.TemplateProp; import com.cloud.utils.DateUtil; +import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.StringUtils; import com.cloud.utils.db.DB; @@ -313,6 +326,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa protected Gson _gson; protected volatile long _cmdSequence = 1; + + protected StorageSubsystemCommandHandler storageHandler; protected static HashMap s_statesTable; static { @@ -321,6 +336,10 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa s_statesTable.put(VirtualMachinePowerState.POWERED_OFF, State.Stopped); s_statesTable.put(VirtualMachinePowerState.SUSPENDED, State.Stopped); } + + public Gson getGson() { + return this._gson; + } public VmwareResource() { _gson = GsonHelper.getGsonLogger(); @@ -472,6 +491,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa answer = execute((CheckS2SVpnConnectionsCommand) cmd); } else if (clz == ResizeVolumeCommand.class) { return execute((ResizeVolumeCommand) cmd); + } else if (clz == StorageSubSystemCommand.class) { + return this.storageHandler.handleStorageCommands((StorageSubSystemCommand)cmd); } else { answer = Answer.createUnsupportedCommandAnswer(cmd); } @@ -2026,14 +2047,21 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa return new CheckSshAnswer(cmd); } - private VolumeTO[] validateDisks(VolumeTO[] disks) { - List validatedDisks = new ArrayList(); + private DiskTO[] validateDisks(DiskTO[] disks) { + List validatedDisks = new ArrayList(); - for (VolumeTO vol : disks) { - if (vol.getPoolUuid() != null && !vol.getPoolUuid().isEmpty()) { - validatedDisks.add(vol); - } else if (vol.getPoolType() == StoragePoolType.ISO && (vol.getPath() != null && !vol.getPath().isEmpty())) { - validatedDisks.add(vol); + for (DiskTO vol : disks) { + if (vol.getType() != Volume.Type.ISO) { + VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volumeTO.getDataStore(); + if (primaryStore.getUuid() != null && !primaryStore.getUuid().isEmpty()) { + validatedDisks.add(vol); + } + } else if (vol.getType() == Volume.Type.ISO) { + TemplateObjectTO templateTO = (TemplateObjectTO)vol.getData(); + if (templateTO.getPath() != null && !templateTO.getPath().isEmpty()) { + validatedDisks.add(vol); + } } else { if (s_logger.isDebugEnabled()) { s_logger.debug("Drop invalid disk option, volumeTO: " + _gson.toJson(vol)); @@ -2041,7 +2069,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } - return validatedDisks.toArray(new VolumeTO[0]); + return validatedDisks.toArray(new DiskTO[0]); } protected StartAnswer execute(StartCommand cmd) { @@ -2068,7 +2096,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa s_logger.debug("VM " + vmName + " will be started with NIC device type: " + nicDeviceType); VmwareHypervisorHost hyperHost = getHyperHost(context); - VolumeTO[] disks = validateDisks(vmSpec.getDisks()); + DiskTO[] disks = validateDisks(vmSpec.getDisks()); assert (disks.length > 0); NicTO[] nics = vmSpec.getNics(); @@ -2105,9 +2133,10 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } else { int ramMb = (int) (vmSpec.getMinRam() / (1024 * 1024)); Pair rootDiskDataStoreDetails = null; - for (VolumeTO vol : disks) { + for (DiskTO vol : disks) { if (vol.getType() == Volume.Type.ROOT) { - rootDiskDataStoreDetails = dataStoresDetails.get(vol.getPoolUuid()); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)vol.getData().getDataStore(); + rootDiskDataStoreDetails = dataStoresDetails.get(primaryStore.getUuid()); } } @@ -2126,12 +2155,12 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } int totalChangeDevices = disks.length + nics.length; - VolumeTO volIso = null; + DiskTO volIso = null; if (vmSpec.getType() != VirtualMachine.Type.User) { // system VM needs a patch ISO totalChangeDevices++; } else { - for (VolumeTO vol : disks) { + for (DiskTO vol : disks) { if (vol.getType() == Volume.Type.ISO) { volIso = vol; break; @@ -2188,23 +2217,35 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa i++; } else { // we will always plugin a CDROM device - if (volIso != null && volIso.getPath() != null && !volIso.getPath().isEmpty()) { - Pair isoDatastoreInfo = getIsoDatastoreInfo(hyperHost, volIso.getPath()); - assert (isoDatastoreInfo != null); - assert (isoDatastoreInfo.second() != null); + + if (volIso != null) { + TemplateObjectTO iso = (TemplateObjectTO)volIso.getData(); - deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); - Pair isoInfo = VmwareHelper.prepareIsoDevice(vmMo, isoDatastoreInfo.first(), isoDatastoreInfo.second(), true, true, i, i + 1); - deviceConfigSpecArray[i].setDevice(isoInfo.first()); - if (isoInfo.second()) { - if(s_logger.isDebugEnabled()) - s_logger.debug("Prepare ISO volume at new device " + _gson.toJson(isoInfo.first())); - deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.ADD); - } else { - if(s_logger.isDebugEnabled()) - s_logger.debug("Prepare ISO volume at existing device " + _gson.toJson(isoInfo.first())); - deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.EDIT); - } + if (iso.getPath() != null && !iso.getPath().isEmpty()) { + DataStoreTO imageStore = iso.getDataStore(); + if (!(imageStore instanceof NfsTO)) { + s_logger.debug("unsupported protocol"); + throw new Exception("unsupported protocol"); + } + NfsTO nfsImageStore = (NfsTO)imageStore; + String isoPath = nfsImageStore.getUrl() + File.separator + iso.getPath(); + Pair isoDatastoreInfo = getIsoDatastoreInfo(hyperHost, isoPath); + assert (isoDatastoreInfo != null); + assert (isoDatastoreInfo.second() != null); + + deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); + Pair isoInfo = VmwareHelper.prepareIsoDevice(vmMo, isoDatastoreInfo.first(), isoDatastoreInfo.second(), true, true, i, i + 1); + deviceConfigSpecArray[i].setDevice(isoInfo.first()); + if (isoInfo.second()) { + if(s_logger.isDebugEnabled()) + s_logger.debug("Prepare ISO volume at new device " + _gson.toJson(isoInfo.first())); + deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.ADD); + } else { + if(s_logger.isDebugEnabled()) + s_logger.debug("Prepare ISO volume at existing device " + _gson.toJson(isoInfo.first())); + deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.EDIT); + } + } } else { deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); Pair isoInfo = VmwareHelper.prepareIsoDevice(vmMo, null, null, true, true, i, i + 1); @@ -2224,7 +2265,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa i++; } - for (VolumeTO vol : sortVolumesByDeviceId(disks)) { + for (DiskTO vol : sortVolumesByDeviceId(disks)) { deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); if (vol.getType() == Volume.Type.ISO) { @@ -2247,11 +2288,13 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } if (vol.getType() != Volume.Type.ISO) { - Pair volumeDsDetails = dataStoresDetails.get(vol.getPoolUuid()); + VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volumeTO.getDataStore(); + Pair volumeDsDetails = dataStoresDetails.get(primaryStore.getUuid()); assert (volumeDsDetails != null); VirtualDevice device; - datastoreDiskPath = String.format("[%s] %s.vmdk", volumeDsDetails.second().getName(), vol.getPath()); - String chainInfo = vol.getChainInfo(); + datastoreDiskPath = String.format("[%s] %s.vmdk", volumeDsDetails.second().getName(), volumeTO.getPath()); + String chainInfo = volumeTO.getChainInfo(); if (chainInfo != null && !chainInfo.isEmpty()) { String[] diskChain = _gson.fromJson(chainInfo, String[].class); @@ -2424,19 +2467,19 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa return listForSort.toArray(new NicTO[0]); } - private VolumeTO[] sortVolumesByDeviceId(VolumeTO[] volumes) { + private DiskTO[] sortVolumesByDeviceId(DiskTO[] volumes) { - List listForSort = new ArrayList(); - for (VolumeTO vol : volumes) { + List listForSort = new ArrayList(); + for (DiskTO vol : volumes) { listForSort.add(vol); } - Collections.sort(listForSort, new Comparator() { + Collections.sort(listForSort, new Comparator() { @Override - public int compare(VolumeTO arg0, VolumeTO arg1) { - if (arg0.getDeviceId() < arg1.getDeviceId()) { + public int compare(DiskTO arg0, DiskTO arg1) { + if (arg0.getDiskSeq() < arg1.getDiskSeq()) { return -1; - } else if (arg0.getDeviceId() == arg1.getDeviceId()) { + } else if (arg0.getDiskSeq() == arg1.getDiskSeq()) { return 0; } @@ -2444,16 +2487,18 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } }); - return listForSort.toArray(new VolumeTO[0]); + return listForSort.toArray(new DiskTO[0]); } - private HashMap> inferDatastoreDetailsFromDiskInfo(VmwareHypervisorHost hyperHost, VmwareContext context, VolumeTO[] disks) throws Exception { + private HashMap> inferDatastoreDetailsFromDiskInfo(VmwareHypervisorHost hyperHost, VmwareContext context, DiskTO[] disks) throws Exception { HashMap> poolMors = new HashMap>(); assert (hyperHost != null) && (context != null); - for (VolumeTO vol : disks) { + for (DiskTO vol : disks) { if (vol.getType() != Volume.Type.ISO) { - String poolUuid = vol.getPoolUuid(); + VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volumeTO.getDataStore(); + String poolUuid = primaryStore.getUuid(); if(poolMors.get(poolUuid) == null) { ManagedObjectReference morDataStore = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, poolUuid); if (morDataStore == null) { @@ -2461,7 +2506,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa s_logger.error(msg); throw new Exception(msg); } - poolMors.put(vol.getPoolUuid(), new Pair (morDataStore, new DatastoreMO(context, morDataStore))); + poolMors.put(poolUuid, new Pair (morDataStore, new DatastoreMO(context, morDataStore))); } } } @@ -3306,7 +3351,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } - private synchronized ManagedObjectReference prepareSecondaryDatastoreOnHost(String storeUrl) throws Exception { + public synchronized ManagedObjectReference prepareSecondaryDatastoreOnHost(String storeUrl) throws Exception { String storeName = getSecondaryDatastoreUUID(storeUrl); URI uri = new URI(storeUrl); @@ -3670,8 +3715,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } - @Override - public Answer execute(DestroyCommand cmd) { + + public Answer execute(DeleteCommand cmd) { if (s_logger.isInfoEnabled()) { s_logger.info("Executing resource DestroyCommand: " + _gson.toJson(cmd)); } @@ -3691,11 +3736,12 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa try { VmwareContext context = getServiceContext(); VmwareHypervisorHost hyperHost = getHyperHost(context); - VolumeTO vol = cmd.getVolume(); + VolumeObjectTO vol = (VolumeObjectTO)cmd.getData(); + PrimaryDataStoreTO store = (PrimaryDataStoreTO)vol.getDataStore(); - ManagedObjectReference morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, vol.getPoolUuid()); + ManagedObjectReference morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, store.getUuid()); if (morDs == null) { - String msg = "Unable to find datastore based on volume mount point " + cmd.getVolume().getMountPoint(); + String msg = "Unable to find datastore based on volume mount point " + store.getPath(); s_logger.error(msg); throw new Exception(msg); } @@ -3706,8 +3752,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa ManagedObjectReference morCluster = hyperHost.getHyperHostCluster(); ClusterMO clusterMo = new ClusterMO(context, morCluster); - if (cmd.getVolume().getType() == Volume.Type.ROOT) { - String vmName = cmd.getVmName(); + if (vol.getVolumeType() == Volume.Type.ROOT) { + String vmName = vol.getVmName(); if (vmName != null) { VirtualMachineMO vmMo = clusterMo.findVmOnHyperHost(vmName); if (vmMo != null) { @@ -3734,20 +3780,20 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } if (s_logger.isInfoEnabled()) - s_logger.info("Destroy volume by original name: " + cmd.getVolume().getPath() + ".vmdk"); - dsMo.deleteFile(cmd.getVolume().getPath() + ".vmdk", morDc, true); + s_logger.info("Destroy volume by original name: " + vol.getPath() + ".vmdk"); + dsMo.deleteFile(vol.getPath() + ".vmdk", morDc, true); // root volume may be created via linked-clone, delete the delta disk as well if (_fullCloneFlag) { if (s_logger.isInfoEnabled()) { - s_logger.info("Destroy volume by derived name: " + cmd.getVolume().getPath() + "-flat.vmdk"); + s_logger.info("Destroy volume by derived name: " + vol.getPath() + "-flat.vmdk"); } - dsMo.deleteFile(cmd.getVolume().getPath() + "-flat.vmdk", morDc, true); + dsMo.deleteFile(vol.getPath() + "-flat.vmdk", morDc, true); } else { if (s_logger.isInfoEnabled()) { - s_logger.info("Destroy volume by derived name: " + cmd.getVolume().getPath() + "-delta.vmdk"); + s_logger.info("Destroy volume by derived name: " + vol.getPath() + "-delta.vmdk"); } - dsMo.deleteFile(cmd.getVolume().getPath() + "-delta.vmdk", morDc, true); + dsMo.deleteFile(vol.getPath() + "-delta.vmdk", morDc, true); } return new Answer(cmd, true, "Success"); } @@ -3757,17 +3803,17 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } else { // evitTemplate will be converted into DestroyCommand, test if we are running in this case - VirtualMachineMO vmMo = clusterMo.findVmOnHyperHost(cmd.getVolume().getPath()); + VirtualMachineMO vmMo = clusterMo.findVmOnHyperHost(vol.getPath()); if (vmMo != null) { if (s_logger.isInfoEnabled()) - s_logger.info("Destroy template volume " + cmd.getVolume().getPath()); + s_logger.info("Destroy template volume " + vol.getPath()); vmMo.destroy(); return new Answer(cmd, true, "Success"); } } - String chainInfo = cmd.getVolume().getChainInfo(); + String chainInfo = vol.getChainInfo(); if (chainInfo != null && !chainInfo.isEmpty()) { s_logger.info("Destroy volume by chain info: " + chainInfo); String[] diskChain = _gson.fromJson(chainInfo, String[].class); @@ -3783,23 +3829,23 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa if (s_logger.isInfoEnabled()) { s_logger.info("Empty disk chain info, fall back to try to delete by original backing file name"); } - dsMo.deleteFile(cmd.getVolume().getPath() + ".vmdk", morDc, true); + dsMo.deleteFile(vol.getPath() + ".vmdk", morDc, true); if (s_logger.isInfoEnabled()) { - s_logger.info("Destroy volume by derived name: " + cmd.getVolume().getPath() + "-flat.vmdk"); + s_logger.info("Destroy volume by derived name: " + vol.getPath() + "-flat.vmdk"); } - dsMo.deleteFile(cmd.getVolume().getPath() + "-flat.vmdk", morDc, true); + dsMo.deleteFile(vol.getPath() + "-flat.vmdk", morDc, true); } } else { if (s_logger.isInfoEnabled()) { - s_logger.info("Destroy volume by original name: " + cmd.getVolume().getPath() + ".vmdk"); + s_logger.info("Destroy volume by original name: " + vol.getPath() + ".vmdk"); } - dsMo.deleteFile(cmd.getVolume().getPath() + ".vmdk", morDc, true); + dsMo.deleteFile(vol.getPath() + ".vmdk", morDc, true); if (s_logger.isInfoEnabled()) { - s_logger.info("Destroy volume by derived name: " + cmd.getVolume().getPath() + "-flat.vmdk"); + s_logger.info("Destroy volume by derived name: " + vol.getPath() + "-flat.vmdk"); } - dsMo.deleteFile(cmd.getVolume().getPath() + "-flat.vmdk", morDc, true); + dsMo.deleteFile(vol.getPath() + "-flat.vmdk", morDc, true); } return new Answer(cmd, true, "Success"); @@ -3815,7 +3861,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } - private void cleanupNetwork(HostMO hostMo, NetworkDetails netDetails) { + public void cleanupNetwork(HostMO hostMo, NetworkDetails netDetails) { // we will no longer cleanup VLAN networks in order to support native VMware HA /* * assert(netDetails.getName() != null); try { synchronized(this) { NetworkMO networkMo = new @@ -4857,7 +4903,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa return s_statesTable.get(powerState); } - private static State getVmState(VirtualMachineMO vmMo) throws Exception { + public static State getVmState(VirtualMachineMO vmMo) throws Exception { VirtualMachineRuntimeInfo runtimeInfo = vmMo.getRuntimeInfo(); return convertState(runtimeInfo.getPowerState()); } @@ -4991,6 +5037,13 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } else { _fullCloneFlag = false; } + value = (String)params.get("scripts.timeout"); + int timeout = NumbersUtil.parseInt(value, 1440) * 1000; + VmwareManager mgr = context.getStockObject(VmwareManager.CONTEXT_STOCK_NAME); + VmwareStorageProcessor storageProcessor = new VmwareStorageProcessor((VmwareHostService)this, this._fullCloneFlag, (VmwareStorageMount)mgr, + timeout, this, this._shutdown_waitMs + ); + storageHandler = new StorageSubsystemCommandHandlerBase(storageProcessor); return true; } @@ -5010,15 +5063,15 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa return true; } - private VmwareContext getServiceContext() { + public VmwareContext getServiceContext() { return getServiceContext(null); } - private void invalidateServiceContext() { + public void invalidateServiceContext() { invalidateServiceContext(null); } - private VmwareHypervisorHost getHyperHost(VmwareContext context) { + public VmwareHypervisorHost getHyperHost(VmwareContext context) { return getHyperHost(context, null); } @@ -5115,4 +5168,10 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa // TODO Auto-generated method stub } + + @Override + public Answer execute(DestroyCommand cmd) { + // TODO Auto-generated method stub + return null; + } } diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java index 566e750c3fe..645b6d30380 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java @@ -18,6 +18,7 @@ package com.cloud.storage.resource; import java.util.List; +import org.apache.cloudstack.storage.command.StorageSubSystemCommand; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; @@ -29,6 +30,7 @@ import com.cloud.agent.api.CreateVolumeFromSnapshotCommand; import com.cloud.agent.api.storage.CopyVolumeCommand; import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; import com.cloud.hypervisor.vmware.manager.VmwareHostService; +import com.cloud.hypervisor.vmware.manager.VmwareManager; import com.cloud.hypervisor.vmware.manager.VmwareStorageManager; import com.cloud.hypervisor.vmware.manager.VmwareStorageManagerImpl; import com.cloud.hypervisor.vmware.manager.VmwareStorageMount; @@ -51,6 +53,7 @@ public class VmwareSecondaryStorageResourceHandler implements SecondaryStorageRe private final VmwareStorageManager _storageMgr; private final Gson _gson; + private StorageSubsystemCommandHandler storageSubsystemHandler; /* private Map _activeHosts = new HashMap(); @@ -60,6 +63,11 @@ public class VmwareSecondaryStorageResourceHandler implements SecondaryStorageRe _resource = resource; _storageMgr = new VmwareStorageManagerImpl(this); _gson = GsonHelper.getGsonLogger(); + + VmwareStorageProcessor storageProcessor = new VmwareStorageProcessor((VmwareHostService)this, true, (VmwareStorageMount)this, + null, null, null + ); + storageSubsystemHandler = new StorageSubsystemCommandHandlerBase(storageProcessor); } @Override @@ -77,6 +85,8 @@ public class VmwareSecondaryStorageResourceHandler implements SecondaryStorageRe answer = execute((CopyVolumeCommand)cmd); } else if(cmd instanceof CreateVolumeFromSnapshotCommand) { answer = execute((CreateVolumeFromSnapshotCommand)cmd); + } else if (cmd instanceof StorageSubSystemCommand) { + answer = storageSubsystemHandler.handleStorageCommands((StorageSubSystemCommand)cmd); } else { answer = _resource.defaultAction(cmd); } diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java new file mode 100644 index 00000000000..988658dbc7c --- /dev/null +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java @@ -0,0 +1,1144 @@ +package com.cloud.storage.resource; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.net.URI; +import java.rmi.RemoteException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.apache.cloudstack.storage.command.AttachAnswer; +import org.apache.cloudstack.storage.command.AttachCommand; +import org.apache.cloudstack.storage.command.CopyCmdAnswer; +import org.apache.cloudstack.storage.command.CopyCommand; +import org.apache.cloudstack.storage.command.CreateObjectAnswer; +import org.apache.cloudstack.storage.command.CreateObjectCommand; +import org.apache.cloudstack.storage.command.DeleteCommand; +import org.apache.cloudstack.storage.command.DettachCommand; +import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; +import org.apache.cloudstack.storage.to.SnapshotObjectTO; +import org.apache.cloudstack.storage.to.TemplateObjectTO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.BackupSnapshotAnswer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.ManageSnapshotAnswer; +import com.cloud.agent.api.ManageSnapshotCommand; +import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; +import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; +import com.cloud.agent.api.to.DiskTO; +import com.cloud.agent.api.to.NfsTO; +import com.cloud.agent.api.to.VolumeTO; +import com.cloud.hypervisor.vmware.manager.VmwareHostService; +import com.cloud.hypervisor.vmware.manager.VmwareStorageMount; +import com.cloud.hypervisor.vmware.mo.ClusterMO; +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.NetworkDetails; +import com.cloud.hypervisor.vmware.mo.VirtualMachineMO; +import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost; +import com.cloud.hypervisor.vmware.resource.VmwareResource; +import com.cloud.hypervisor.vmware.util.VmwareContext; +import com.cloud.hypervisor.vmware.util.VmwareHelper; +import com.cloud.serializer.GsonHelper; +import com.cloud.storage.JavaStorageLayer; +import com.cloud.storage.StorageLayer; +import com.cloud.storage.Volume; +import com.cloud.storage.Storage.ImageFormat; +import com.cloud.storage.template.VmdkProcessor; +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.State; +import com.google.gson.Gson; +import com.vmware.vim25.ManagedObjectReference; +import com.vmware.vim25.VirtualDeviceConfigSpec; +import com.vmware.vim25.VirtualDeviceConfigSpecOperation; +import com.vmware.vim25.VirtualDisk; +import com.vmware.vim25.VirtualEthernetCard; +import com.vmware.vim25.VirtualLsiLogicController; +import com.vmware.vim25.VirtualMachineConfigSpec; +import com.vmware.vim25.VirtualMachineFileInfo; +import com.vmware.vim25.VirtualMachineGuestOsIdentifier; +import com.vmware.vim25.VirtualSCSISharing; + +public class VmwareStorageProcessor implements StorageProcessor { + private static final Logger s_logger = Logger.getLogger(VmwareStorageProcessor.class); + private VmwareHostService hostService; + private boolean _fullCloneFlag; + private VmwareStorageMount mountService; + private VmwareResource resource; + private Integer _timeout; + protected Integer _shutdown_waitMs; + private final Gson _gson; + private final StorageLayer _storage = new JavaStorageLayer(); + public VmwareStorageProcessor(VmwareHostService hostService, boolean fullCloneFlag, VmwareStorageMount mountService, + Integer timeout, + VmwareResource resource, + Integer shutdownWaitMs) { + this.hostService = hostService; + this._fullCloneFlag = fullCloneFlag; + this.mountService = mountService; + this._timeout = timeout; + this.resource = resource; + this._shutdown_waitMs = shutdownWaitMs; + _gson = GsonHelper.getGsonLogger(); + } + + private String getOVFFilePath(String srcOVAFileName) { + File file = new File(srcOVAFileName); + assert(_storage != null); + String[] files = _storage.listFiles(file.getParent()); + if(files != null) { + for(String fileName : files) { + if(fileName.toLowerCase().endsWith(".ovf")) { + File ovfFile = new File(fileName); + return file.getParent() + File.separator + ovfFile.getName(); + } + } + } + return null; + } + private void copyTemplateFromSecondaryToPrimary(VmwareHypervisorHost hyperHost, DatastoreMO datastoreMo, String secondaryStorageUrl, + String templatePathAtSecondaryStorage, String templateName, String templateUuid) throws Exception { + + s_logger.info("Executing copyTemplateFromSecondaryToPrimary. secondaryStorage: " + + secondaryStorageUrl + ", templatePathAtSecondaryStorage: " + templatePathAtSecondaryStorage + + ", templateName: " + templateName); + + String secondaryMountPoint = mountService.getMountPoint(secondaryStorageUrl); + s_logger.info("Secondary storage mount point: " + secondaryMountPoint); + + String srcOVAFileName = secondaryMountPoint + "/" + templatePathAtSecondaryStorage + + templateName + "." + ImageFormat.OVA.getFileExtension(); + + String srcFileName = getOVFFilePath(srcOVAFileName); + if(srcFileName == null) { + Script command = new Script("tar", 0, s_logger); + command.add("--no-same-owner"); + command.add("-xf", srcOVAFileName); + command.setWorkDir(secondaryMountPoint + "/" + templatePathAtSecondaryStorage); + s_logger.info("Executing command: " + command.toString()); + String result = command.execute(); + if(result != null) { + String msg = "Unable to unpack snapshot OVA file at: " + srcOVAFileName; + s_logger.error(msg); + throw new Exception(msg); + } + } + + srcFileName = getOVFFilePath(srcOVAFileName); + if(srcFileName == null) { + String msg = "Unable to locate OVF file in template package directory: " + srcOVAFileName; + s_logger.error(msg); + throw new Exception(msg); + } + + String vmName = templateUuid; + hyperHost.importVmFromOVF(srcFileName, vmName, datastoreMo, "thin"); + + VirtualMachineMO vmMo = hyperHost.findVmOnHyperHost(vmName); + if(vmMo == null) { + String msg = "Failed to import OVA template. secondaryStorage: " + + secondaryStorageUrl + ", templatePathAtSecondaryStorage: " + templatePathAtSecondaryStorage + + ", templateName: " + templateName + ", templateUuid: " + templateUuid; + s_logger.error(msg); + throw new Exception(msg); + } + + if(vmMo.createSnapshot("cloud.template.base", "Base snapshot", false, false)) { + vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_UUID, templateUuid); + vmMo.markAsTemplate(); + } else { + vmMo.destroy(); + String msg = "Unable to create base snapshot for template, templateName: " + templateName + ", templateUuid: " + templateUuid; + s_logger.error(msg); + throw new Exception(msg); + } + } + + @Override + public Answer copyTemplateToPrimaryStorage(CopyCommand cmd) { + DataTO srcData = cmd.getSrcTO(); + TemplateObjectTO template = (TemplateObjectTO)srcData; + DataStoreTO srcStore = srcData.getDataStore(); + if (!(srcStore instanceof NfsTO)) { + return new CopyCmdAnswer("unsupported protocol"); + } + NfsTO nfsImageStore = (NfsTO)srcStore; + DataTO destData = cmd.getDestTO(); + DataStoreTO destStore = destData.getDataStore(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)destStore; + String secondaryStorageUrl = nfsImageStore.getUrl(); + assert (secondaryStorageUrl != null); + + String templateUrl = secondaryStorageUrl + File.separator + srcData.getPath(); + + String templateName = null; + String mountPoint = null; + if (templateUrl.endsWith(".ova")) { + int index = templateUrl.lastIndexOf("/"); + mountPoint = templateUrl.substring(0, index); + mountPoint = mountPoint.substring(secondaryStorageUrl.length() + 1); + if (!mountPoint.endsWith("/")) { + mountPoint = mountPoint + "/"; + } + + templateName = templateUrl.substring(index + 1).replace("." + ImageFormat.OVA.getFileExtension(), ""); + + if (templateName == null || templateName.isEmpty()) { + templateName = template.getName(); + } + } else { + mountPoint = templateUrl.substring(secondaryStorageUrl.length() + 1); + if (!mountPoint.endsWith("/")) { + mountPoint = mountPoint + "/"; + } + templateName = template.getName(); + } + + VmwareContext context = hostService.getServiceContext(cmd); + try { + VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd); + + String templateUuidName = UUID.nameUUIDFromBytes((templateName + "@" + primaryStore.getUuid() + "-" + hyperHost.getMor().getValue()).getBytes()).toString(); + // truncate template name to 32 chars to ensure they work well with vSphere API's. + templateUuidName = templateUuidName.replace("-", ""); + + DatacenterMO dcMo = new DatacenterMO(context, hyperHost.getHyperHostDatacenter()); + VirtualMachineMO templateMo = VmwareHelper.pickOneVmOnRunningHost(dcMo.findVmByNameAndLabel(templateUuidName), true); + + if (templateMo == null) { + if(s_logger.isInfoEnabled()) + s_logger.info("Template " + templateName + " is not setup yet, setup template from secondary storage with uuid name: " + templateUuidName); + ManagedObjectReference morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, primaryStore.getUuid()); + assert (morDs != null); + DatastoreMO primaryStorageDatastoreMo = new DatastoreMO(context, morDs); + + copyTemplateFromSecondaryToPrimary(hyperHost, + primaryStorageDatastoreMo, secondaryStorageUrl, + mountPoint, templateName, templateUuidName); + } else { + s_logger.info("Template " + templateName + " has already been setup, skip the template setup process in primary storage"); + } + + TemplateObjectTO newTemplate = new TemplateObjectTO(); + newTemplate.setPath(templateUuidName); + return new CopyCmdAnswer(newTemplate); + } catch (Throwable e) { + if (e instanceof RemoteException) { + hostService.invalidateServiceContext(context); + } + + String msg = "Unable to execute PrimaryStorageDownloadCommand due to exception"; + s_logger.error(msg, e); + return new CopyCmdAnswer(msg); + } + } + private boolean createVMLinkedClone(VirtualMachineMO vmTemplate, DatacenterMO dcMo, DatastoreMO dsMo, + String vmdkName, ManagedObjectReference morDatastore, ManagedObjectReference morPool) throws Exception { + + ManagedObjectReference morBaseSnapshot = vmTemplate.getSnapshotMor("cloud.template.base"); + if (morBaseSnapshot == null) { + String msg = "Unable to find template base snapshot, invalid template"; + s_logger.error(msg); + throw new Exception(msg); + } + + if(dsMo.folderExists(String.format("[%s]", dsMo.getName()), vmdkName)) + dsMo.deleteFile(String.format("[%s] %s/", dsMo.getName(), vmdkName), dcMo.getMor(), false); + + s_logger.info("creating linked clone from template"); + if (!vmTemplate.createLinkedClone(vmdkName, morBaseSnapshot, dcMo.getVmFolder(), morPool, morDatastore)) { + String msg = "Unable to clone from the template"; + s_logger.error(msg); + throw new Exception(msg); + } + + // we can't rely on un-offical API (VirtualMachineMO.moveAllVmDiskFiles() any more, use hard-coded disk names that we know + // to move files + s_logger.info("Move volume out of volume-wrapper VM "); + dsMo.moveDatastoreFile(String.format("[%s] %s/%s.vmdk", dsMo.getName(), vmdkName, vmdkName), + dcMo.getMor(), dsMo.getMor(), + String.format("[%s] %s.vmdk", dsMo.getName(), vmdkName), dcMo.getMor(), true); + + dsMo.moveDatastoreFile(String.format("[%s] %s/%s-delta.vmdk", dsMo.getName(), vmdkName, vmdkName), + dcMo.getMor(), dsMo.getMor(), + String.format("[%s] %s-delta.vmdk", dsMo.getName(), vmdkName), dcMo.getMor(), true); + + return true; + } + + private boolean createVMFullClone(VirtualMachineMO vmTemplate, DatacenterMO dcMo, DatastoreMO dsMo, + String vmdkName, ManagedObjectReference morDatastore, ManagedObjectReference morPool) throws Exception { + + if(dsMo.folderExists(String.format("[%s]", dsMo.getName()), vmdkName)) + dsMo.deleteFile(String.format("[%s] %s/", dsMo.getName(), vmdkName), dcMo.getMor(), false); + + s_logger.info("creating full clone from template"); + if (!vmTemplate.createFullClone(vmdkName, dcMo.getVmFolder(), morPool, morDatastore)) { + String msg = "Unable to create full clone from the template"; + s_logger.error(msg); + throw new Exception(msg); + } + + // we can't rely on un-offical API (VirtualMachineMO.moveAllVmDiskFiles() any more, use hard-coded disk names that we know + // to move files + s_logger.info("Move volume out of volume-wrapper VM "); + dsMo.moveDatastoreFile(String.format("[%s] %s/%s.vmdk", dsMo.getName(), vmdkName, vmdkName), + dcMo.getMor(), dsMo.getMor(), + String.format("[%s] %s.vmdk", dsMo.getName(), vmdkName), dcMo.getMor(), true); + + dsMo.moveDatastoreFile(String.format("[%s] %s/%s-flat.vmdk", dsMo.getName(), vmdkName, vmdkName), + dcMo.getMor(), dsMo.getMor(), + String.format("[%s] %s-flat.vmdk", dsMo.getName(), vmdkName), dcMo.getMor(), true); + + return true; + } + + @Override + public Answer cloneVolumeFromBaseTemplate(CopyCommand cmd) { + DataTO srcData = cmd.getSrcTO(); + TemplateObjectTO template = (TemplateObjectTO)srcData; + DataStoreTO imageStore = template.getDataStore(); + DataTO destData = cmd.getDestTO(); + VolumeObjectTO volume = (VolumeObjectTO)destData; + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volume.getDataStore(); + if (imageStore != null && !(imageStore instanceof NfsTO)) { + return new CopyCmdAnswer("unsupported protocol"); + } + NfsTO nfsImageStore = (NfsTO)imageStore; + + try { + VmwareContext context = this.hostService.getServiceContext(null); + VmwareHypervisorHost hyperHost = this.hostService.getHyperHost(context, null); + DatacenterMO dcMo = new DatacenterMO(context, hyperHost.getHyperHostDatacenter()); + VirtualMachineMO vmMo = null; + ManagedObjectReference morDatastore = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, primaryStore.getUuid()); + if (morDatastore == null) + throw new Exception("Unable to find datastore in vSphere"); + + DatastoreMO dsMo = new DatastoreMO(context, morDatastore); + + + // attach volume id to make the name unique + String vmdkName = volume.getName() + "-" + volume.getId(); + if (nfsImageStore == null) { + // create a root volume for blank VM + String dummyVmName = this.hostService.getWorkerName(context, cmd, 0); + + try { + vmMo = prepareVolumeHostDummyVm(hyperHost, dsMo, dummyVmName); + if (vmMo == null) { + throw new Exception("Unable to create a dummy VM for volume creation"); + } + + String volumeDatastorePath = String.format("[%s] %s.vmdk", dsMo.getName(), vmdkName); + synchronized (this) { + s_logger.info("Delete file if exists in datastore to clear the way for creating the volume. file: " + volumeDatastorePath); + VmwareHelper.deleteVolumeVmdkFiles(dsMo, vmdkName, dcMo); + vmMo.createDisk(volumeDatastorePath, (int) (volume.getSize() / (1024L * 1024L)), morDatastore, -1); + vmMo.detachDisk(volumeDatastorePath, false); + } + + VolumeObjectTO newVol = new VolumeObjectTO(); + newVol.setPath(vmdkName); + return new CopyCmdAnswer(newVol); + } finally { + vmMo.detachAllDisks(); + + s_logger.info("Destroy dummy VM after volume creation"); + vmMo.destroy(); + } + } else { + String templateUrl = nfsImageStore.getUrl() + File.separator + template.getPath(); + VirtualMachineMO vmTemplate = VmwareHelper.pickOneVmOnRunningHost(dcMo.findVmByNameAndLabel(templateUrl), true); + if (vmTemplate == null) { + s_logger.warn("Template host in vSphere is not in connected state, request template reload"); + return new CopyCmdAnswer("Template host in vSphere is not in connected state, request template reload"); + } + + ManagedObjectReference morPool = hyperHost.getHyperHostOwnerResourcePool(); + ManagedObjectReference morCluster = hyperHost.getHyperHostCluster(); + //createVMLinkedClone(vmTemplate, dcMo, dsMo, vmdkName, morDatastore, morPool); + if (!_fullCloneFlag) { + createVMLinkedClone(vmTemplate, dcMo, dsMo, vmdkName, morDatastore, morPool); + } else { + createVMFullClone(vmTemplate, dcMo, dsMo, vmdkName, morDatastore, morPool); + } + + vmMo = new ClusterMO(context, morCluster).findVmOnHyperHost(vmdkName); + assert (vmMo != null); + + s_logger.info("detach disks from volume-wrapper VM " + vmdkName); + vmMo.detachAllDisks(); + + s_logger.info("destroy volume-wrapper VM " + vmdkName); + vmMo.destroy(); + + String srcFile = String.format("[%s] %s/", dsMo.getName(), vmdkName); + dsMo.deleteFile(srcFile, dcMo.getMor(), true); + VolumeObjectTO newVol = new VolumeObjectTO(); + newVol.setPath(vmdkName); + return new CopyCmdAnswer(newVol); + } + } catch (Throwable e) { + if (e instanceof RemoteException) { + s_logger.warn("Encounter remote exception to vCenter, invalidate VMware session context"); + this.hostService.invalidateServiceContext(null); + } + + String msg = "CreateCommand failed due to " + VmwareHelper.getExceptionMessage(e); + s_logger.error(msg, e); + return new CopyCmdAnswer(e.toString()); + } + } + + + @Override + public Answer copyVolumeFromImageCacheToPrimary(CopyCommand cmd) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Answer copyVolumeFromPrimaryToSecondary(CopyCommand cmd) { + // TODO Auto-generated method stub + return null; + } + + private void postCreatePrivateTemplate(String installFullPath, long templateId, + String templateName, long size, long virtualSize) throws Exception { + + // TODO a bit ugly here + BufferedWriter out = null; + try { + out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(installFullPath + "/template.properties"))); + out.write("filename=" + templateName + ".ova"); + out.newLine(); + out.write("description="); + out.newLine(); + out.write("checksum="); + out.newLine(); + out.write("hvm=false"); + out.newLine(); + out.write("size=" + size); + out.newLine(); + out.write("ova=true"); + out.newLine(); + out.write("id=" + templateId); + out.newLine(); + out.write("public=false"); + out.newLine(); + out.write("ova.filename=" + templateName + ".ova"); + out.newLine(); + out.write("uniquename=" + templateName); + out.newLine(); + out.write("ova.virtualsize=" + virtualSize); + out.newLine(); + out.write("virtualsize=" + virtualSize); + out.newLine(); + out.write("ova.size=" + size); + out.newLine(); + } finally { + if(out != null) + out.close(); + } + } + + private Ternary createTemplateFromVolume(VirtualMachineMO vmMo, String installPath, long templateId, String templateUniqueName, + String secStorageUrl, String volumePath, String workerVmName) throws Exception { + + String secondaryMountPoint = mountService.getMountPoint(secStorageUrl); + String installFullPath = secondaryMountPoint + "/" + installPath; + synchronized(installPath.intern()) { + Script command = new Script(false, "mkdir", _timeout, s_logger); + command.add("-p"); + command.add(installFullPath); + + String result = command.execute(); + if(result != null) { + String msg = "unable to prepare template directory: " + + installPath + ", storage: " + secStorageUrl + ", error msg: " + result; + s_logger.error(msg); + throw new Exception(msg); + } + } + + VirtualMachineMO clonedVm = null; + try { + Pair volumeDeviceInfo = vmMo.getDiskDevice(volumePath, false); + if(volumeDeviceInfo == null) { + String msg = "Unable to find related disk device for volume. volume path: " + volumePath; + s_logger.error(msg); + throw new Exception(msg); + } + + if(!vmMo.createSnapshot(templateUniqueName, "Temporary snapshot for template creation", false, false)) { + String msg = "Unable to take snapshot for creating template from volume. volume path: " + volumePath; + s_logger.error(msg); + throw new Exception(msg); + } + + // 4 MB is the minimum requirement for VM memory in VMware + vmMo.cloneFromCurrentSnapshot(workerVmName, 0, 4, volumeDeviceInfo.second(), + VmwareHelper.getDiskDeviceDatastore(volumeDeviceInfo.first())); + clonedVm = vmMo.getRunningHost().findVmOnHyperHost(workerVmName); + if(clonedVm == null) { + String msg = "Unable to create dummy VM to export volume. volume path: " + volumePath; + s_logger.error(msg); + throw new Exception(msg); + } + + clonedVm.exportVm(secondaryMountPoint + "/" + installPath, templateUniqueName, true, false); + + long physicalSize = new File(installFullPath + "/" + templateUniqueName + ".ova").length(); + VmdkProcessor processor = new VmdkProcessor(); + Map params = new HashMap(); + params.put(StorageLayer.InstanceConfigKey, _storage); + processor.configure("VMDK Processor", params); + long virtualSize = processor.getTemplateVirtualSize(installFullPath, templateUniqueName); + + postCreatePrivateTemplate(installFullPath, templateId, templateUniqueName, physicalSize, virtualSize); + return new Ternary(installPath + "/" + templateUniqueName + ".ova", physicalSize, virtualSize); + + } finally { + if(clonedVm != null) { + clonedVm.detachAllDisks(); + clonedVm.destroy(); + } + + vmMo.removeSnapshot(templateUniqueName, false); + } + } + + @Override + public Answer createTemplateFromVolume(CopyCommand cmd) { + VolumeObjectTO volume = (VolumeObjectTO)cmd.getSrcTO(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volume.getDataStore(); + TemplateObjectTO template = (TemplateObjectTO)cmd.getDestTO(); + DataStoreTO imageStore = template.getDataStore(); + + if (!(imageStore instanceof NfsTO)) { + return new CopyCmdAnswer("unsupported protocol"); + } + NfsTO nfsImageStore = (NfsTO)imageStore; + String secondaryStoragePoolURL = nfsImageStore.getUrl(); + String volumePath = volume.getPath(); + + String details = null; + + VmwareContext context = hostService.getServiceContext(cmd); + try { + VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd); + + VirtualMachineMO vmMo = hyperHost.findVmOnHyperHost(volume.getVmName()); + if (vmMo == null) { + if(s_logger.isDebugEnabled()) + s_logger.debug("Unable to find the owner VM for CreatePrivateTemplateFromVolumeCommand on host " + hyperHost.getHyperHostName() + ", try within datacenter"); + vmMo = hyperHost.findVmOnPeerHyperHost(volume.getVmName()); + + if(vmMo == null) { + String msg = "Unable to find the owner VM for volume operation. vm: " + volume.getVmName(); + s_logger.error(msg); + throw new Exception(msg); + } + } + + Ternary result = createTemplateFromVolume(vmMo, + template.getPath(), template.getId(), template.getName(), + secondaryStoragePoolURL, volumePath, + hostService.getWorkerName(context, cmd, 0)); + + TemplateObjectTO newTemplate = new TemplateObjectTO(); + newTemplate.setPath(template.getName()); + newTemplate.setFormat(ImageFormat.OVA); + return new CopyCmdAnswer(newTemplate); + + } catch (Throwable e) { + if (e instanceof RemoteException) { + hostService.invalidateServiceContext(context); + } + + s_logger.error("Unexpecpted exception ", e); + + details = "CreatePrivateTemplateFromVolumeCommand exception: " + StringUtils.getExceptionStackInfo(e); + return new CopyCmdAnswer(details); + } + } + + private void exportVolumeToSecondaryStroage(VirtualMachineMO vmMo, String volumePath, + String secStorageUrl, String secStorageDir, String exportName, + String workerVmName) throws Exception { + + String secondaryMountPoint = mountService.getMountPoint(secStorageUrl); + String exportPath = secondaryMountPoint + "/" + secStorageDir + "/" + exportName; + + synchronized(exportPath.intern()) { + if(!new File(exportPath).exists()) { + Script command = new Script(false, "mkdir", _timeout, s_logger); + command.add("-p"); + command.add(exportPath); + if(command.execute() != null) + throw new Exception("unable to prepare snapshot backup directory"); + } + } + + VirtualMachineMO clonedVm = null; + try { + + Pair volumeDeviceInfo = vmMo.getDiskDevice(volumePath, false); + if(volumeDeviceInfo == null) { + String msg = "Unable to find related disk device for volume. volume path: " + volumePath; + s_logger.error(msg); + throw new Exception(msg); + } + + // 4 MB is the minimum requirement for VM memory in VMware + vmMo.cloneFromCurrentSnapshot(workerVmName, 0, 4, volumeDeviceInfo.second(), + VmwareHelper.getDiskDeviceDatastore(volumeDeviceInfo.first())); + clonedVm = vmMo.getRunningHost().findVmOnHyperHost(workerVmName); + if(clonedVm == null) { + String msg = "Unable to create dummy VM to export volume. volume path: " + volumePath; + s_logger.error(msg); + throw new Exception(msg); + } + + clonedVm.exportVm(exportPath, exportName, true, true); + } finally { + if(clonedVm != null) { + clonedVm.detachAllDisks(); + clonedVm.destroy(); + } + } + } + + + private String backupSnapshotToSecondaryStorage(VirtualMachineMO vmMo, String installPath, + String volumePath, String snapshotUuid, String secStorageUrl, + String prevSnapshotUuid, String prevBackupUuid, String workerVmName) throws Exception { + + String backupUuid = UUID.randomUUID().toString(); + exportVolumeToSecondaryStroage(vmMo, volumePath, secStorageUrl, + installPath, backupUuid, workerVmName); + return backupUuid + "/" + backupUuid; + } + @Override + public Answer backupSnasphot(CopyCommand cmd) { + SnapshotObjectTO srcSnapshot = (SnapshotObjectTO)cmd.getSrcTO(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)srcSnapshot.getDataStore(); + SnapshotObjectTO destSnapshot = (SnapshotObjectTO)cmd.getDestTO(); + DataStoreTO destStore = destSnapshot.getDataStore(); + if (!(destStore instanceof NfsTO)) { + return new CopyCmdAnswer("unsupported protocol"); + } + + NfsTO destNfsStore = (NfsTO)destStore; + + + String secondaryStorageUrl = destNfsStore.getUrl(); + String snapshotUuid = srcSnapshot.getPath(); + String prevSnapshotUuid = srcSnapshot.getParentSnapshotPath(); + String prevBackupUuid = destSnapshot.getParentSnapshotPath(); + VirtualMachineMO workerVm=null; + String workerVMName = null; + String volumePath = srcSnapshot.getVolume().getPath(); + ManagedObjectReference morDs = null; + DatastoreMO dsMo=null; + + // By default assume failure + String details = null; + boolean success = false; + String snapshotBackupUuid = null; + + VmwareContext context = hostService.getServiceContext(cmd); + VirtualMachineMO vmMo = null; + String vmName = srcSnapshot.getVmName(); + try { + VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd); + morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, primaryStore.getUuid()); + + try { + vmMo = hyperHost.findVmOnHyperHost(vmName); + if (vmMo == null) { + if(s_logger.isDebugEnabled()) + s_logger.debug("Unable to find owner VM for BackupSnapshotCommand on host " + hyperHost.getHyperHostName() + ", will try within datacenter"); + + vmMo = hyperHost.findVmOnPeerHyperHost(vmName); + if(vmMo == null) { + dsMo = new DatastoreMO(hyperHost.getContext(), morDs); + + workerVMName = hostService.getWorkerName(context, cmd, 0); + + // attach a volume to dummay wrapper VM for taking snapshot and exporting the VM for backup + if (!hyperHost.createBlankVm(workerVMName, 1, 512, 0, false, 4, 0, VirtualMachineGuestOsIdentifier.OTHER_GUEST.value(), morDs, false)) { + String msg = "Unable to create worker VM to execute BackupSnapshotCommand"; + s_logger.error(msg); + throw new Exception(msg); + } + vmMo = hyperHost.findVmOnHyperHost(workerVMName); + if (vmMo == null) { + throw new Exception("Failed to find the newly create or relocated VM. vmName: " + workerVMName); + } + workerVm = vmMo; + + // attach volume to worker VM + String datastoreVolumePath = String.format("[%s] %s.vmdk", dsMo.getName(), volumePath); + vmMo.attachDisk(new String[] { datastoreVolumePath }, morDs); + } + } + + if (!vmMo.createSnapshot(snapshotUuid, "Snapshot taken for " + srcSnapshot.getName(), false, false)) { + throw new Exception("Failed to take snapshot " + srcSnapshot.getName() + " on vm: " + vmName); + } + + snapshotBackupUuid = backupSnapshotToSecondaryStorage(vmMo, destSnapshot.getPath(), srcSnapshot.getVolume().getPath(), snapshotUuid, secondaryStorageUrl, prevSnapshotUuid, prevBackupUuid, + hostService.getWorkerName(context, cmd, 1)); + + success = (snapshotBackupUuid != null); + if (success) { + details = "Successfully backedUp the snapshotUuid: " + snapshotUuid + " to secondary storage."; + return new CopyCmdAnswer(details); + } else { + SnapshotObjectTO newSnapshot = new SnapshotObjectTO(); + newSnapshot.setPath(snapshotBackupUuid); + return new CopyCmdAnswer(newSnapshot); + } + } finally { + if(vmMo != null){ + ManagedObjectReference snapshotMor = vmMo.getSnapshotMor(snapshotUuid); + if (snapshotMor != null){ + vmMo.removeSnapshot(snapshotUuid, false); + } + } + + try { + if (workerVm != null) { + // detach volume and destroy worker vm + workerVm.detachAllDisks(); + workerVm.destroy(); + } + } catch (Throwable e) { + s_logger.warn("Failed to destroy worker VM: " + workerVMName); + } + } + } catch (Throwable e) { + if (e instanceof RemoteException) { + hostService.invalidateServiceContext(context); + } + + s_logger.error("Unexpecpted exception ", e); + + details = "BackupSnapshotCommand exception: " + StringUtils.getExceptionStackInfo(e); + return new CopyCmdAnswer(details); + } + } + + @Override + public Answer attachIso(AttachCommand cmd) { + return this.attachIso(cmd.getDisk(), true, cmd.getVmName()); + } + + @Override + public Answer attachVolume(AttachCommand cmd) { + return this.attachVolume(cmd, cmd.getDisk(), true, cmd.getVmName()); + } + + private Answer attachVolume(Command cmd, DiskTO disk, boolean isAttach, String vmName) { + + VolumeObjectTO volumeTO = (VolumeObjectTO)disk.getData(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volumeTO.getDataStore(); + try { + VmwareHypervisorHost hyperHost = this.hostService.getHyperHost(this.hostService.getServiceContext(null), null); + VirtualMachineMO vmMo = hyperHost.findVmOnHyperHost(vmName); + if (vmMo == null) { + String msg = "Unable to find the VM to execute AttachVolumeCommand, vmName: " + vmName; + s_logger.error(msg); + throw new Exception(msg); + } + + ManagedObjectReference morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, primaryStore.getUuid()); + if (morDs == null) { + String msg = "Unable to find the mounted datastore to execute AttachVolumeCommand, vmName: " + vmName; + s_logger.error(msg); + throw new Exception(msg); + } + + DatastoreMO dsMo = new DatastoreMO(this.hostService.getServiceContext(null), morDs); + String datastoreVolumePath = String.format("[%s] %s.vmdk", dsMo.getName(), volumeTO.getPath()); + + AttachAnswer answer = new AttachAnswer(disk); + if (isAttach) { + vmMo.attachDisk(new String[] { datastoreVolumePath }, morDs); + } else { + vmMo.removeAllSnapshots(); + vmMo.detachDisk(datastoreVolumePath, false); + } + + return answer; + } catch (Throwable e) { + if (e instanceof RemoteException) { + s_logger.warn("Encounter remote exception to vCenter, invalidate VMware session context"); + this.hostService.invalidateServiceContext(null); + } + + String msg = "AttachVolumeCommand failed due to " + VmwareHelper.getExceptionMessage(e); + s_logger.error(msg, e); + return new AttachAnswer(msg); + } + } + + private static String getSecondaryDatastoreUUID(String storeUrl) { + return UUID.nameUUIDFromBytes(storeUrl.getBytes()).toString(); + } + + public synchronized ManagedObjectReference prepareSecondaryDatastoreOnHost(String storeUrl) throws Exception { + String storeName = getSecondaryDatastoreUUID(storeUrl); + URI uri = new URI(storeUrl); + + VmwareHypervisorHost hyperHost = this.hostService.getHyperHost(this.hostService.getServiceContext(null), null); + ManagedObjectReference morDatastore = hyperHost.mountDatastore(false, uri.getHost(), 0, uri.getPath(), storeName.replace("-", "")); + + if (morDatastore == null) + throw new Exception("Unable to mount secondary storage on host. storeUrl: " + storeUrl); + + return morDatastore; + } + private Answer attachIso(DiskTO disk, boolean isAttach, String vmName) { + + + try { + VmwareHypervisorHost hyperHost = this.hostService.getHyperHost(this.hostService.getServiceContext(null), null); + VirtualMachineMO vmMo = hyperHost.findVmOnHyperHost(vmName); + if (vmMo == null) { + String msg = "Unable to find VM in vSphere to execute AttachIsoCommand, vmName: " + vmName; + s_logger.error(msg); + throw new Exception(msg); + } + TemplateObjectTO iso = (TemplateObjectTO)disk.getData(); + NfsTO nfsImageStore = (NfsTO)iso.getDataStore(); + String storeUrl = nfsImageStore.getUrl(); + if (storeUrl == null) { + if (!iso.getName().equalsIgnoreCase("vmware-tools.iso")) { + String msg = "ISO store root url is not found in AttachIsoCommand"; + s_logger.error(msg); + throw new Exception(msg); + } else { + if (isAttach) { + vmMo.mountToolsInstaller(); + } else { + try{ + vmMo.unmountToolsInstaller(); + }catch(Throwable e){ + vmMo.detachIso(null); + } + } + + return new AttachAnswer(disk); + } + } + + ManagedObjectReference morSecondaryDs = prepareSecondaryDatastoreOnHost(storeUrl); + String isoPath = nfsImageStore.getUrl() + File.separator + iso.getPath(); + if (!isoPath.startsWith(storeUrl)) { + assert (false); + String msg = "ISO path does not start with the secondary storage root"; + s_logger.error(msg); + throw new Exception(msg); + } + + int isoNameStartPos = isoPath.lastIndexOf('/'); + String isoFileName = isoPath.substring(isoNameStartPos + 1); + String isoStorePathFromRoot = isoPath.substring(storeUrl.length(), isoNameStartPos); + + // TODO, check if iso is already attached, or if there is a previous + // attachment + DatastoreMO secondaryDsMo = new DatastoreMO(this.hostService.getServiceContext(null), morSecondaryDs); + String storeName = secondaryDsMo.getName(); + String isoDatastorePath = String.format("[%s] %s%s", storeName, isoStorePathFromRoot, isoFileName); + + if (isAttach) { + vmMo.attachIso(isoDatastorePath, morSecondaryDs, true, false); + } else { + vmMo.detachIso(isoDatastorePath); + } + + return new AttachAnswer(disk); + } catch (Throwable e) { + if (e instanceof RemoteException) { + s_logger.warn("Encounter remote exception to vCenter, invalidate VMware session context"); + this.hostService.invalidateServiceContext(null); + } + + if(isAttach) { + String msg = "AttachIsoCommand(attach) failed due to " + VmwareHelper.getExceptionMessage(e); + s_logger.error(msg, e); + return new AttachAnswer(msg); + } else { + String msg = "AttachIsoCommand(detach) failed due to " + VmwareHelper.getExceptionMessage(e); + s_logger.warn(msg, e); + return new AttachAnswer(msg); + } + } + } + @Override + public Answer dettachIso(DettachCommand cmd) { + return this.attachIso(cmd.getDisk(), false, cmd.getVmName()); + } + + @Override + public Answer dettachVolume(DettachCommand cmd) { + return this.attachVolume(cmd, cmd.getDisk(), false, cmd.getVmName()); + } + + protected VirtualMachineMO prepareVolumeHostDummyVm(VmwareHypervisorHost hyperHost, DatastoreMO dsMo, String vmName) throws Exception { + assert (hyperHost != null); + + VirtualMachineMO vmMo = null; + VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec(); + vmConfig.setName(vmName); + vmConfig.setMemoryMB((long) 4); // vmware request minimum of 4 MB + vmConfig.setNumCPUs(1); + vmConfig.setGuestId(VirtualMachineGuestOsIdentifier.OTHER_GUEST.value()); + VirtualMachineFileInfo fileInfo = new VirtualMachineFileInfo(); + fileInfo.setVmPathName(String.format("[%s]", dsMo.getName())); + vmConfig.setFiles(fileInfo); + + // Scsi controller + VirtualLsiLogicController scsiController = new VirtualLsiLogicController(); + scsiController.setSharedBus(VirtualSCSISharing.NO_SHARING); + scsiController.setBusNumber(0); + scsiController.setKey(1); + VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec(); + scsiControllerSpec.setDevice(scsiController); + scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD); + + vmConfig.getDeviceChange().add(scsiControllerSpec ); + hyperHost.createVm(vmConfig); + vmMo = hyperHost.findVmOnHyperHost(vmName); + return vmMo; + } + @Override + public Answer createVolume(CreateObjectCommand cmd) { + + VolumeObjectTO volume = (VolumeObjectTO)cmd.getData(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volume.getDataStore(); + + try { + VmwareContext context = this.hostService.getServiceContext(null); + VmwareHypervisorHost hyperHost = this.hostService.getHyperHost(context, null); + DatacenterMO dcMo = new DatacenterMO(context, hyperHost.getHyperHostDatacenter()); + + ManagedObjectReference morDatastore = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, primaryStore.getUuid()); + if (morDatastore == null) + throw new Exception("Unable to find datastore in vSphere"); + + DatastoreMO dsMo = new DatastoreMO(context, morDatastore); + // create data volume + VirtualMachineMO vmMo = null; + String volumeUuid = UUID.randomUUID().toString().replace("-", ""); + String volumeDatastorePath = String.format("[%s] %s.vmdk", dsMo.getName(), volumeUuid); + String dummyVmName = this.hostService.getWorkerName(context, cmd, 0); + try { + vmMo = prepareVolumeHostDummyVm(hyperHost, dsMo, dummyVmName); + if (vmMo == null) { + throw new Exception("Unable to create a dummy VM for volume creation"); + } + + synchronized (this) { + // s_logger.info("Delete file if exists in datastore to clear the way for creating the volume. file: " + volumeDatastorePath); + VmwareHelper.deleteVolumeVmdkFiles(dsMo, volumeUuid.toString(), dcMo); + + vmMo.createDisk(volumeDatastorePath, (int) (volume.getSize() / (1024L * 1024L)), morDatastore, vmMo.getScsiDeviceControllerKey()); + vmMo.detachDisk(volumeDatastorePath, false); + } + + VolumeObjectTO newVol = new VolumeObjectTO(); + newVol.setPath(volumeUuid); + return new CreateObjectAnswer(newVol); + } finally { + s_logger.info("Destroy dummy VM after volume creation"); + vmMo.detachAllDisks(); + vmMo.destroy(); + } + + } catch (Throwable e) { + if (e instanceof RemoteException) { + s_logger.warn("Encounter remote exception to vCenter, invalidate VMware session context"); + this.hostService.invalidateServiceContext(null); + } + + String msg = "CreateCommand failed due to " + VmwareHelper.getExceptionMessage(e); + s_logger.error(msg, e); + return new CreateObjectAnswer(e.toString()); + } + } + + @Override + public Answer createSnapshot(CreateObjectCommand cmd) { + // snapshot operation (create or destroy) is handled inside BackupSnapshotCommand(), we just fake + // a success return here + String snapshotUUID = UUID.randomUUID().toString(); + SnapshotObjectTO newSnapshot = new SnapshotObjectTO(); + newSnapshot.setPath(snapshotUUID); + return new CreateObjectAnswer(newSnapshot); + } + + @Override + public Answer deleteVolume(DeleteCommand cmd) { + if (s_logger.isInfoEnabled()) { + s_logger.info("Executing resource DestroyCommand: " + _gson.toJson(cmd)); + } + + /* + * DestroyCommand content example + * + * {"volume": {"id":5,"name":"Volume1", "mountPoint":"/export/home/kelven/vmware-test/primary", + * "path":"6bb8762f-c34c-453c-8e03-26cc246ceec4", "size":0,"type":"DATADISK","resourceType": + * "STORAGE_POOL","storagePoolType":"NetworkFilesystem", "poolId":0,"deviceId":0 } } + * + * {"volume": {"id":1, "name":"i-2-1-KY-ROOT", "mountPoint":"/export/home/kelven/vmware-test/primary", + * "path":"i-2-1-KY-ROOT","size":0,"type":"ROOT", "resourceType":"STORAGE_POOL", "storagePoolType":"NetworkFilesystem", + * "poolId":0,"deviceId":0 } } + */ + + try { + VmwareContext context = this.hostService.getServiceContext(null); + VmwareHypervisorHost hyperHost = this.hostService.getHyperHost(context, null); + VolumeObjectTO vol = (VolumeObjectTO)cmd.getData(); + PrimaryDataStoreTO store = (PrimaryDataStoreTO)vol.getDataStore(); + + ManagedObjectReference morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, store.getUuid()); + if (morDs == null) { + String msg = "Unable to find datastore based on volume mount point " + store.getPath(); + s_logger.error(msg); + throw new Exception(msg); + } + + DatastoreMO dsMo = new DatastoreMO(context, morDs); + + ManagedObjectReference morDc = hyperHost.getHyperHostDatacenter(); + ManagedObjectReference morCluster = hyperHost.getHyperHostCluster(); + ClusterMO clusterMo = new ClusterMO(context, morCluster); + + if (vol.getVolumeType() == Volume.Type.ROOT) { + String vmName = vol.getVmName(); + if (vmName != null) { + VirtualMachineMO vmMo = clusterMo.findVmOnHyperHost(vmName); + if (vmMo != null) { + if (s_logger.isInfoEnabled()) { + s_logger.info("Destroy root volume and VM itself. vmName " + vmName); + } + + HostMO hostMo = vmMo.getRunningHost(); + List networks = vmMo.getNetworksWithDetails(); + + // tear down all devices first before we destroy the VM to avoid accidently delete disk backing files + if (this.resource.getVmState(vmMo) != State.Stopped) + vmMo.safePowerOff(_shutdown_waitMs); + vmMo.tearDownDevices(new Class[] { VirtualDisk.class, VirtualEthernetCard.class }); + vmMo.destroy(); + + for (NetworkDetails netDetails : networks) { + if (netDetails.getGCTag() != null && netDetails.getGCTag().equalsIgnoreCase("true")) { + if (netDetails.getVMMorsOnNetwork() == null || netDetails.getVMMorsOnNetwork().length == 1) { + this.resource.cleanupNetwork(hostMo, netDetails); + } + } + } + } + + if (s_logger.isInfoEnabled()) + s_logger.info("Destroy volume by original name: " + vol.getPath() + ".vmdk"); + dsMo.deleteFile(vol.getPath() + ".vmdk", morDc, true); + + // root volume may be created via linked-clone, delete the delta disk as well + if (_fullCloneFlag) { + if (s_logger.isInfoEnabled()) { + s_logger.info("Destroy volume by derived name: " + vol.getPath() + "-flat.vmdk"); + } + dsMo.deleteFile(vol.getPath() + "-flat.vmdk", morDc, true); + } else { + if (s_logger.isInfoEnabled()) { + s_logger.info("Destroy volume by derived name: " + vol.getPath() + "-delta.vmdk"); + } + dsMo.deleteFile(vol.getPath() + "-delta.vmdk", morDc, true); + } + return new Answer(cmd, true, "Success"); + } + + if (s_logger.isInfoEnabled()) { + s_logger.info("Destroy root volume directly from datastore"); + } + } else { + // evitTemplate will be converted into DestroyCommand, test if we are running in this case + VirtualMachineMO vmMo = clusterMo.findVmOnHyperHost(vol.getPath()); + if (vmMo != null) { + if (s_logger.isInfoEnabled()) + s_logger.info("Destroy template volume " + vol.getPath()); + + vmMo.destroy(); + return new Answer(cmd, true, "Success"); + } + } + + String chainInfo = vol.getChainInfo(); + if (chainInfo != null && !chainInfo.isEmpty()) { + s_logger.info("Destroy volume by chain info: " + chainInfo); + String[] diskChain = _gson.fromJson(chainInfo, String[].class); + + if (diskChain != null && diskChain.length > 0) { + for (String backingName : diskChain) { + if (s_logger.isInfoEnabled()) { + s_logger.info("Delete volume backing file: " + backingName); + } + dsMo.deleteFile(backingName, morDc, true); + } + } else { + if (s_logger.isInfoEnabled()) { + s_logger.info("Empty disk chain info, fall back to try to delete by original backing file name"); + } + dsMo.deleteFile(vol.getPath() + ".vmdk", morDc, true); + + if (s_logger.isInfoEnabled()) { + s_logger.info("Destroy volume by derived name: " + vol.getPath() + "-flat.vmdk"); + } + dsMo.deleteFile(vol.getPath() + "-flat.vmdk", morDc, true); + } + } else { + if (s_logger.isInfoEnabled()) { + s_logger.info("Destroy volume by original name: " + vol.getPath() + ".vmdk"); + } + dsMo.deleteFile(vol.getPath() + ".vmdk", morDc, true); + + if (s_logger.isInfoEnabled()) { + s_logger.info("Destroy volume by derived name: " + vol.getPath() + "-flat.vmdk"); + } + dsMo.deleteFile(vol.getPath() + "-flat.vmdk", morDc, true); + } + + return new Answer(cmd, true, "Success"); + } catch (Throwable e) { + if (e instanceof RemoteException) { + s_logger.warn("Encounter remote exception to vCenter, invalidate VMware session context"); + this.hostService.invalidateServiceContext(null); + } + + String msg = "DestroyCommand failed due to " + VmwareHelper.getExceptionMessage(e); + s_logger.error(msg, e); + return new Answer(cmd, false, msg); + } + } +} diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index c88a7462f8f..701adbe3a52 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -58,6 +58,8 @@ import com.cloud.agent.api.to.*; import com.cloud.network.rules.FirewallRule; import org.apache.cloudstack.storage.command.StorageSubSystemCommand; +import org.apache.cloudstack.storage.to.TemplateObjectTO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.log4j.Logger; import org.apache.xmlrpc.XmlRpcException; import org.w3c.dom.Document; @@ -234,6 +236,8 @@ import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; +import com.cloud.storage.resource.StorageSubsystemCommandHandler; +import com.cloud.storage.resource.StorageSubsystemCommandHandlerBase; import com.cloud.storage.template.TemplateProp; import com.cloud.template.VirtualMachineTemplate.BootloaderType; import com.cloud.utils.NumbersUtil; @@ -334,7 +338,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe protected boolean _canBridgeFirewall = false; protected boolean _isOvs = false; protected List _tmpDom0Vif = new ArrayList(); - protected XenServerStorageResource storageResource; + protected StorageSubsystemCommandHandler storageHandler; protected int _maxNics = 7; public enum SRType { @@ -594,7 +598,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } else if (clazz == CheckS2SVpnConnectionsCommand.class) { return execute((CheckS2SVpnConnectionsCommand) cmd); } else if (cmd instanceof StorageSubSystemCommand) { - return this.storageResource.handleStorageCommands((StorageSubSystemCommand)cmd); + return this.storageHandler.handleStorageCommands((StorageSubSystemCommand)cmd); } else if (clazz == CreateVMSnapshotCommand.class) { return execute((CreateVMSnapshotCommand)cmd); } else if (clazz == DeleteVMSnapshotCommand.class) { @@ -1089,31 +1093,42 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } } - protected VDI mount(Connection conn, String vmName, VolumeTO volume) throws XmlRpcException, XenAPIException { - if (volume.getType() == Volume.Type.ISO) { - String isopath = volume.getPath(); - if (isopath == null) { + protected VDI mount(Connection conn, String vmName, DiskTO volume) throws XmlRpcException, XenAPIException { + DataTO data = volume.getData(); + Volume.Type type = volume.getType(); + if (type == Volume.Type.ISO) { + TemplateObjectTO iso = (TemplateObjectTO)data; + DataStoreTO store = iso.getDataStore(); + + if (store == null) { + //It's a fake iso return null; } - if (isopath.startsWith("xs-tools")) { + + //corer case, xenserver pv driver iso + String templateName = iso.getName(); + if (templateName.startsWith("xs-tools")) { try { - Set vdis = VDI.getByNameLabel(conn, isopath); + Set vdis = VDI.getByNameLabel(conn, templateName); if (vdis.isEmpty()) { - throw new CloudRuntimeException("Could not find ISO with URL: " + isopath); + throw new CloudRuntimeException("Could not find ISO with URL: " + templateName); } return vdis.iterator().next(); - } catch (XenAPIException e) { - throw new CloudRuntimeException("Unable to get pv iso: " + isopath + " due to " + e.toString()); + throw new CloudRuntimeException("Unable to get pv iso: " + templateName + " due to " + e.toString()); } catch (Exception e) { - throw new CloudRuntimeException("Unable to get pv iso: " + isopath + " due to " + e.toString()); + throw new CloudRuntimeException("Unable to get pv iso: " + templateName + " due to " + e.toString()); } } + + if (!(store instanceof NfsTO)) { + throw new CloudRuntimeException("only support mount iso on nfs"); + } + NfsTO nfsStore = (NfsTO)store; + String isoPath = nfsStore.getUrl() + File.separator + iso.getPath(); + int index = isoPath.lastIndexOf("/"); - - int index = isopath.lastIndexOf("/"); - - String mountpoint = isopath.substring(0, index); + String mountpoint = isoPath.substring(0, index); URI uri; try { uri = new URI(mountpoint); @@ -1122,20 +1137,21 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } SR isoSr = createIsoSRbyURI(conn, uri, vmName, false); - String isoname = isopath.substring(index + 1); + String isoname = isoPath.substring(index + 1); VDI isoVdi = getVDIbyLocationandSR(conn, isoname, isoSr); if (isoVdi == null) { - throw new CloudRuntimeException("Unable to find ISO " + volume.getPath()); + throw new CloudRuntimeException("Unable to find ISO " + isoPath); } return isoVdi; } else { - return VDI.getByUuid(conn, volume.getPath()); + VolumeObjectTO vol = (VolumeObjectTO)data; + return VDI.getByUuid(conn,vol.getPath()); } } - protected VBD createVbd(Connection conn, VolumeTO volume, String vmName, VM vm, BootloaderType bootLoaderType) throws XmlRpcException, XenAPIException { + protected VBD createVbd(Connection conn, DiskTO volume, String vmName, VM vm, BootloaderType bootLoaderType) throws XmlRpcException, XenAPIException { Volume.Type type = volume.getType(); VDI vdi = mount(conn, vmName, volume); @@ -1153,7 +1169,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe vbdr.bootable = true; } - vbdr.userdevice = Long.toString(volume.getDeviceId()); + vbdr.userdevice = Long.toString(volume.getDiskSeq()); if (volume.getType() == Volume.Type.ISO) { vbdr.mode = Types.VbdMode.RO; vbdr.type = Types.VbdType.CD; @@ -1246,12 +1262,17 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe if (!(guestOsTypeName.startsWith("Windows") || guestOsTypeName.startsWith("Citrix") || guestOsTypeName.startsWith("Other"))) { if (vmSpec.getBootloader() == BootloaderType.CD) { - VolumeTO [] disks = vmSpec.getDisks(); - for (VolumeTO disk : disks) { - if (disk.getType() == Volume.Type.ISO && disk.getOsType() != null) { - String isoGuestOsName = getGuestOsType(disk.getOsType(), vmSpec.getBootloader() == BootloaderType.CD); - if (!isoGuestOsName.equals(guestOsTypeName)) { - vmSpec.setBootloader(BootloaderType.PyGrub); + DiskTO [] disks = vmSpec.getDisks(); + for (DiskTO disk : disks) { + Volume.Type type = disk.getType(); + if (type == Volume.Type.ISO) { + TemplateObjectTO tmpl = (TemplateObjectTO)disk.getData(); + String osType = tmpl.getGuestOsType(); + if (tmpl.getFormat() == ImageFormat.ISO && osType != null ) { + String isoGuestOsName = getGuestOsType(osType, vmSpec.getBootloader() == BootloaderType.CD); + if (!isoGuestOsName.equals(guestOsTypeName)) { + vmSpec.setBootloader(BootloaderType.PyGrub); + } } } } @@ -1496,7 +1517,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe Host host = Host.getByUuid(conn, _host.uuid); vm = createVmFromTemplate(conn, vmSpec, host); - for (VolumeTO disk : vmSpec.getDisks()) { + for (DiskTO disk : vmSpec.getDisks()) { createVbd(conn, disk, vmName, vm, vmSpec.getBootloader()); } @@ -5774,13 +5795,14 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe CheckXenHostInfo(); - this.storageResource = getStorageResource(); + this.storageHandler = getStorageHandler(); return true; } - protected XenServerStorageResource getStorageResource() { - return new XenServerStorageResource(this); + protected StorageSubsystemCommandHandler getStorageHandler() { + XenServerStorageProcessor processor = new XenServerStorageProcessor(this); + return new StorageSubsystemCommandHandlerBase(processor); } private void CheckXenHostInfo() throws ConfigurationException { diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java index d64e17337a1..6ce3837b89c 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java @@ -26,11 +26,14 @@ import java.util.Map; import java.util.Set; import javax.ejb.Local; + +import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.log4j.Logger; import org.apache.xmlrpc.XmlRpcException; import com.cloud.agent.api.FenceAnswer; import com.cloud.agent.api.FenceCommand; +import com.cloud.agent.api.to.DiskTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VolumeTO; import com.cloud.resource.ServerResource; @@ -194,13 +197,17 @@ public class XenServer56FP1Resource extends XenServer56Resource { if (!(guestOsTypeName.startsWith("Windows") || guestOsTypeName.startsWith("Citrix") || guestOsTypeName.startsWith("Other"))) { if (vmSpec.getBootloader() == BootloaderType.CD) { - VolumeTO[] disks = vmSpec.getDisks(); - for (VolumeTO disk : disks) { - if (disk.getType() == Volume.Type.ISO && disk.getOsType() != null) { - String isoGuestOsName = getGuestOsType(disk.getOsType(), vmSpec.getBootloader() == BootloaderType.CD); - if (!isoGuestOsName.equals(guestOsTypeName)) { - vmSpec.setBootloader(BootloaderType.PyGrub); - } + DiskTO[] disks = vmSpec.getDisks(); + for (DiskTO disk : disks) { + if (disk.getType() == Volume.Type.ISO ) { + TemplateObjectTO iso = (TemplateObjectTO)disk.getData(); + String osType = iso.getGuestOsType(); + if (osType != null) { + String isoGuestOsName = getGuestOsType(osType, vmSpec.getBootloader() == BootloaderType.CD); + if (!isoGuestOsName.equals(guestOsTypeName)) { + vmSpec.setBootloader(BootloaderType.PyGrub); + } + } } } } diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageResource.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java similarity index 77% rename from plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageResource.java rename to plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java index 8b27efec8c0..727c3f4b082 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageResource.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java @@ -22,13 +22,9 @@ import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; -import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.URI; -import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -38,89 +34,315 @@ import java.util.Map; import java.util.Set; import java.util.UUID; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; +import org.apache.cloudstack.storage.command.AttachAnswer; +import org.apache.cloudstack.storage.command.AttachCommand; import org.apache.cloudstack.storage.command.AttachPrimaryDataStoreAnswer; import org.apache.cloudstack.storage.command.AttachPrimaryDataStoreCmd; import org.apache.cloudstack.storage.command.CopyCmdAnswer; import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.command.CreateObjectAnswer; import org.apache.cloudstack.storage.command.CreateObjectCommand; -import org.apache.cloudstack.storage.command.CreatePrimaryDataStoreCmd; import org.apache.cloudstack.storage.command.DeleteCommand; -import org.apache.cloudstack.storage.command.StorageSubSystemCommand; +import org.apache.cloudstack.storage.command.DettachAnswer; +import org.apache.cloudstack.storage.command.DettachCommand; import org.apache.cloudstack.storage.datastore.protocol.DataStoreProtocol; -import org.apache.cloudstack.storage.to.ImageStoreTO; import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; import org.apache.cloudstack.storage.to.SnapshotObjectTO; import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.cloudstack.storage.to.VolumeObjectTO; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.DefaultHttpClient; import org.apache.log4j.Logger; import org.apache.xmlrpc.XmlRpcException; import com.cloud.agent.api.Answer; -import com.cloud.agent.api.BackupSnapshotAnswer; -import com.cloud.agent.api.Command; -import com.cloud.agent.api.ManageSnapshotAnswer; -import com.cloud.agent.api.ManageSnapshotCommand; -import com.cloud.agent.api.storage.CopyVolumeAnswer; -import com.cloud.agent.api.storage.CreateAnswer; -import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; -import com.cloud.agent.api.storage.DeleteVolumeCommand; -import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; +import com.cloud.agent.api.CreateStoragePoolCommand; +import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; +import com.cloud.agent.api.to.DiskTO; import com.cloud.agent.api.to.NfsTO; import com.cloud.agent.api.to.S3TO; import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.agent.api.to.SwiftTO; -import com.cloud.agent.api.to.VolumeTO; import com.cloud.exception.InternalErrorException; import com.cloud.hypervisor.xen.resource.CitrixResourceBase.SRType; -import com.cloud.storage.DataStoreRole; import com.cloud.storage.Storage.ImageFormat; +import com.cloud.storage.Storage.StoragePoolType; +import com.cloud.storage.resource.StorageProcessor; import com.cloud.utils.S3Utils; import com.cloud.utils.StringUtils; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.storage.encoding.DecodedDataObject; import com.cloud.utils.storage.encoding.DecodedDataStore; import com.cloud.utils.storage.encoding.Decoder; -import com.cloud.vm.DiskProfile; import com.xensource.xenapi.Connection; import com.xensource.xenapi.Host; import com.xensource.xenapi.PBD; +import com.xensource.xenapi.Pool; import com.xensource.xenapi.SR; import com.xensource.xenapi.Types; import com.xensource.xenapi.Types.BadServerResponse; import com.xensource.xenapi.Types.XenAPIException; +import com.xensource.xenapi.VBD; import com.xensource.xenapi.VDI; +import com.xensource.xenapi.VM; +import com.xensource.xenapi.VMGuestMetrics; -public class XenServerStorageResource { - private static final Logger s_logger = Logger.getLogger(XenServerStorageResource.class); +public class XenServerStorageProcessor implements StorageProcessor { + private static final Logger s_logger = Logger.getLogger(XenServerStorageProcessor.class); protected CitrixResourceBase hypervisorResource; - public XenServerStorageResource(CitrixResourceBase resource) { + public XenServerStorageProcessor(CitrixResourceBase resource) { this.hypervisorResource = resource; } + + @Override + public AttachAnswer attachIso(AttachCommand cmd) { + DiskTO disk = cmd.getDisk(); + DataTO data = disk.getData(); + DataStoreTO store = data.getDataStore(); - public Answer handleStorageCommands(StorageSubSystemCommand command) { - if (command instanceof CopyCommand) { - return this.execute((CopyCommand)command); - } else if (command instanceof AttachPrimaryDataStoreCmd) { - return this.execute((AttachPrimaryDataStoreCmd)command); - } else if (command instanceof CreatePrimaryDataStoreCmd) { - return execute((CreatePrimaryDataStoreCmd) command); - } else if (command instanceof CreateObjectCommand) { - return execute((CreateObjectCommand) command); - } else if (command instanceof DeleteCommand) { - return execute((DeleteCommand)command); + if (!(store instanceof NfsTO)) { + s_logger.debug("Can't attach a iso which is not created on nfs: "); + return new AttachAnswer("Can't attach a iso which is not created on nfs: "); + } + NfsTO nfsStore = (NfsTO)store; + String isoURL = nfsStore.getUrl() + File.separator + data.getPath(); + + String vmName = cmd.getVmName(); + try { + Connection conn = this.hypervisorResource.getConnection(); + + VBD isoVBD = null; + + // Find the VM + VM vm = this.hypervisorResource.getVM(conn, vmName); + // Find the ISO VDI + VDI isoVDI = this.hypervisorResource.getIsoVDIByURL(conn, vmName, isoURL); + + // Find the VM's CD-ROM VBD + Set vbds = vm.getVBDs(conn); + for (VBD vbd : vbds) { + String userDevice = vbd.getUserdevice(conn); + Types.VbdType type = vbd.getType(conn); + + if (userDevice.equals("3") && type == Types.VbdType.CD) { + isoVBD = vbd; + break; + } + } + + if (isoVBD == null) { + throw new CloudRuntimeException("Unable to find CD-ROM VBD for VM: " + vmName); + } else { + // If an ISO is already inserted, eject it + if (isoVBD.getEmpty(conn) == false) { + isoVBD.eject(conn); + } + + // Insert the new ISO + isoVBD.insert(conn, isoVDI); + } + + return new AttachAnswer(disk); + + } catch (XenAPIException e) { + s_logger.warn("Failed to attach iso" + ": " + e.toString(), e); + return new AttachAnswer(e.toString()); + } catch (Exception e) { + s_logger.warn("Failed to attach iso" + ": " + e.toString(), e); + return new AttachAnswer(e.toString()); } - return new Answer((Command)command, false, "not implemented yet"); } + + @Override + public AttachAnswer attachVolume(AttachCommand cmd) { + String vmName = cmd.getVmName(); + DiskTO disk = cmd.getDisk(); + DataTO data = disk.getData(); + + try { + Connection conn = this.hypervisorResource.getConnection(); + // Look up the VDI + VDI vdi = this.hypervisorResource.mount(conn, null, null, data.getPath()); + // Look up the VM + VM vm = this.hypervisorResource.getVM(conn, vmName); + /* For HVM guest, if no pv driver installed, no attach/detach */ + boolean isHVM; + if (vm.getPVBootloader(conn).equalsIgnoreCase("")) { + isHVM = true; + } else { + isHVM = false; + } + VMGuestMetrics vgm = vm.getGuestMetrics(conn); + boolean pvDrvInstalled = false; + if (!this.hypervisorResource.isRefNull(vgm) && vgm.getPVDriversUpToDate(conn)) { + pvDrvInstalled = true; + } + if (isHVM && !pvDrvInstalled) { + s_logger.warn(": You attempted an operation on a VM which requires PV drivers to be installed but the drivers were not detected"); + return new AttachAnswer("You attempted an operation that requires PV drivers to be installed on the VM. Please install them by inserting xen-pv-drv.iso."); + } + + // Figure out the disk number to attach the VM to + String diskNumber = null; + Long deviceId = disk.getDiskSeq(); + if( deviceId != null ) { + if( deviceId.longValue() == 3 ) { + String msg = "Device 3 is reserved for CD-ROM, choose other device"; + return new AttachAnswer(msg); + } + if(this.hypervisorResource.isDeviceUsed(conn, vm, deviceId)) { + String msg = "Device " + deviceId + " is used in VM " + vmName; + return new AttachAnswer(msg); + } + diskNumber = deviceId.toString(); + } else { + diskNumber = this.hypervisorResource.getUnusedDeviceNum(conn, vm); + } + // Create a new VBD + VBD.Record vbdr = new VBD.Record(); + vbdr.VM = vm; + vbdr.VDI = vdi; + vbdr.bootable = false; + vbdr.userdevice = diskNumber; + vbdr.mode = Types.VbdMode.RW; + vbdr.type = Types.VbdType.DISK; + vbdr.unpluggable = true; + VBD vbd = VBD.create(conn, vbdr); + + // Attach the VBD to the VM + vbd.plug(conn); + + // Update the VDI's label to include the VM name + vdi.setNameLabel(conn, vmName + "-DATA"); + DiskTO newDisk = new DiskTO(disk.getData(), Long.parseLong(diskNumber), disk.getType()); + return new AttachAnswer(newDisk); + + } catch (XenAPIException e) { + String msg = "Failed to attach volume" + " for uuid: " + data.getPath() + " due to " + e.toString(); + s_logger.warn(msg, e); + return new AttachAnswer(msg); + } catch (Exception e) { + String msg = "Failed to attach volume" + " for uuid: " + data.getPath() + " due to " + e.getMessage(); + s_logger.warn(msg, e); + return new AttachAnswer(msg); + } + } + + @Override + public Answer dettachIso(DettachCommand cmd) { + DiskTO disk = cmd.getDisk(); + DataTO data = disk.getData(); + DataStoreTO store = data.getDataStore(); + + if (!(store instanceof NfsTO)) { + s_logger.debug("Can't attach a iso which is not created on nfs: "); + return new DettachAnswer("Can't attach a iso which is not created on nfs: "); + } + NfsTO nfsStore = (NfsTO)store; + String isoURL = nfsStore.getUrl() + File.separator + data.getPath(); + + try { + Connection conn = this.hypervisorResource.getConnection(); + // Find the VM + VM vm = this.hypervisorResource.getVM(conn, cmd.getVmName()); + String vmUUID = vm.getUuid(conn); + + // Find the ISO VDI + VDI isoVDI = this.hypervisorResource.getIsoVDIByURL(conn, cmd.getVmName(), isoURL); + + SR sr = isoVDI.getSR(conn); + + // Look up all VBDs for this VDI + Set vbds = isoVDI.getVBDs(conn); + + // Iterate through VBDs, and if the VBD belongs the VM, eject + // the ISO from it + for (VBD vbd : vbds) { + VM vbdVM = vbd.getVM(conn); + String vbdVmUUID = vbdVM.getUuid(conn); + + if (vbdVmUUID.equals(vmUUID)) { + // If an ISO is already inserted, eject it + if (!vbd.getEmpty(conn)) { + vbd.eject(conn); + } + break; + } + } + + if (!sr.getNameLabel(conn).startsWith("XenServer Tools")) { + this.hypervisorResource.removeSR(conn, sr); + } + + return new DettachAnswer(disk); + } catch (XenAPIException e) { + String msg = "Failed to dettach volume" + " for uuid: " + data.getPath() + " due to " + e.toString(); + s_logger.warn(msg, e); + return new DettachAnswer(msg); + } catch (Exception e) { + String msg = "Failed to dettach volume" + " for uuid: " + data.getPath() + " due to " + e.getMessage(); + s_logger.warn(msg, e); + return new DettachAnswer(msg); + } + } + + + @Override + public Answer dettachVolume(DettachCommand cmd) { + String vmName = cmd.getVmName(); + DiskTO disk = cmd.getDisk(); + DataTO data = disk.getData(); + try { + Connection conn = this.hypervisorResource.getConnection(); + // Look up the VDI + VDI vdi = this.hypervisorResource.mount(conn, null, null, data.getPath()); + // Look up the VM + VM vm = this.hypervisorResource.getVM(conn, vmName); + /* For HVM guest, if no pv driver installed, no attach/detach */ + boolean isHVM; + if (vm.getPVBootloader(conn).equalsIgnoreCase("")) { + isHVM = true; + } else { + isHVM = false; + } + VMGuestMetrics vgm = vm.getGuestMetrics(conn); + boolean pvDrvInstalled = false; + if (!this.hypervisorResource.isRefNull(vgm) && vgm.getPVDriversUpToDate(conn)) { + pvDrvInstalled = true; + } + if (isHVM && !pvDrvInstalled) { + s_logger.warn(": You attempted an operation on a VM which requires PV drivers to be installed but the drivers were not detected"); + return new DettachAnswer("You attempted an operation that requires PV drivers to be installed on the VM. Please install them by inserting xen-pv-drv.iso."); + } + + + // Look up all VBDs for this VDI + Set vbds = vdi.getVBDs(conn); + + // Detach each VBD from its VM, and then destroy it + for (VBD vbd : vbds) { + VBD.Record vbdr = vbd.getRecord(conn); + + if (vbdr.currentlyAttached) { + vbd.unplug(conn); + } + + vbd.destroy(conn); + } + + // Update the VDI's label to be "detached" + vdi.setNameLabel(conn, "detached"); + + this.hypervisorResource.umount(conn, vdi); + + return new DettachAnswer(disk); + } catch(Exception e) { + s_logger.warn("Failed dettach volume: " + data.getPath()); + return new DettachAnswer("Failed dettach volume: " + data.getPath() + ", due to " + e.toString()); + } + } + protected SR getSRByNameLabel(Connection conn, String nameLabel) throws BadServerResponse, XenAPIException, XmlRpcException { Set srs = SR.getByNameLabel(conn, nameLabel); @@ -146,26 +368,10 @@ public class XenServerStorageResource { vdi.destroy(conn); } - private Map getParameters(URI uri) { - String parameters = uri.getQuery(); - Map params = new HashMap(); - List paraLists = Arrays.asList(parameters.split("&")); - for (String para : paraLists) { - String[] pair = para.split("="); - params.put(pair[0], pair[1]); - } - return params; - } - - protected CreateObjectAnswer getTemplateSize(CreateObjectCommand cmd, String templateUrl) { - /*Connection conn = hypervisorResource.getConnection(); - long size = this.getTemplateSize(conn, templateUrl); - return new CreateObjectAnswer(cmd, templateUrl, size);*/ - return null; - } - - protected CreateObjectAnswer createSnapshot(SnapshotObjectTO snapshotTO) { + @Override + public Answer createSnapshot(CreateObjectCommand cmd) { Connection conn = hypervisorResource.getConnection(); + SnapshotObjectTO snapshotTO = (SnapshotObjectTO)cmd.getData(); long snapshotId = snapshotTO.getId(); String snapshotName = snapshotTO.getName(); String details = "create snapshot operation Failed for snapshotId: " + snapshotId; @@ -211,22 +417,10 @@ public class XenServerStorageResource { return new CreateObjectAnswer(details); } - protected CreateObjectAnswer execute(CreateObjectCommand cmd) { - DataTO data = cmd.getData(); - try { - if (data.getObjectType() == DataObjectType.VOLUME) { - return createVolume(data); - } else if (data.getObjectType() == DataObjectType.SNAPSHOT) { - return createSnapshot((SnapshotObjectTO)data); - } - return new CreateObjectAnswer("not supported type"); - } catch (Exception e) { - s_logger.debug("Failed to create object: " + data.getObjectType() + ": " + e.toString()); - return new CreateObjectAnswer(e.toString()); - } - } - protected Answer deleteVolume(VolumeObjectTO volume) { + @Override + public Answer deleteVolume(DeleteCommand cmd) { + DataTO volume = cmd.getData(); Connection conn = hypervisorResource.getConnection(); String errorMsg = null; try { @@ -246,46 +440,12 @@ public class XenServerStorageResource { return new Answer(null, false, errorMsg); } - protected Answer execute(DeleteCommand cmd) { - DataTO data = cmd.getData(); - Answer answer = null; - if (data.getObjectType() == DataObjectType.VOLUME) { - answer = deleteVolume((VolumeObjectTO)data); - } else { - answer = new Answer(cmd, false, "unsupported type"); - } - - return answer; - } - - /* protected Answer execute(CreateVolumeFromBaseImageCommand cmd) { - VolumeObjectTO volume = cmd.getVolume(); - ImageOnPrimayDataStoreTO baseImage = cmd.getImage(); - Connection conn = hypervisorResource.getConnection(); - - try { - VDI baseVdi = VDI.getByUuid(conn, baseImage.getPathOnPrimaryDataStore()); - VDI newVol = baseVdi.createClone(conn, new HashMap()); - newVol.setNameLabel(conn, volume.getName()); - return new CreateObjectAnswer(cmd, newVol.getUuid(conn), newVol.getVirtualSize(conn)); - } catch (BadServerResponse e) { - return new Answer(cmd, false, e.toString()); - } catch (XenAPIException e) { - return new Answer(cmd, false, e.toString()); - } catch (XmlRpcException e) { - return new Answer(cmd, false, e.toString()); - } - }*/ - - protected SR getNfsSR(Connection conn, DecodedDataStore store) { + protected SR getNfsSR(Connection conn, StorageFilerTO pool) { Map deviceConfig = new HashMap(); - - String uuid = store.getUuid(); try { - String server = store.getServer(); - String serverpath = store.getPath(); - + String server = pool.getHost(); + String serverpath = pool.getPath(); serverpath = serverpath.replace("//", "/"); Set srs = SR.getAll(conn); for (SR sr : srs) { @@ -316,25 +476,25 @@ public class XenServerStorageResource { if (server.equals(dc.get("server")) && serverpath.equals(dc.get("serverpath"))) { throw new CloudRuntimeException("There is a SR using the same configuration server:" + dc.get("server") + ", serverpath:" - + dc.get("serverpath") + " for pool " + uuid + "on host:" + hypervisorResource.getHost().uuid); + + dc.get("serverpath") + " for pool " + pool.getUuid() + "on host:" + hypervisorResource.getHost().uuid); } } deviceConfig.put("server", server); deviceConfig.put("serverpath", serverpath); Host host = Host.getByUuid(conn, hypervisorResource.getHost().uuid); - SR sr = SR.create(conn, host, deviceConfig, new Long(0), uuid, uuid, SRType.NFS.toString(), "user", true, + SR sr = SR.create(conn, host, deviceConfig, new Long(0), pool.getUuid(), Long.toString(pool.getId()), SRType.NFS.toString(), "user", true, new HashMap()); sr.scan(conn); return sr; } catch (XenAPIException e) { - throw new CloudRuntimeException("Unable to create NFS SR " + uuid, e); + throw new CloudRuntimeException("Unable to create NFS SR " + pool.toString(), e); } catch (XmlRpcException e) { - throw new CloudRuntimeException("Unable to create NFS SR " + uuid, e); + throw new CloudRuntimeException("Unable to create NFS SR " + pool.toString(), e); } } - /* - protected SR getIscsiSR(Connection conn, PrimaryDataStoreTO pool) { + + protected SR getIscsiSR(Connection conn, StorageFilerTO pool) { synchronized (pool.getUuid().intern()) { Map deviceConfig = new HashMap(); try { @@ -379,13 +539,13 @@ public class XenServerStorageResource { } if (target.equals(dc.get("target")) && targetiqn.equals(dc.get("targetIQN")) && lunid.equals(dc.get("lunid"))) { throw new CloudRuntimeException("There is a SR using the same configuration target:" + dc.get("target") + ", targetIQN:" - + dc.get("targetIQN") + ", lunid:" + dc.get("lunid") + " for pool " + pool.getUuid() + "on host:" + _host.uuid); + + dc.get("targetIQN") + ", lunid:" + dc.get("lunid") + " for pool " + pool.getUuid() + "on host:" + this.hypervisorResource.getHost().uuid); } } deviceConfig.put("target", target); deviceConfig.put("targetIQN", targetiqn); - Host host = Host.getByUuid(conn, _host.uuid); + Host host = Host.getByUuid(conn, this.hypervisorResource.getHost().uuid); Map smConfig = new HashMap(); String type = SRType.LVMOISCSI.toString(); String poolId = Long.toString(pool.getId()); @@ -456,99 +616,24 @@ public class XenServerStorageResource { throw new CloudRuntimeException(msg, e); } } - }*/ - - protected Answer execute(CreatePrimaryDataStoreCmd cmd) { - Connection conn = hypervisorResource.getConnection(); - String storeUrl = cmd.getDataStore(); - + } + protected Answer execute(CreateStoragePoolCommand cmd) { + Connection conn = this.hypervisorResource.getConnection(); + StorageFilerTO pool = cmd.getPool(); try { - DecodedDataObject obj = Decoder.decode(storeUrl); - DecodedDataStore store = obj.getStore(); - - if (store.getScheme().equalsIgnoreCase("nfs")) { - SR sr = getNfsSR(conn, store); - } else if (store.getScheme().equalsIgnoreCase("iscsi")) { - //getIscsiSR(conn, dataStore); - } else if (store.getScheme().equalsIgnoreCase("presetup")) { + if (pool.getType() == StoragePoolType.NetworkFilesystem) { + getNfsSR(conn, pool); + } else if (pool.getType() == StoragePoolType.IscsiLUN) { + getIscsiSR(conn, pool); + } else if (pool.getType() == StoragePoolType.PreSetup) { } else { - return new Answer(cmd, false, "The pool type: " + store.getScheme() + " is not supported."); + return new Answer(cmd, false, "The pool type: " + pool.getType().name() + " is not supported."); } return new Answer(cmd, true, "success"); } catch (Exception e) { - // String msg = "Catch Exception " + e.getClass().getName() + ", create StoragePool failed due to " + e.toString() + " on host:" + _host.uuid + " pool: " + pool.getHost() + pool.getPath(); - //s_logger.warn(msg, e); - return new Answer(cmd, false, null); - } - } - - private long getTemplateSize(Connection conn, String url) { - String size = hypervisorResource.callHostPlugin(conn, "storagePlugin", "getTemplateSize", "srcUrl", url); - if (size.equalsIgnoreCase("") || size == null) { - throw new CloudRuntimeException("Can't get template size"); - } - - try { - return Long.parseLong(size); - } catch (NumberFormatException e) { - throw new CloudRuntimeException("Failed to get template lenght", e); - } - - /* - HttpHead method = new HttpHead(url); - DefaultHttpClient client = new DefaultHttpClient(); - try { - HttpResponse response = client.execute(method); - Header header = response.getFirstHeader("Content-Length"); - if (header == null) { - throw new CloudRuntimeException("Can't get content-lenght header from :" + url); - } - Long length = Long.parseLong(header.getValue()); - return length; - } catch (HttpException e) { - throw new CloudRuntimeException("Failed to get template lenght", e); - } catch (IOException e) { - throw new CloudRuntimeException("Failed to get template lenght", e); - } catch (NumberFormatException e) { - throw new CloudRuntimeException("Failed to get template lenght", e); - }*/ - } - - private void downloadHttpToLocalFile(String destFilePath, String url) { - File destFile = new File(destFilePath); - if (!destFile.exists()) { - throw new CloudRuntimeException("dest file doesn't exist: " + destFilePath); - } - - DefaultHttpClient client = new DefaultHttpClient(); - HttpGet getMethod = new HttpGet(url); - HttpResponse response; - BufferedOutputStream output = null; - long length = 0; - try { - response = client.execute(getMethod); - HttpEntity entity = response.getEntity(); - length = entity.getContentLength(); - output = new BufferedOutputStream(new FileOutputStream(destFile)); - entity.writeTo(output); - } catch (ClientProtocolException e) { - throw new CloudRuntimeException("Failed to download template", e); - } catch (IOException e) { - throw new CloudRuntimeException("Failed to download template", e); - } finally { - if (output != null) { - try { - output.close(); - } catch (IOException e) { - throw new CloudRuntimeException("Failed to download template", e); - } - } - } - - //double check the length - destFile = new File(destFilePath); - if (destFile.length() != length) { - throw new CloudRuntimeException("Download file length doesn't match: expected: " + length + ", actual: " + destFile.length()); + String msg = "Catch Exception " + e.getClass().getName() + ", create StoragePool failed due to " + e.toString() + " on host:" + this.hypervisorResource.getHost().uuid + " pool: " + pool.getHost() + pool.getPath(); + s_logger.warn(msg, e); + return new Answer(cmd, false, msg); } } @@ -721,7 +806,11 @@ public class XenServerStorageResource { return parentUuid; } - protected CopyCmdAnswer copyTemplateToPrimaryStorage(DataTO srcData, DataTO destData, int wait) { + @Override + public Answer copyTemplateToPrimaryStorage(CopyCommand cmd) { + DataTO srcData = cmd.getSrcTO(); + DataTO destData = cmd.getDestTO(); + int wait = cmd.getWait(); DataStoreTO srcStore = srcData.getDataStore(); try { if ((srcStore instanceof NfsTO) && (srcData.getObjectType() == DataObjectType.TEMPLATE)) { @@ -774,32 +863,42 @@ public class XenServerStorageResource { return new CopyCmdAnswer("not implemented yet"); } - protected CreateObjectAnswer createVolume(DataTO data) throws BadServerResponse, XenAPIException, XmlRpcException { - VolumeObjectTO volume = (VolumeObjectTO)data; + @Override + public Answer createVolume(CreateObjectCommand cmd) { + DataTO data = cmd.getData(); + VolumeObjectTO volume = (VolumeObjectTO)data; - Connection conn = hypervisorResource.getConnection(); - PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)data.getDataStore(); - SR poolSr = hypervisorResource.getStorageRepository(conn, primaryStore.getUuid()); - VDI.Record vdir = new VDI.Record(); - vdir.nameLabel = volume.getName(); - vdir.SR = poolSr; - vdir.type = Types.VdiType.USER; + try { + Connection conn = hypervisorResource.getConnection(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)data.getDataStore(); + SR poolSr = hypervisorResource.getStorageRepository(conn, primaryStore.getUuid()); + VDI.Record vdir = new VDI.Record(); + vdir.nameLabel = volume.getName(); + vdir.SR = poolSr; + vdir.type = Types.VdiType.USER; - vdir.virtualSize = volume.getSize(); - VDI vdi; + vdir.virtualSize = volume.getSize(); + VDI vdi; - vdi = VDI.create(conn, vdir); - vdir = vdi.getRecord(conn); - VolumeObjectTO newVol = new VolumeObjectTO(); - newVol.setName(vdir.nameLabel); - newVol.setSize(vdir.virtualSize); - newVol.setPath(vdir.uuid); + vdi = VDI.create(conn, vdir); + vdir = vdi.getRecord(conn); + VolumeObjectTO newVol = new VolumeObjectTO(); + newVol.setName(vdir.nameLabel); + newVol.setSize(vdir.virtualSize); + newVol.setPath(vdir.uuid); - return new CreateObjectAnswer(newVol); + return new CreateObjectAnswer(newVol); + } catch (Exception e) { + s_logger.debug("create volume failed: " + e.toString()); + return new CreateObjectAnswer(e.toString()); + } } - protected CopyCmdAnswer cloneVolumeFromBaseTemplate(DataTO srcData, DataTO destData) { + @Override + public Answer cloneVolumeFromBaseTemplate(CopyCommand cmd) { Connection conn = hypervisorResource.getConnection(); + DataTO srcData = cmd.getSrcTO(); + DataTO destData = cmd.getDestTO(); PrimaryDataStoreTO pool = (PrimaryDataStoreTO)destData.getDataStore(); VolumeObjectTO volume = (VolumeObjectTO)destData; VDI vdi = null; @@ -827,9 +926,12 @@ public class XenServerStorageResource { } } - - protected Answer copyVolumeFromImageCacheToPrimary(DataTO srcData, DataTO destData, int wait) { + @Override + public Answer copyVolumeFromImageCacheToPrimary(CopyCommand cmd) { Connection conn = hypervisorResource.getConnection(); + DataTO srcData = cmd.getSrcTO(); + DataTO destData = cmd.getDestTO(); + int wait = cmd.getWait(); VolumeObjectTO srcVolume = (VolumeObjectTO)srcData; VolumeObjectTO destVolume = (VolumeObjectTO)destData; PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)destVolume.getDataStore(); @@ -858,10 +960,12 @@ public class XenServerStorageResource { return new CopyCmdAnswer("unsupported protocol"); } - protected Answer copyVolumeFromPrimaryToSecondary(DataTO srcData, DataTO destData, int wait) { + @Override + public Answer copyVolumeFromPrimaryToSecondary(CopyCommand cmd) { Connection conn = hypervisorResource.getConnection(); - VolumeObjectTO srcVolume = (VolumeObjectTO)srcData; - VolumeObjectTO destVolume = (VolumeObjectTO)destData; + VolumeObjectTO srcVolume = (VolumeObjectTO)cmd.getSrcTO(); + VolumeObjectTO destVolume = (VolumeObjectTO)cmd.getDestTO(); + int wait = cmd.getWait(); DataStoreTO destStore = destVolume.getDataStore(); if (destStore instanceof NfsTO) { @@ -1088,8 +1192,13 @@ public class XenServerStorageResource { return false; } - protected Answer backupSnasphot(DataTO srcData, DataTO destData, DataTO cacheData, int wait) { + @Override + public Answer backupSnasphot(CopyCommand cmd) { Connection conn = hypervisorResource.getConnection(); + DataTO srcData = cmd.getSrcTO(); + DataTO cacheData = cmd.getCacheTO(); + DataTO destData = cmd.getDestTO(); + int wait = cmd.getWait(); PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)srcData.getDataStore(); String primaryStorageNameLabel = primaryStore.getUuid(); String secondaryStorageUrl = null; @@ -1220,11 +1329,13 @@ public class XenServerStorageResource { return new CopyCmdAnswer(details); } - protected CopyCmdAnswer createTemplateFromVolume(DataTO srcData, DataTO destData, int wait) { + @Override + public Answer createTemplateFromVolume(CopyCommand cmd) { Connection conn = this.hypervisorResource.getConnection(); - VolumeObjectTO volume = (VolumeObjectTO)srcData; - TemplateObjectTO template = (TemplateObjectTO)destData; - NfsTO destStore = (NfsTO)destData.getDataStore(); + VolumeObjectTO volume = (VolumeObjectTO)cmd.getSrcTO(); + TemplateObjectTO template = (TemplateObjectTO)cmd.getDestTO(); + NfsTO destStore = (NfsTO)cmd.getDestTO().getDataStore(); + int wait = cmd.getWait(); String secondaryStoragePoolURL = destStore.getUrl(); String volumeUUID = volume.getPath(); @@ -1290,38 +1401,4 @@ public class XenServerStorageResource { } return new CopyCmdAnswer(details); } - - protected Answer execute(CopyCommand cmd) { - DataTO srcData = cmd.getSrcTO(); - DataTO destData = cmd.getDestTO(); - DataStoreTO srcDataStore = srcData.getDataStore(); - DataStoreTO destDataStore = destData.getDataStore(); - DataObjectType srcType = srcData.getObjectType(); - DataObjectType destType = destData.getObjectType(); - DataStoreRole destRole = destDataStore.getRole(); - - boolean nfs = (srcDataStore instanceof NfsTO) ? true : false; - - if ((srcData.getObjectType() == DataObjectType.TEMPLATE) && (srcDataStore instanceof NfsTO) && (destData.getDataStore().getRole() == DataStoreRole.Primary)) { - //copy template to primary storage - return copyTemplateToPrimaryStorage(srcData, destData, cmd.getWait()); - } else if (srcData.getObjectType() == DataObjectType.TEMPLATE && srcDataStore.getRole() == DataStoreRole.Primary && destDataStore.getRole() == DataStoreRole.Primary) { - //clone template to a volume - return cloneVolumeFromBaseTemplate(srcData, destData); - } else if (srcData.getObjectType() == DataObjectType.VOLUME && (srcData.getDataStore().getRole() == DataStoreRole.ImageCache || srcDataStore.getRole() == DataStoreRole.Image)) { - //copy volume from image cache to primary - return copyVolumeFromImageCacheToPrimary(srcData, destData, cmd.getWait()); - } else if (srcData.getObjectType() == DataObjectType.VOLUME && srcData.getDataStore().getRole() == DataStoreRole.Primary) { - if (destData.getObjectType() == DataObjectType.VOLUME) { - return copyVolumeFromPrimaryToSecondary(srcData, destData, cmd.getWait()); - } else if (destData.getObjectType() == DataObjectType.TEMPLATE) { - return createTemplateFromVolume(srcData, destData, cmd.getWait()); - } - } else if (srcData.getObjectType() == DataObjectType.SNAPSHOT && srcData.getDataStore().getRole() == DataStoreRole.Primary) { - DataTO cacheData = cmd.getCacheTO(); - return backupSnasphot(srcData, destData, cacheData, cmd.getWait()); - } - - return new Answer(cmd, false, "not implemented yet"); - } } diff --git a/plugins/storage/image/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackImageStoreDriverImpl.java b/plugins/storage/image/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackImageStoreDriverImpl.java index 1865068ac55..52f8d9cf45f 100644 --- a/plugins/storage/image/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackImageStoreDriverImpl.java +++ b/plugins/storage/image/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackImageStoreDriverImpl.java @@ -27,10 +27,8 @@ import javax.inject.Inject; import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; @@ -53,7 +51,9 @@ import com.cloud.agent.api.Answer; import com.cloud.agent.api.storage.DeleteTemplateCommand; import com.cloud.agent.api.storage.DeleteVolumeCommand; import com.cloud.agent.api.storage.DownloadAnswer; +import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.NfsTO; import com.cloud.event.EventTypes; import com.cloud.event.UsageEventUtils; diff --git a/plugins/storage/image/s3/src/org/apache/cloudstack/storage/datastore/driver/S3ImageStoreDriverImpl.java b/plugins/storage/image/s3/src/org/apache/cloudstack/storage/datastore/driver/S3ImageStoreDriverImpl.java index 354151eaa3c..1992c5eda06 100644 --- a/plugins/storage/image/s3/src/org/apache/cloudstack/storage/datastore/driver/S3ImageStoreDriverImpl.java +++ b/plugins/storage/image/s3/src/org/apache/cloudstack/storage/datastore/driver/S3ImageStoreDriverImpl.java @@ -29,10 +29,8 @@ import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; @@ -57,7 +55,9 @@ import com.cloud.agent.api.DeleteSnapshotBackupCommand; import com.cloud.agent.api.storage.DeleteTemplateCommand; import com.cloud.agent.api.storage.DeleteVolumeCommand; import com.cloud.agent.api.storage.DownloadAnswer; +import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.S3TO; import com.cloud.event.EventTypes; import com.cloud.event.UsageEventUtils; diff --git a/plugins/storage/image/sample/src/org/apache/cloudstack/storage/datastore/driver/SampleImageStoreDriverImpl.java b/plugins/storage/image/sample/src/org/apache/cloudstack/storage/datastore/driver/SampleImageStoreDriverImpl.java index 72864c3513a..2c7ba1117b4 100644 --- a/plugins/storage/image/sample/src/org/apache/cloudstack/storage/datastore/driver/SampleImageStoreDriverImpl.java +++ b/plugins/storage/image/sample/src/org/apache/cloudstack/storage/datastore/driver/SampleImageStoreDriverImpl.java @@ -25,9 +25,7 @@ import javax.inject.Inject; import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; @@ -36,7 +34,9 @@ import org.apache.cloudstack.storage.command.CreateObjectAnswer; import org.apache.cloudstack.storage.command.CreateObjectCommand; import org.apache.cloudstack.storage.image.ImageStoreDriver; +import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; import com.cloud.storage.dao.VMTemplateDao; //http-read-only based image store diff --git a/plugins/storage/image/swift/src/org/apache/cloudstack/storage/datastore/driver/SwiftImageStoreDriverImpl.java b/plugins/storage/image/swift/src/org/apache/cloudstack/storage/datastore/driver/SwiftImageStoreDriverImpl.java index 0eb3acfc6a3..82df043bb81 100644 --- a/plugins/storage/image/swift/src/org/apache/cloudstack/storage/datastore/driver/SwiftImageStoreDriverImpl.java +++ b/plugins/storage/image/swift/src/org/apache/cloudstack/storage/datastore/driver/SwiftImageStoreDriverImpl.java @@ -29,10 +29,8 @@ import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; import org.apache.cloudstack.framework.async.AsyncCallbackDispatcher; @@ -56,7 +54,9 @@ import com.cloud.agent.api.DeleteSnapshotBackupCommand; import com.cloud.agent.api.storage.DeleteTemplateCommand; import com.cloud.agent.api.storage.DeleteVolumeCommand; import com.cloud.agent.api.storage.DownloadAnswer; +import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.SwiftTO; import com.cloud.event.EventTypes; import com.cloud.event.UsageEventUtils; diff --git a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java index 912a11ee66e..aad17691f47 100644 --- a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java @@ -25,9 +25,7 @@ import javax.inject.Inject; import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; @@ -50,7 +48,9 @@ import com.cloud.agent.api.storage.CreateCommand; import com.cloud.agent.api.storage.DestroyCommand; import com.cloud.agent.api.storage.ResizeVolumeAnswer; import com.cloud.agent.api.storage.ResizeVolumeCommand; +import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.exception.StorageUnavailableException; import com.cloud.host.dao.HostDao; diff --git a/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java b/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java index f530362896c..b3a83f797c3 100644 --- a/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java @@ -25,7 +25,6 @@ import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; @@ -42,6 +41,7 @@ import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.storage.encoding.DecodedDataObject; diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java index 9b2334bb60e..eee9ba5e1d9 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java @@ -22,7 +22,6 @@ import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; @@ -30,6 +29,7 @@ import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.storage.command.CommandResult; import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { diff --git a/server/src/com/cloud/hypervisor/HypervisorGuruBase.java b/server/src/com/cloud/hypervisor/HypervisorGuruBase.java index d77796da300..2300953e37f 100644 --- a/server/src/com/cloud/hypervisor/HypervisorGuruBase.java +++ b/server/src/com/cloud/hypervisor/HypervisorGuruBase.java @@ -22,6 +22,7 @@ import java.util.Map; import javax.inject.Inject; import com.cloud.agent.api.Command; +import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VolumeTO; @@ -101,7 +102,7 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis } to.setNics(nics); - to.setDisks(vmProfile.getDisks().toArray(new VolumeTO[vmProfile.getDisks().size()])); + to.setDisks(vmProfile.getDisks().toArray(new DataTO[vmProfile.getDisks().size()])); if(vmProfile.getTemplate().getBits() == 32) { to.setArch("i686"); diff --git a/server/src/com/cloud/storage/VolumeManagerImpl.java b/server/src/com/cloud/storage/VolumeManagerImpl.java index 90b2366130e..17e0e0906b8 100644 --- a/server/src/com/cloud/storage/VolumeManagerImpl.java +++ b/server/src/com/cloud/storage/VolumeManagerImpl.java @@ -45,18 +45,22 @@ import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProviderManager; -import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreInfo; import org.apache.cloudstack.engine.subsystem.api.storage.Scope; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; +import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService.VolumeApiResult; import org.apache.cloudstack.framework.async.AsyncCallFuture; +import org.apache.cloudstack.storage.command.AttachAnswer; +import org.apache.cloudstack.storage.command.AttachCommand; +import org.apache.cloudstack.storage.command.DettachAnswer; +import org.apache.cloudstack.storage.command.DettachCommand; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; @@ -69,8 +73,8 @@ import org.springframework.stereotype.Component; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; import com.cloud.agent.api.AttachVolumeAnswer; -import com.cloud.agent.api.AttachVolumeCommand; -import com.cloud.agent.api.to.VolumeTO; +import com.cloud.agent.api.to.DataTO; +import com.cloud.agent.api.to.DiskTO; import com.cloud.alert.AlertManager; import com.cloud.api.ApiDBUtils; import com.cloud.async.AsyncJobExecutor; @@ -117,7 +121,6 @@ import com.cloud.server.ManagementServer; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.Storage.ImageFormat; -import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.Volume.Event; import com.cloud.storage.Volume.Type; import com.cloud.storage.dao.DiskOfferingDao; @@ -168,7 +171,6 @@ import com.cloud.vm.dao.DomainRouterDao; 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.VMSnapshotVO; import com.cloud.vm.snapshot.dao.VMSnapshotDao; @@ -1514,7 +1516,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { String errorMsg = "Failed to attach volume: " + volume.getName() + " to VM: " + vm.getHostName(); boolean sendCommand = (vm.getState() == State.Running); - AttachVolumeAnswer answer = null; + AttachAnswer answer = null; Long hostId = vm.getHostId(); if (hostId == null) { hostId = vm.getLastHostId(); @@ -1526,16 +1528,11 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { } if (sendCommand) { - StoragePoolVO volumePool = _storagePoolDao.findById(volume - .getPoolId()); - AttachVolumeCommand cmd = new AttachVolumeCommand(true, - vm.getInstanceName(), volume.getPoolType(), - volume.getFolder(), volume.getPath(), volume.getName(), - deviceId, volume.getChainInfo()); - cmd.setPoolUuid(volumePool.getUuid()); - + DataTO volTO = this.volFactory.getVolume(volume.getId()).getTO(); + DiskTO disk = new DiskTO(volTO, deviceId, volume.getVolumeType()); + AttachCommand cmd = new AttachCommand(disk, vm.getInstanceName()); try { - answer = (AttachVolumeAnswer) _agentMgr.send(hostId, cmd); + answer = (AttachAnswer) _agentMgr.send(hostId, cmd); } catch (Exception e) { throw new CloudRuntimeException(errorMsg + " due to: " + e.getMessage()); @@ -1545,8 +1542,9 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { if (!sendCommand || (answer != null && answer.getResult())) { // Mark the volume as attached if (sendCommand) { + DiskTO disk = answer.getDisk(); _volsDao.attachVolume(volume.getId(), vm.getId(), - answer.getDeviceId()); + disk.getDiskSeq()); } else { _volsDao.attachVolume(volume.getId(), vm.getId(), deviceId); } @@ -1858,16 +1856,9 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { Answer answer = null; if (sendCommand) { - AttachVolumeCommand cmd = new AttachVolumeCommand(false, - vm.getInstanceName(), volume.getPoolType(), - volume.getFolder(), volume.getPath(), volume.getName(), - cmmd.getDeviceId() != null ? cmmd.getDeviceId() : volume - .getDeviceId(), volume.getChainInfo()); - - StoragePoolVO volumePool = _storagePoolDao.findById(volume - .getPoolId()); - cmd.setPoolUuid(volumePool.getUuid()); - + DataTO volTO = this.volFactory.getVolume(volume.getId()).getTO(); + DiskTO disk = new DiskTO(volTO, volume.getDeviceId(), volume.getVolumeType()); + DettachCommand cmd = new DettachCommand(disk, vm.getInstanceName()); try { answer = _agentMgr.send(vm.getHostId(), cmd); } catch (Exception e) { @@ -1879,12 +1870,6 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { if (!sendCommand || (answer != null && answer.getResult())) { // Mark the volume as detached _volsDao.detachVolume(volume.getId()); - if (answer != null && answer instanceof AttachVolumeAnswer) { - volume.setChainInfo(((AttachVolumeAnswer) answer) - .getChainInfo()); - _volsDao.update(volume.getId(), volume); - } - return _volsDao.findById(volumeId); } else { @@ -2097,22 +2082,17 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { } for (VolumeVO vol : vols) { - PrimaryDataStoreInfo pool = (PrimaryDataStoreInfo)this.dataStoreMgr.getDataStore(vol.getPoolId(), DataStoreRole.Primary); - vm.addDisk(new VolumeTO(vol, pool)); + DataTO volTO = this.volFactory.getVolume(vol.getId()).getTO(); + DiskTO disk = new DiskTO(volTO, vol.getDeviceId(), vol.getVolumeType()); + vm.addDisk(disk); } if (vm.getType() == VirtualMachine.Type.User) { UserVmVO userVM = (UserVmVO) vm.getVirtualMachine(); if (userVM.getIsoId() != null) { - Pair isoPathPair = this._tmpltMgr.getAbsoluteIsoPath( - userVM.getIsoId(), userVM.getDataCenterId()); - if (isoPathPair != null) { - String isoPath = isoPathPair.first(); - VolumeTO iso = new VolumeTO(vm.getId(), Volume.Type.ISO, - StoragePoolType.ISO, null, null, null, isoPath, 0, - null, null); - vm.addDisk(iso); - } + DataTO dataTO = this.tmplFactory.getTemplate(userVM.getIsoId(), DataStoreRole.Image, userVM.getDataCenterId()).getTO(); + DiskTO iso = new DiskTO(dataTO, 3L, Volume.Type.ISO); + vm.addDisk(iso); } } } @@ -2328,7 +2308,9 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { pool = (StoragePool)dataStoreMgr.getDataStore(result.second().getId(), DataStoreRole.Primary); vol = result.first(); } - vm.addDisk(new VolumeTO(vol, pool)); + DataTO volumeTO = this.volFactory.getVolume(vol.getId()).getTO(); + DiskTO disk = new DiskTO(volumeTO, vol.getDeviceId(), vol.getVolumeType()); + vm.addDisk(disk); } } diff --git a/server/src/com/cloud/storage/download/DownloadListener.java b/server/src/com/cloud/storage/download/DownloadListener.java index 001b45dc1c4..691d65548d7 100755 --- a/server/src/com/cloud/storage/download/DownloadListener.java +++ b/server/src/com/cloud/storage/download/DownloadListener.java @@ -26,7 +26,6 @@ import java.util.TimerTask; import javax.inject.Inject; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; @@ -50,6 +49,7 @@ import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupRoutingCommand; import com.cloud.agent.api.StartupSecondaryStorageCommand; import com.cloud.agent.api.storage.DownloadAnswer; +import com.cloud.agent.api.to.DataObjectType; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.ConnectionException; import com.cloud.host.Host; diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java index 20cdce16c36..0bdd8c14f64 100755 --- a/server/src/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/com/cloud/template/TemplateManagerImpl.java @@ -65,7 +65,9 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope; import org.apache.cloudstack.framework.async.AsyncCallFuture; +import org.apache.cloudstack.storage.command.AttachCommand; import org.apache.cloudstack.storage.command.CommandResult; +import org.apache.cloudstack.storage.command.DettachCommand; import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; import org.apache.cloudstack.storage.datastore.db.ImageStoreVO; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; @@ -79,8 +81,11 @@ import org.springframework.stereotype.Component; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; import com.cloud.agent.api.AttachIsoCommand; +import com.cloud.agent.api.Command; import com.cloud.agent.api.ComputeChecksumCommand; import com.cloud.agent.api.storage.DestroyCommand; +import com.cloud.agent.api.to.DataTO; +import com.cloud.agent.api.to.DiskTO; import com.cloud.api.ApiDBUtils; import com.cloud.async.AsyncJobManager; import com.cloud.async.AsyncJobVO; @@ -1024,27 +1029,13 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, } else if (vm.getState() != State.Running) { return true; } - String isoPath; - VMTemplateVO tmplt = this._tmpltDao.findById(isoId); + //FIXME: if it's s3, need to download into cache store + TemplateInfo tmplt = this._tmplFactory.getTemplate(isoId, DataStoreRole.Image, vm.getDataCenterId()); if (tmplt == null) { s_logger.warn("ISO: " + isoId + " does not exist"); return false; } - // Get the path of the ISO - Pair isoPathPair = null; - if (tmplt.getTemplateType() == TemplateType.PERHOST) { - isoPath = tmplt.getName(); - } else { - isoPathPair = getAbsoluteIsoPath(isoId, - vm.getDataCenterId()); - if (isoPathPair == null) { - s_logger.warn("Couldn't get absolute iso path"); - return false; - } else { - isoPath = isoPathPair.first(); - } - } - + String vmName = vm.getInstanceName(); HostVO host = _hostDao.findById(vm.getHostId()); @@ -1052,12 +1043,16 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, s_logger.warn("Host: " + vm.getHostId() + " does not exist"); return false; } - AttachIsoCommand cmd = new AttachIsoCommand(vmName, isoPath, attach); - if (isoPathPair != null) { - cmd.setStoreUrl(isoPathPair.second()); + + DataTO isoTO = tmplt.getTO(); + DiskTO disk = new DiskTO(isoTO, null, Volume.Type.ISO); + Command cmd = null; + if (attach) { + cmd = new AttachCommand(disk, vmName); + } else { + cmd = new DettachCommand(disk, vmName); } Answer a = _agentMgr.easySend(vm.getHostId(), cmd); - return (a != null && a.getResult()); } diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 000430fca25..98aeaf2e612 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -42,7 +42,10 @@ import org.apache.cloudstack.api.command.user.vmgroup.CreateVMGroupCmd; import org.apache.cloudstack.api.command.user.vmgroup.DeleteVMGroupCmd; import org.apache.cloudstack.engine.cloud.entity.api.VirtualMachineEntity; import org.apache.cloudstack.engine.service.api.OrchestrationService; +import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; @@ -58,6 +61,7 @@ import com.cloud.agent.api.StopAnswer; import com.cloud.agent.api.UnPlugNicAnswer; import com.cloud.agent.api.UnPlugNicCommand; import com.cloud.agent.api.VmStatsEntry; +import com.cloud.agent.api.to.DiskTO; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VolumeTO; @@ -159,6 +163,7 @@ import com.cloud.storage.Storage; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.Storage.TemplateType; +import com.cloud.storage.DataStoreRole; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolStatus; @@ -373,6 +378,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use @Inject List plannerSelectors; + @Inject + TemplateDataFactory templateFactory; protected ScheduledExecutorService _executor = null; protected int _expungeInterval; @@ -2625,52 +2632,33 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use vm.setDetails(details); if (vm.getIsoId() != null) { - String isoPath = null; - - VirtualMachineTemplate template = _templateDao.findById(vm - .getIsoId()); + TemplateInfo template = templateFactory.getTemplate(vm.getIsoId(), DataStoreRole.Image, vm.getDataCenterId()); + if (template == null || template.getFormat() != ImageFormat.ISO) { throw new CloudRuntimeException( "Can not find ISO in vm_template table for id " + vm.getIsoId()); } - - Pair isoPathPair = this.templateMgr.getAbsoluteIsoPath( - template.getId(), vm.getDataCenterId()); - - if (template.getTemplateType() == TemplateType.PERHOST) { - isoPath = template.getName(); - } else { - if (isoPathPair == null) { - s_logger.warn("Couldn't get absolute iso path"); - return false; - } else { - isoPath = isoPathPair.first(); - } - } - + if (template.isBootable()) { profile.setBootLoaderType(BootloaderType.CD); } + GuestOSVO guestOS = _guestOSDao.findById(template.getGuestOSId()); String displayName = null; if (guestOS != null) { displayName = guestOS.getDisplayName(); } - VolumeTO iso = new VolumeTO(profile.getId(), Volume.Type.ISO, - StoragePoolType.ISO, null, template.getName(), null, - isoPath, 0, null, displayName); - - iso.setDeviceId(3); - profile.addDisk(iso); + + TemplateObjectTO iso = (TemplateObjectTO)template.getTO(); + iso.setGuestOsType(displayName); + DiskTO disk = new DiskTO(iso, 3L, Volume.Type.ISO); + profile.addDisk(disk); } else { - VirtualMachineTemplate template = profile.getTemplate(); - /* create a iso placeholder */ - VolumeTO iso = new VolumeTO(profile.getId(), Volume.Type.ISO, - StoragePoolType.ISO, null, template.getName(), null, null, - 0, null); - iso.setDeviceId(3); - profile.addDisk(iso); + TemplateObjectTO iso = new TemplateObjectTO(); + iso.setFormat(ImageFormat.ISO); + DiskTO disk = new DiskTO(iso, 3L, Volume.Type.ISO); + profile.addDisk(disk); } return true; diff --git a/server/src/com/cloud/vm/VirtualMachineProfileImpl.java b/server/src/com/cloud/vm/VirtualMachineProfileImpl.java index 24f44cb07ac..c4e8822c0d8 100644 --- a/server/src/com/cloud/vm/VirtualMachineProfileImpl.java +++ b/server/src/com/cloud/vm/VirtualMachineProfileImpl.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; +import com.cloud.agent.api.to.DiskTO; import com.cloud.agent.api.to.VolumeTO; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.offering.ServiceOffering; @@ -46,7 +47,7 @@ public class VirtualMachineProfileImpl implements Virtua UserVmDetailVO _userVmDetails; Map _params; List _nics = new ArrayList(); - List _disks = new ArrayList(); + List _disks = new ArrayList(); StringBuilder _bootArgs = new StringBuilder(); Account _owner; BootloaderType _bootloader; @@ -141,7 +142,7 @@ public class VirtualMachineProfileImpl implements Virtua _nics = nics; } - public void setDisks(List disks) { + public void setDisks(List disks) { _disks = disks; } @@ -151,7 +152,7 @@ public class VirtualMachineProfileImpl implements Virtua } @Override - public List getDisks() { + public List getDisks() { return _disks; } @@ -161,7 +162,7 @@ public class VirtualMachineProfileImpl implements Virtua } @Override - public void addDisk(int index, VolumeTO disk) { + public void addDisk(int index, DiskTO disk) { _disks.add(index, disk); } @@ -210,7 +211,7 @@ public class VirtualMachineProfileImpl implements Virtua } @Override - public void addDisk(VolumeTO disk) { + public void addDisk(DiskTO disk) { _disks.add(disk); }