Skip to content

Commit

Permalink
Check cluster when attach data volume to vm
Browse files Browse the repository at this point in the history
Resolves ZSTAC-9039

Signed-off-by: AlanJager <[email protected]>
  • Loading branch information
AlanJager committed Feb 7, 2018
1 parent 0b05540 commit 2381a34
Show file tree
Hide file tree
Showing 3 changed files with 350 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
import org.zstack.core.db.SimpleQuery.Op;
import org.zstack.core.errorcode.ErrorFacade;
import org.zstack.header.Component;
import org.zstack.header.apimediator.ApiMessageInterceptionException;
import org.zstack.header.cluster.ClusterVO;
import org.zstack.header.cluster.ClusterVO_;
import org.zstack.header.core.FutureCompletion;
import org.zstack.header.core.ReturnValueCompletion;
import org.zstack.header.core.workflow.*;
Expand Down Expand Up @@ -531,10 +534,29 @@ public void preAttachVolume(VmInstanceInventory vm, final VolumeInventory volume
q.groupBy(LocalStorageResourceRefVO_.hostUuid);
long count = q.count();

if (count < 2) {

if (count == 0) {
return;
}

// if count is 1, multi primary storage is indicated
if (count == 1) {
if (!Q.New(LocalStorageResourceRefVO.class).eq(LocalStorageResourceRefVO_.resourceUuid, volume.getUuid()).isExists()) {
return;
}

String vmClusterUuid = vm.getClusterUuid();
String volumeHostUuid = Q.New(LocalStorageResourceRefVO.class)
.select(LocalStorageResourceRefVO_.hostUuid)
.eq(LocalStorageResourceRefVO_.resourceUuid, volume.getUuid()).findValue();

if (!Q.New(HostVO.class)
.eq(HostVO_.uuid, volumeHostUuid)
.eq(HostVO_.clusterUuid, vmClusterUuid).isExists()) {
throw new OperationFailureException(operr("Can't attach volume to VM, no qualified cluster"));
}
}

q = dbf.createQuery(LocalStorageResourceRefVO.class);
q.select(LocalStorageResourceRefVO_.hostUuid);
q.add(LocalStorageResourceRefVO_.resourceUuid, Op.EQ, vm.getRootVolumeUuid());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
import org.zstack.header.image.ImageStatus;
import org.zstack.header.image.ImageVO;
import org.zstack.header.message.APIMessage;
import org.zstack.header.storage.primary.PrimaryStorageClusterRefVO;
import org.zstack.header.storage.primary.PrimaryStorageClusterRefVO_;
import org.zstack.header.vm.VmInstance;
import org.zstack.header.vm.VmInstanceState;
import org.zstack.header.vm.VmInstanceVO;
import org.zstack.header.vm.VmInstanceVO_;
Expand All @@ -31,6 +34,7 @@
import static org.zstack.core.Platform.operr;

import javax.persistence.Tuple;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -166,6 +170,37 @@ private void validate(APIAttachDataVolumeToVmMsg msg) {
new SQLBatch(){
@Override
protected void scripts() {
VolumeVO vol = q(VolumeVO.class).eq(VolumeVO_.uuid, msg.getVolumeUuid()).find();
List<String> volumeClusterUuids = q(PrimaryStorageClusterRefVO.class)
.select(PrimaryStorageClusterRefVO_.clusterUuid)
.eq(PrimaryStorageClusterRefVO_.primaryStorageUuid, vol.getPrimaryStorageUuid())
.listValues();

List<String> vmInstanceClusterUuids = q(VmInstanceVO.class)
.select(VmInstanceVO_.clusterUuid)
.eq(VmInstanceVO_.uuid, msg.getVmInstanceUuid())
.listValues();

if (vmInstanceClusterUuids.isEmpty()) {
String vmRootVolumeUuid = q(VmInstanceVO.class).select(VmInstanceVO_.rootVolumeUuid)
.eq(VmInstanceVO_.uuid, msg.getVmInstanceUuid()).findValue();

String vmPrimaryStorageUuid = q(VolumeVO.class).select(VolumeVO_.primaryStorageUuid)
.eq(VmInstanceVO_.uuid, vmRootVolumeUuid).findValue();

vmInstanceClusterUuids = q(PrimaryStorageClusterRefVO.class)
.select(PrimaryStorageClusterRefVO_.clusterUuid)
.eq(PrimaryStorageClusterRefVO_.primaryStorageUuid, vmPrimaryStorageUuid)
.listValues();
}

vmInstanceClusterUuids.retainAll(volumeClusterUuids);

// if there is no cluster contains both vm root volume and data volume, the data volume won't be attachable
if (vmInstanceClusterUuids.isEmpty() && !volumeClusterUuids.isEmpty()) {
throw new ApiMessageInterceptionException(operr("Can't attach volume to VM, no qualified cluster"));
}

long count = sql("select count(vm.uuid)" +
" from VmInstanceVO vm, ImageVO image" +
" where vm.uuid = :vmUuid" +
Expand All @@ -179,7 +214,7 @@ protected void scripts() {
throw new ApiMessageInterceptionException(operr("the vm[uuid:%s] doesn't support to online attach volume[%s] on the basis of that the image platform type of the vm is other ", msg.getVmInstanceUuid(), msg.getVolumeUuid()));
}

VolumeVO vol = Q.New(VolumeVO.class).eq(VolumeVO_.uuid, msg.getVolumeUuid()).find();

if (vol.getType() == VolumeType.Root) {
throw new ApiMessageInterceptionException(operr("the volume[uuid:%s, name:%s] is Root Volume, can't attach it",
vol.getUuid(), vol.getName()));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
package org.zstack.test.integration.storage.primary.local_nfs

import org.zstack.header.vm.VmCreationStrategy
import org.zstack.sdk.AttachDataVolumeToVmAction
import org.zstack.sdk.BackupStorageInventory
import org.zstack.sdk.ClusterInventory
import org.zstack.sdk.DiskOfferingInventory
import org.zstack.sdk.HostInventory
import org.zstack.sdk.ImageInventory
import org.zstack.sdk.InstanceOfferingInventory
import org.zstack.sdk.L3NetworkInventory
import org.zstack.sdk.PrimaryStorageInventory
import org.zstack.sdk.VmInstanceInventory
import org.zstack.sdk.VolumeInventory
import org.zstack.test.integration.storage.StorageTest
import org.zstack.testlib.EnvSpec
import org.zstack.testlib.SubCase
import org.zstack.utils.data.SizeUnit

/**
* Created by kayo on 2018/2/5.
*/
class AttachDataVolumeCrossClusterCase extends SubCase {
EnvSpec env

@Override
void clean() {
env.delete()
}

@Override
void setup() {
useSpring(StorageTest.springSpec)
}

@Override
void environment() {
env = env {
instanceOffering {
name = "instanceOffering"
memory = SizeUnit.GIGABYTE.toByte(1)
cpu = 1
}

diskOffering {
name = "diskOffering"
diskSize = SizeUnit.GIGABYTE.toByte(1)
}

sftpBackupStorage {
name = "sftp"
url = "/sftp"
username = "root"
password = "password"
hostname = "localhost"

image {
name = "image"
url = "http://zstack.org/download/test.qcow2"
}
}

zone {
name = "zone"
description = "test"

cluster {
name = "cluster"
hypervisorType = "KVM"

kvm {
name = "kvm"
managementIp = "localhost"
username = "root"
password = "password"
totalCpu = 88
totalMem = SizeUnit.GIGABYTE.toByte(100)
}

kvm {
name = "kvm2"
managementIp = "127.0.0.2"
username = "root"
password = "password"
totalCpu = 88
totalMem = SizeUnit.GIGABYTE.toByte(100)
}

attachPrimaryStorage("local")
attachL2Network("l2")
}

cluster {
name = "cluster2"
hypervisorType = "KVM"

kvm {
name = "kvm3"
managementIp = "127.0.0.3"
username = "root"
password = "password"
totalCpu = 88
totalMem = SizeUnit.GIGABYTE.toByte(100)
}

kvm {
name = "kvm4"
managementIp = "127.0.0.4"
username = "root"
password = "password"
totalCpu = 88
totalMem = SizeUnit.GIGABYTE.toByte(100)
}

attachPrimaryStorage("nfs")
attachL2Network("l2")
}

localPrimaryStorage {
name = "local"
url = "/local_ps"
}

nfsPrimaryStorage {
name = "nfs"
url = "127.20.0.1:/nfs_root"
}

l2NoVlanNetwork {
name = "l2"
physicalInterface = "eth0"

l3Network {
name = "l3"

ip {
startIp = "12.16.10.10"
endIp = "12.16.10.100"
netmask = "255.255.255.0"
gateway = "12.16.10.1"
}
}
}

attachBackupStorage("sftp")
}

vm {
name = "vm"
useCluster("cluster2")
useHost("kvm3")
useL3Networks("l3")
useInstanceOffering("instanceOffering")
useRootDiskOffering("diskOffering")
useImage("image")

}
}
}

@Override
void test() {
env.create {
testDataVolumeCrossClusterAttachedForStoppedVm()
testDataVolumeCrossClusterAttachedForJustCreatedVm()
testDataVolumeCrossClusterAttachedInMultiPs()
}
}

void testDataVolumeCrossClusterAttachedForStoppedVm() {
VmInstanceInventory vm = env.inventoryByName("vm")
DiskOfferingInventory diskOffering = env.inventoryByName("diskOffering")
PrimaryStorageInventory local = env.inventoryByName("local")
PrimaryStorageInventory nfs = env.inventoryByName("nfs")
HostInventory host = env.inventoryByName("kvm")
stopVmInstance {
uuid = vm.uuid
}

VolumeInventory volume = createDataVolume {
name = "data-volume"
diskOfferingUuid = diskOffering.uuid
primaryStorageUuid = local.uuid
systemTags = ["localStorage::hostUuid::" + host.uuid]
}

AttachDataVolumeToVmAction action = new AttachDataVolumeToVmAction()
action.sessionId = adminSession()
action.vmInstanceUuid = vm.uuid
action.volumeUuid = volume.uuid
AttachDataVolumeToVmAction.Result ret = action.call()

assert ret.error.details.contains("attach volume to VM, no qualified cluster")
}

void testDataVolumeCrossClusterAttachedForJustCreatedVm() {
DiskOfferingInventory diskOffering = env.inventoryByName("diskOffering")
PrimaryStorageInventory local = env.inventoryByName("local")
HostInventory host = env.inventoryByName("kvm")
ImageInventory image = env.inventoryByName("image")
InstanceOfferingInventory instanceOffering = env.inventoryByName("instanceOffering")
L3NetworkInventory l3 = env.inventoryByName("l3")
HostInventory host4 = env.inventoryByName("kvm4")

VmInstanceInventory vm = createVmInstance {
name = "test-2"
imageUuid = image.uuid
instanceOfferingUuid = instanceOffering.uuid
l3NetworkUuids = [l3.uuid]
hostUuid = host4.uuid
strategy = VmCreationStrategy.JustCreate.toString()
}

VolumeInventory volume = createDataVolume {
name = "data-volume"
diskOfferingUuid = diskOffering.uuid
primaryStorageUuid = local.uuid
systemTags = ["localStorage::hostUuid::" + host.uuid]
}

AttachDataVolumeToVmAction action = new AttachDataVolumeToVmAction()
action.sessionId = adminSession()
action.vmInstanceUuid = vm.uuid
action.volumeUuid = volume.uuid
AttachDataVolumeToVmAction.Result ret = action.call()

assert ret.error.details.contains("attach volume to VM, no qualified cluster")
}

void testDataVolumeCrossClusterAttachedInMultiPs() {
DiskOfferingInventory diskOffering = env.inventoryByName("diskOffering")
PrimaryStorageInventory local = env.inventoryByName("local")
PrimaryStorageInventory nfs = env.inventoryByName("nfs")
HostInventory host = env.inventoryByName("kvm")
ImageInventory image = env.inventoryByName("image")
InstanceOfferingInventory instanceOffering = env.inventoryByName("instanceOffering")
L3NetworkInventory l3 = env.inventoryByName("l3")
ClusterInventory cluster = env.inventoryByName("cluster")
HostInventory host4 = env.inventoryByName("kvm4")
ClusterInventory cluster2 = env.inventoryByName("cluster2")

attachPrimaryStorageToCluster {
primaryStorageUuid = nfs.uuid
clusterUuid = cluster.uuid
}

attachPrimaryStorageToCluster {
primaryStorageUuid = local.uuid
clusterUuid = cluster2.uuid
}

VmInstanceInventory vm = createVmInstance {
name = "test-3"
imageUuid = image.uuid
instanceOfferingUuid = instanceOffering.uuid
l3NetworkUuids = [l3.uuid]
hostUuid = host.uuid
primaryStorageUuidForRootVolume = nfs.uuid
clusterUuid = cluster.uuid
}

stopVmInstance {
uuid = vm.uuid
}

VolumeInventory volume = createDataVolume {
name = "data-volume-3"
diskOfferingUuid = diskOffering.uuid
primaryStorageUuid = local.uuid
systemTags = ["localStorage::hostUuid::" + host4.uuid]
}

AttachDataVolumeToVmAction action = new AttachDataVolumeToVmAction()
action.sessionId = adminSession()
action.vmInstanceUuid = vm.uuid
action.volumeUuid = volume.uuid
AttachDataVolumeToVmAction.Result ret = action.call()

assert ret.error.details.contains("attach volume to VM, no qualified cluster")

detachPrimaryStorageFromCluster {
primaryStorageUuid = nfs.uuid
clusterUuid = cluster.uuid
}

detachPrimaryStorageFromCluster {
primaryStorageUuid = local.uuid
clusterUuid = cluster2.uuid
}
}
}

0 comments on commit 2381a34

Please sign in to comment.