package org.zstack.appliancevm;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.transaction.annotation.Transactional;
import org.zstack.core.Platform;
import org.zstack.core.cloudbus.CloudBus;
import org.zstack.core.db.DatabaseFacade;
import org.zstack.core.db.TransactionalCallback;
import org.zstack.header.core.workflow.Flow;
import org.zstack.header.core.workflow.FlowException;
import org.zstack.header.core.workflow.FlowRollback;
import org.zstack.header.core.workflow.FlowTrigger;
import org.zstack.header.message.MessageReply;
import org.zstack.header.network.l3.*;
import org.zstack.header.vm.VmInstanceConstant;
import org.zstack.header.vm.VmInstanceSpec;
import org.zstack.header.vm.VmNicInventory;
import org.zstack.header.vm.VmNicVO;
import org.zstack.identity.AccountManager;
import org.zstack.utils.network.NetworkUtils;
import javax.persistence.Query;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Created with IntelliJ IDEA.
* User: frank
* Time: 11:38 PM
* To change this template use File | Settings | File Templates.
*/
@Configurable(preConstruction = true, autowire = Autowire.BY_TYPE)
public class ApplianceVmAllocateNicFlow implements Flow {
@Autowired
private CloudBus bus;
@Autowired
private DatabaseFacade dbf;
@Autowired
private AccountManager acntMgr;
private UsedIpInventory acquireIp(String l3NetworkUuid, String stratgey) {
AllocateIpMsg msg = new AllocateIpMsg();
msg.setL3NetworkUuid(l3NetworkUuid);
bus.makeTargetServiceIdByResourceUuid(msg, L3NetworkConstant.SERVICE_ID, l3NetworkUuid);
msg.setAllocateStrategy(stratgey);
MessageReply reply = bus.call(msg);
if (!reply.isSuccess()) {
throw new FlowException(reply.getError());
}
AllocateIpReply areply = (AllocateIpReply) reply;
return areply.getIpInventory();
}
private VmNicInventory makeNicInventory(VmInstanceSpec vmSpec, ApplianceVmNicSpec nicSpec, int[] deviceId) {
VmNicInventory inv = new VmNicInventory();
inv.setUuid(Platform.getUuid());
inv.setL3NetworkUuid(nicSpec.getL3NetworkUuid());
inv.setVmInstanceUuid(vmSpec.getVmInventory().getUuid());
inv.setDeviceId(deviceId[0]);
inv.setMetaData(nicSpec.getMetaData());
inv.setInternalName(VmNicVO.generateNicInternalName(vmSpec.getVmInventory().getInternalId(), inv.getDeviceId()));
inv.setMac(NetworkUtils.generateMacWithDeviceId((short) inv.getDeviceId()));
if (nicSpec.getIp() == null) {
String strategy = nicSpec.getAllocatorStrategy() == null ? L3NetworkConstant.RANDOM_IP_ALLOCATOR_STRATEGY : nicSpec.getAllocatorStrategy();
UsedIpInventory ip = acquireIp(nicSpec.getL3NetworkUuid(), strategy);
inv.setGateway(ip.getGateway());
inv.setIp(ip.getIp());
inv.setNetmask(ip.getNetmask());
inv.setUsedIpUuid(ip.getUuid());
} else {
inv.setGateway(nicSpec.getGateway());
inv.setIp(nicSpec.getIp());
inv.setNetmask(nicSpec.getNetmask());
inv.setUsedIpUuid(null);
if (nicSpec.getMac() != null) {
inv.setMac(nicSpec.getMac());
}
}
deviceId[0] ++;
return inv;
}
@Transactional
private void persistNicInDb(List<VmNicInventory> nics) {
dbf.entityForTranscationCallback(TransactionalCallback.Operation.PERSIST, VmNicVO.class);
for (VmNicInventory nic : nics) {
VmNicVO nvo = new VmNicVO();
nvo.setUuid(nic.getUuid());
nvo.setDeviceId(nic.getDeviceId());
nvo.setIp(nic.getIp());
nvo.setL3NetworkUuid(nic.getL3NetworkUuid());
nvo.setMac(nic.getMac());
nvo.setUsedIpUuid(nic.getUsedIpUuid());
nvo.setGateway(nic.getGateway());
nvo.setNetmask(nic.getNetmask());
nvo.setVmInstanceUuid(nic.getVmInstanceUuid());
nvo.setMetaData(nic.getMetaData());
nvo.setInternalName(nic.getInternalName());
dbf.getEntityManager().persist(nvo);
}
}
@Transactional
private void removeNicFromDb(List<VmNicInventory> nics) {
dbf.entityForTranscationCallback(TransactionalCallback.Operation.REMOVE, VmNicVO.class);
List<String> uuids = new ArrayList<String>(nics.size());
for (VmNicInventory nic : nics) {
uuids.add(nic.getUuid());
}
String sql = "delete from VmNicVO v where v.uuid in (:uuids)";
Query q = dbf.getEntityManager().createQuery(sql);
q.setParameter("uuids", uuids);
q.executeUpdate();
}
@Override
public void run(FlowTrigger chain, Map data) {
VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString());
ApplianceVmSpec aspec = spec.getExtensionData(ApplianceVmConstant.Params.applianceVmSpec.toString(), ApplianceVmSpec.class);
int[] deviceId = {0};
VmNicInventory mgmtNic = makeNicInventory(spec, aspec.getManagementNic(), deviceId);
spec.getDestNics().add(mgmtNic);
for (ApplianceVmNicSpec nicSpec : aspec.getAdditionalNics()) {
spec.getDestNics().add(makeNicInventory(spec, nicSpec, deviceId));
}
persistNicInDb(spec.getDestNics());
String acntUuid = acntMgr.getOwnerAccountUuidOfResource(spec.getVmInventory().getUuid());
for (VmNicInventory nic : spec.getDestNics()) {
acntMgr.createAccountResourceRef(acntUuid, nic.getUuid(), VmNicVO.class);
}
ApplianceVmVO apvm = dbf.findByUuid(spec.getVmInventory().getUuid(), ApplianceVmVO.class);
apvm.setManagementNetworkUuid(mgmtNic.getL3NetworkUuid());
dbf.update(apvm);
chain.next();
}
@Override
public void rollback(FlowRollback chain, Map data) {
try {
VmInstanceSpec spec = (VmInstanceSpec) data.get(VmInstanceConstant.Params.VmInstanceSpec.toString());
List<VmNicInventory> nics = spec.getDestNics();
if (nics.isEmpty()) {
return;
}
removeNicFromDb(nics);
for (VmNicInventory nic : nics) {
if (nic.getUsedIpUuid() == null) {
continue;
}
ReturnIpMsg msg = new ReturnIpMsg();
msg.setL3NetworkUuid(nic.getL3NetworkUuid());
msg.setUsedIpUuid(nic.getUsedIpUuid());
bus.makeTargetServiceIdByResourceUuid(msg, L3NetworkConstant.SERVICE_ID, nic.getL3NetworkUuid());
bus.send(msg);
}
} finally {
chain.rollback();
}
}
}