package org.zstack.appliancevm; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.zstack.appliancevm.ApplianceVmConstant.BootstrapParams; import org.zstack.core.CoreGlobalProperty; import org.zstack.core.Platform; import org.zstack.core.ansible.AnsibleFacade; import org.zstack.core.cloudbus.CloudBus; import org.zstack.core.cloudbus.CloudBusCallBack; import org.zstack.core.cloudbus.MessageSafe; import org.zstack.core.componentloader.PluginExtension; import org.zstack.core.componentloader.PluginRegistry; import org.zstack.core.config.GlobalConfigFacade; import org.zstack.core.db.DatabaseFacade; import org.zstack.core.db.SimpleQuery; import org.zstack.core.db.SimpleQuery.Op; import org.zstack.core.job.JobQueueFacade; import org.zstack.core.workflow.FlowChainBuilder; import org.zstack.header.AbstractService; import org.zstack.header.Component; import org.zstack.header.core.Completion; import org.zstack.header.core.ReturnValueCompletion; import org.zstack.header.core.workflow.Flow; import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.exception.CloudRuntimeException; import org.zstack.header.host.HypervisorType; import org.zstack.header.message.APIMessage; import org.zstack.header.message.Message; import org.zstack.header.message.MessageReply; import org.zstack.header.vm.*; import org.zstack.utils.CollectionUtils; import org.zstack.utils.DebugUtils; import org.zstack.utils.Utils; import org.zstack.utils.function.Function; import org.zstack.utils.logging.CLogger; import javax.persistence.Query; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Created with IntelliJ IDEA. * User: frank * Time: 11:55 PM * To change this template use File | Settings | File Templates. */ public class ApplianceVmFacadeImpl extends AbstractService implements ApplianceVmFacade, Component { private static final CLogger logger = Utils.getLogger(ApplianceVmFacadeImpl.class); @Autowired private CloudBus bus; @Autowired private DatabaseFacade dbf; @Autowired private JobQueueFacade jobf; @Autowired private PluginRegistry pluginRgty; @Autowired private GlobalConfigFacade gcf; @Autowired private AnsibleFacade asf; private List<String> createApplianceVmWorkFlow; private FlowChainBuilder createApplianceVmWorkFlowBuilder; private Map<String, ApplianceVmBootstrapFlowFactory> bootstrapInfoFlowFactories = new HashMap<String, ApplianceVmBootstrapFlowFactory>(); private String OWNER = String.format("ApplianceVm.%s", Platform.getManagementServerId()); public void createApplianceVm(ApplianceVmSpec spec, final ReturnValueCompletion<ApplianceVmInventory> completion) { CreateApplianceVmJob job = new CreateApplianceVmJob(); job.setSpec(spec); if (!spec.isSyncCreate()) { job.run(new ReturnValueCompletion<Object>(completion) { @Override public void success(Object returnValue) { completion.success((ApplianceVmInventory) returnValue); } @Override public void fail(ErrorCode errorCode) { completion.fail(errorCode); } }); } else { jobf.execute(spec.getName(), OWNER, job, completion, ApplianceVmInventory.class); } } @Override public void startApplianceVm(final String vmUuid, final ReturnValueCompletion<ApplianceVmInventory> completion) { StartVmInstanceMsg msg = new StartVmInstanceMsg(); msg.setVmInstanceUuid(vmUuid); bus.makeTargetServiceIdByResourceUuid(msg, VmInstanceConstant.SERVICE_ID, vmUuid); bus.send(msg, new CloudBusCallBack(completion) { @Override public void run(MessageReply reply) { if (reply.isSuccess()) { ApplianceVmVO vo = dbf.findByUuid(vmUuid, ApplianceVmVO.class); completion.success(ApplianceVmInventory.valueOf(vo)); } else { completion.fail(reply.getError()); } } }); } @Override public void stopApplianceVm(final String vmUuid, final ReturnValueCompletion<ApplianceVmInventory> completion) { StopVmInstanceMsg msg = new StopVmInstanceMsg(); msg.setVmInstanceUuid(vmUuid); bus.makeTargetServiceIdByResourceUuid(msg, VmInstanceConstant.SERVICE_ID, vmUuid); bus.send(msg, new CloudBusCallBack(completion) { @Override public void run(MessageReply reply) { if (reply.isSuccess()) { ApplianceVmVO vo = dbf.findByUuid(vmUuid, ApplianceVmVO.class); completion.success(ApplianceVmInventory.valueOf(vo)); } else { completion.fail(reply.getError()); } } }); } @Override public void rebootApplianceVm(final String vmUuid, final ReturnValueCompletion<ApplianceVmInventory> completion) { RebootVmInstanceMsg msg = new RebootVmInstanceMsg(); msg.setVmInstanceUuid(vmUuid); bus.makeTargetServiceIdByResourceUuid(msg, VmInstanceConstant.SERVICE_ID, vmUuid); bus.send(msg, new CloudBusCallBack(completion) { @Override public void run(MessageReply reply) { if (reply.isSuccess()) { ApplianceVmVO vo = dbf.findByUuid(vmUuid, ApplianceVmVO.class); completion.success(ApplianceVmInventory.valueOf(vo)); } else { completion.fail(reply.getError()); } } }); } @Override public void destroyApplianceVm(final String vmUuid, final ReturnValueCompletion<ApplianceVmInventory> completion) { final ApplianceVmVO vo = dbf.findByUuid(vmUuid, ApplianceVmVO.class); DestroyVmInstanceMsg msg = new DestroyVmInstanceMsg(); msg.setVmInstanceUuid(vmUuid); bus.makeTargetServiceIdByResourceUuid(msg, VmInstanceConstant.SERVICE_ID, vmUuid); bus.send(msg, new CloudBusCallBack(completion) { @Override public void run(MessageReply reply) { if (reply.isSuccess()) { completion.success(ApplianceVmInventory.valueOf(vo)); } else { completion.fail(reply.getError()); } } }); } @Override public void destroyApplianceVm(String vmUuid) { destroyApplianceVm(vmUuid, new ReturnValueCompletion<ApplianceVmInventory>(null) { @Override public void success(ApplianceVmInventory returnValue) { } @Override public void fail(ErrorCode errorCode) { } }); } public List<String> getCreateApplianceVmWorkFlow() { return createApplianceVmWorkFlow; } public void setCreateApplianceVmWorkFlow(List<String> createApplianceVmWorkFlow) { this.createApplianceVmWorkFlow = createApplianceVmWorkFlow; } private void populateExtensions() { List<PluginExtension> exts = pluginRgty.getExtensionByInterfaceName(ApplianceVmBootstrapFlowFactory.class.getName()); for (PluginExtension ext : exts) { ApplianceVmBootstrapFlowFactory extp = (ApplianceVmBootstrapFlowFactory) ext.getInstance(); ApplianceVmBootstrapFlowFactory old = bootstrapInfoFlowFactories.get(extp.getHypervisorTypeForApplianceVmBootstrapFlow().toString()); if (old != null) { throw new CloudRuntimeException(String.format("two extensions[%s, %s] declare ApplianceVmBootstrapFlowFactory for hypervisor type[%s]", old.getClass().getName(), extp.getClass().getName(), extp.getHypervisorTypeForApplianceVmBootstrapFlow())); } bootstrapInfoFlowFactories.put(extp.getHypervisorTypeForApplianceVmBootstrapFlow().toString(), extp); } } private void deployAnsible() { if (CoreGlobalProperty.UNIT_TEST_ON) { return; } asf.deployModule(ApplianceVmConstant.ANSIBLE_MODULE_PATH, ApplianceVmConstant.ANSIBLE_PLAYBOOK_NAME); } @Override public boolean start() { createApplianceVmWorkFlowBuilder = FlowChainBuilder.newBuilder().setFlowClassNames(getCreateApplianceVmWorkFlow()).construct(); populateExtensions(); deployAnsible(); return true; } @Override public boolean stop() { return true; } public FlowChainBuilder getCreateApplianceVmWorkFlowBuilder() { return createApplianceVmWorkFlowBuilder; } private List<VmNicInventory> reduceNic(List<VmNicInventory> nics, VmNicInventory nic) { List<VmNicInventory> ret = new ArrayList<VmNicInventory>(); for (VmNicInventory n : nics) { if (n.getUuid().equals(nic.getUuid())) { continue; } ret.add(n); } return ret; } @Override public Map<String, Object> prepareBootstrapInformation(VmInstanceSpec spec) { VmNicInventory mgmtNic = null; String defaultL3Uuid; int sshPort; if (spec.getCurrentVmOperation() == VmInstanceConstant.VmOperation.NewCreate) { ApplianceVmSpec aspec = spec.getExtensionData(ApplianceVmConstant.Params.applianceVmSpec.toString(), ApplianceVmSpec.class); for (VmNicInventory nic : spec.getDestNics()) { if (nic.getL3NetworkUuid().equals(aspec.getManagementNic().getL3NetworkUuid())) { mgmtNic = nic; break; } } DebugUtils.Assert(mgmtNic!=null, String.format("cannot find management nic for appliance vm[uuid:%s]", aspec.getUuid())); defaultL3Uuid = aspec.getDefaultRouteL3Network() != null ? aspec.getDefaultRouteL3Network().getUuid() : mgmtNic.getL3NetworkUuid(); sshPort = aspec.getSshPort(); } else { ApplianceVmVO avo = dbf.findByUuid(spec.getVmInventory().getUuid(), ApplianceVmVO.class); ApplianceVmInventory ainv = ApplianceVmInventory.valueOf(avo); mgmtNic = ainv.getManagementNic(); defaultL3Uuid = ainv.getDefaultRouteL3NetworkUuid(); //TODO: make it configurable sshPort = 22; } Map<String, Object> ret = new HashMap<String, Object>(); ApplianceVmNicTO mto = new ApplianceVmNicTO(mgmtNic); mto.setDeviceName(String.format("eth0")); if (mgmtNic.getL3NetworkUuid().equals(defaultL3Uuid)) { mto.setDefaultRoute(true); } ret.put(ApplianceVmConstant.BootstrapParams.managementNic.toString(), mto); List<ApplianceVmNicTO> extraTos = new ArrayList<ApplianceVmNicTO>(); ret.put(ApplianceVmConstant.BootstrapParams.additionalNics.toString(), extraTos); List<VmNicInventory> additionalNics = reduceNic(spec.getDestNics(), mgmtNic); // if management nic is not default route nic, choose default route nic as eth1 int deviceId = 1; if (!mto.isDefaultRoute()) { VmNicInventory defaultRouteNic = null; for (VmNicInventory nic : additionalNics) { if (nic.getL3NetworkUuid().equals(defaultL3Uuid)) { defaultRouteNic = nic; break; } } ApplianceVmNicTO t = new ApplianceVmNicTO(defaultRouteNic); t.setDeviceName(String.format("eth%s", deviceId)); t.setDefaultRoute(true); deviceId ++; extraTos.add(t); additionalNics = reduceNic(additionalNics, defaultRouteNic); } for (VmNicInventory nic : additionalNics) { ApplianceVmNicTO nto = new ApplianceVmNicTO(nic); nto.setDeviceName(String.format("eth%s", deviceId)); extraTos.add(nto); deviceId ++; } String publicKey = asf.getPublicKey(); ret.put(ApplianceVmConstant.BootstrapParams.publicKey.toString(), publicKey); ret.put(BootstrapParams.sshPort.toString(), sshPort); for (ApplianceVmPrepareBootstrapInfoExtensionPoint ext : pluginRgty.getExtensionList(ApplianceVmPrepareBootstrapInfoExtensionPoint.class)) { ext.applianceVmPrepareBootstrapInfo(spec, ret); } return ret; } @Override public Flow createBootstrapFlow(HypervisorType hvType) { ApplianceVmBootstrapFlowFactory factory = bootstrapInfoFlowFactories.get(hvType.toString()); if (factory == null) { throw new CloudRuntimeException(String.format("unable to find ApplianceVmBootstrapFlowFactory for hypervisor type[%s]", hvType)); } return factory.createApplianceVmBootstrapInfoFlow(); } void openFirewallInBootstrap(String apvmUuid, final String l3uuid, List<ApplianceVmFirewallRuleInventory> rules, final Completion completion) { openFirewall(apvmUuid, l3uuid, rules, false, completion); } private void openFirewall(final String apvmUuid, final String l3uuid, final List<ApplianceVmFirewallRuleInventory> rules, boolean needVmSync, final Completion completion) { final ApplianceVmVO apvm = dbf.findByUuid(apvmUuid, ApplianceVmVO.class); VmNicInventory targetNic = CollectionUtils.find(apvm.getVmNics(), new Function<VmNicInventory, VmNicVO>() { @Override public VmNicInventory call(VmNicVO arg) { if (arg.getL3NetworkUuid().equals(l3uuid)) { return VmNicInventory.valueOf(arg); } return null; } }); DebugUtils.Assert(targetNic!=null, String.format("appliance vm[uuid:%s, name:%s] is not on l3network[uuid:%s]", apvm.getUuid(), apvm.getName(), l3uuid)); List<String> ids = CollectionUtils.transformToList(rules, new Function<String, ApplianceVmFirewallRuleInventory>() { @Override public String call(ApplianceVmFirewallRuleInventory arg) { arg.setL3NetworkUuid(l3uuid); arg.setApplianceVmUuid(apvmUuid); return arg.makeIdentity(); } }); SimpleQuery<ApplianceVmFirewallRuleVO> q = dbf.createQuery(ApplianceVmFirewallRuleVO.class); q.select(ApplianceVmFirewallRuleVO_.identity); q.add(ApplianceVmFirewallRuleVO_.identity, Op.IN, ids); final List<String> existingIds = q.listValue(); List<ApplianceVmFirewallRuleVO> vos = CollectionUtils.transformToList(rules, new Function<ApplianceVmFirewallRuleVO, ApplianceVmFirewallRuleInventory>() { @Override public ApplianceVmFirewallRuleVO call(ApplianceVmFirewallRuleInventory r) { if (!existingIds.contains(r.makeIdentity())) { ApplianceVmFirewallRuleVO vo = new ApplianceVmFirewallRuleVO(); vo.setSourceIp(r.getSourceIp()); vo.setDestIp(r.getDestIp()); vo.setAllowCidr(r.getAllowCidr()); vo.setProtocol(ApplianceVmFirewallProtocol.valueOf(r.getProtocol())); vo.setEndPort(r.getEndPort()); vo.setStartPort(r.getStartPort()); vo.setL3NetworkUuid(l3uuid); vo.setApplianceVmUuid(apvm.getUuid()); vo.makeIdentity(); return vo; } return null; } }); dbf.persistCollection(vos); ApplianceVmRefreshFirewallMsg msg = new ApplianceVmRefreshFirewallMsg(); msg.setVmInstanceUuid(apvm.getUuid()); msg.setInSyncThread(needVmSync); bus.makeTargetServiceIdByResourceUuid(msg, VmInstanceConstant.SERVICE_ID, msg.getVmInstanceUuid()); bus.send(msg, new CloudBusCallBack(completion) { @Override public void run(MessageReply reply) { if (reply.isSuccess()) { completion.success(); } else { completion.fail(reply.getError()); } } }); } @Override public void openFirewall(String apvmUuid, final String l3uuid, List<ApplianceVmFirewallRuleInventory> rules, final Completion completion) { openFirewall(apvmUuid, l3uuid, rules, true, completion); } private void removeFirewall(final String applianceVmUuid, final String l3uuid, List<ApplianceVmFirewallRuleInventory> rules, boolean needVmSync, final Completion completion) { final List<String> ids = CollectionUtils.transformToList(rules, new Function<String, ApplianceVmFirewallRuleInventory>() { @Override public String call(ApplianceVmFirewallRuleInventory r) { r.setL3NetworkUuid(l3uuid); r.setApplianceVmUuid(applianceVmUuid); return r.makeIdentity(); } }); new Runnable() { @Override @Transactional public void run() { String sql = "delete from ApplianceVmFirewallRuleVO r where r.identity in (:ids)"; Query q = dbf.getEntityManager().createQuery(sql); q.setParameter("ids", ids); q.executeUpdate(); } }.run(); ApplianceVmRefreshFirewallMsg msg = new ApplianceVmRefreshFirewallMsg(); msg.setVmInstanceUuid(applianceVmUuid); msg.setInSyncThread(needVmSync); bus.makeTargetServiceIdByResourceUuid(msg, VmInstanceConstant.SERVICE_ID, msg.getVmInstanceUuid()); bus.send(msg, new CloudBusCallBack(completion) { @Override public void run(MessageReply reply) { if (reply.isSuccess()) { completion.success(); } else { completion.fail(reply.getError()); } } }); } @Override public void removeFirewall(String applianceVmUuid, String l3uuid, List<ApplianceVmFirewallRuleInventory> rules, final Completion completion) { removeFirewall(applianceVmUuid, l3uuid, rules, true, completion); } @Override @MessageSafe public void handleMessage(Message msg) { if (msg instanceof APIMessage) { handleApiMessage((APIMessage)msg); } else { handleLocalMessage(msg); } } private void handleLocalMessage(Message msg) { bus.dealWithUnknownMessage(msg); } private void handleApiMessage(APIMessage msg) { if (msg instanceof APIListApplianceVmMsg) { handle((APIListApplianceVmMsg) msg); } else { bus.dealWithUnknownMessage(msg); } } private void handle(APIListApplianceVmMsg msg) { List<ApplianceVmVO> vos = dbf.listAll(ApplianceVmVO.class); List<ApplianceVmInventory> invs = ApplianceVmInventory.valueOf1(vos); APIListApplianceVmReply reply = new APIListApplianceVmReply(); reply.setInventories(invs); bus.reply(msg, reply); } @Override public String getId() { return bus.makeLocalServiceId(ApplianceVmConstant.SERVICE_ID); } }