package org.zstack.network.service; import org.zstack.core.db.SimpleQuery; import org.zstack.core.db.SimpleQuery.Op; import org.zstack.core.notification.N; import org.zstack.header.Component; import org.zstack.header.core.Completion; import org.zstack.header.core.NoErrorCompletion; import org.zstack.header.errorcode.ErrorCode; import org.zstack.header.exception.CloudRuntimeException; import org.zstack.header.network.l3.L3NetworkInventory; import org.zstack.header.network.l3.L3NetworkVO; import org.zstack.header.network.l3.L3NetworkVO_; import org.zstack.header.network.service.DhcpStruct; import org.zstack.header.network.service.NetworkServiceDhcpBackend; import org.zstack.header.network.service.NetworkServiceProviderType; import org.zstack.header.network.service.NetworkServiceType; import org.zstack.header.vm.*; import org.zstack.header.vm.VmInstanceSpec.HostName; import org.zstack.utils.CollectionUtils; import org.zstack.utils.Utils; import org.zstack.utils.function.Function; import org.zstack.utils.logging.CLogger; import java.util.*; /** * Created with IntelliJ IDEA. * User: frank * Time: 7:53 PM * To change this template use File | Settings | File Templates. */ public class DhcpExtension extends AbstractNetworkServiceExtension implements Component, VmDefaultL3NetworkChangedExtensionPoint { private static final CLogger logger = Utils.getLogger(DhcpExtension.class); private Map<NetworkServiceProviderType, NetworkServiceDhcpBackend> dhcpBackends = new HashMap<NetworkServiceProviderType, NetworkServiceDhcpBackend>(); private final String RESULT = String.format("result.%s", DhcpExtension.class.getName()); public NetworkServiceType getNetworkServiceType() { return NetworkServiceType.DHCP; } private void doDhcp(final Iterator<Map.Entry<NetworkServiceDhcpBackend, List<DhcpStruct>>> it, final VmInstanceSpec spec, final Completion complete) { if (!it.hasNext()) { complete.success(); return; } Map.Entry<NetworkServiceDhcpBackend, List<DhcpStruct>> e = it.next(); NetworkServiceDhcpBackend bkd = e.getKey(); List<DhcpStruct> structs = e.getValue(); logger.debug(String.format("%s is applying DHCP service", bkd.getClass().getName())); bkd.applyDhcpService(structs, spec, new Completion(complete) { @Override public void success() { doDhcp(it, spec, complete); } @Override public void fail(ErrorCode errorCode) { complete.fail(errorCode); } }); } private void releaseDhcp(final Iterator<Map.Entry<NetworkServiceDhcpBackend, List<DhcpStruct>>> it, final VmInstanceSpec spec, final NoErrorCompletion completion) { if (!it.hasNext()) { completion.done(); return; } Map.Entry<NetworkServiceDhcpBackend, List<DhcpStruct>> e = it.next(); NetworkServiceDhcpBackend bkd = e.getKey(); List<DhcpStruct> structs = e.getValue(); logger.debug(String.format("%s is releasing DHCP service", bkd.getClass().getName())); bkd.releaseDhcpService(structs, spec, new NoErrorCompletion(completion) { @Override public void done() { releaseDhcp(it, spec, completion); } }); } @Override public void applyNetworkService(VmInstanceSpec spec, Map<String, Object> data, Completion complete) { Map<NetworkServiceDhcpBackend, List<DhcpStruct>> entries = workoutDhcp(spec); data.put(RESULT, entries); doDhcp(entries.entrySet().iterator(), spec, complete); } @Override public void releaseNetworkService(VmInstanceSpec spec, Map<String, Object> data, NoErrorCompletion completion) { Map<NetworkServiceDhcpBackend, List<DhcpStruct>> entries = (Map<NetworkServiceDhcpBackend, List<DhcpStruct>>) data.get(RESULT); if (entries == null) { entries = workoutDhcp(spec); } releaseDhcp(entries.entrySet().iterator(), spec, completion); } private void populateExtensions() { for (NetworkServiceDhcpBackend bkd : pluginRgty.getExtensionList(NetworkServiceDhcpBackend.class)) { NetworkServiceDhcpBackend old = dhcpBackends.get(bkd.getProviderType()); if (old != null) { throw new CloudRuntimeException(String.format("duplicate NetworkServiceDhcpBackend[%s, %s] for type[%s]", bkd.getClass().getName(), old.getClass().getName(), bkd.getProviderType())); } dhcpBackends.put(bkd.getProviderType(), bkd); } } private DhcpStruct makeDhcpStruct(VmInstanceSpec spec, final L3NetworkInventory l3) { VmNicInventory nic = null; for (VmNicInventory inv : spec.getDestNics()) { if (inv.getL3NetworkUuid().equals(l3.getUuid())) { nic = inv; break; } } DhcpStruct struct = new DhcpStruct(); struct.setGateway(nic.getGateway()); String hostname = CollectionUtils.find(spec.getHostnames(), new Function<String, HostName>() { @Override public String call(HostName arg) { return arg.getL3NetworkUuid().equals(l3.getUuid()) ? arg.getHostname() : null; } }); if (hostname != null && l3.getDnsDomain() != null) { hostname = String.format("%s.%s", hostname, l3.getDnsDomain()); } struct.setHostname(hostname); struct.setIp(nic.getIp()); struct.setDnsDomain(l3.getDnsDomain()); struct.setL3Network(l3); struct.setDefaultL3Network(spec.getVmInventory().getDefaultL3NetworkUuid() != null && spec.getVmInventory().getDefaultL3NetworkUuid().equals(l3.getUuid())); struct.setMac(nic.getMac()); struct.setNetmask(nic.getNetmask()); return struct; } private Map<NetworkServiceDhcpBackend, List<DhcpStruct>> workoutDhcp(VmInstanceSpec spec) { Map<NetworkServiceDhcpBackend, List<DhcpStruct>> map = new HashMap<NetworkServiceDhcpBackend, List<DhcpStruct>>(); Map<NetworkServiceProviderType, List<L3NetworkInventory>> providerMap = getNetworkServiceProviderMap(NetworkServiceType.DHCP, spec.getL3Networks()); for (Map.Entry<NetworkServiceProviderType, List<L3NetworkInventory>> e : providerMap.entrySet()) { NetworkServiceProviderType ptype = e.getKey(); List<DhcpStruct> lst = new ArrayList<DhcpStruct>(); for (L3NetworkInventory l3 : e.getValue()) { lst.add(makeDhcpStruct(spec, l3)); } NetworkServiceDhcpBackend bkd = dhcpBackends.get(ptype); if (bkd == null) { throw new CloudRuntimeException(String.format("unable to find NetworkServiceDhcpBackend[provider type: %s]", ptype)); } map.put(bkd, lst); if (logger.isTraceEnabled()) { logger.trace(String.format("DHCP Backend[%s] is about to apply entries: \n%s", bkd.getClass().getName(), lst)); } } return map; } @Override public boolean start() { populateExtensions(); return true; } @Override public boolean stop() { return true; } @Override public void vmDefaultL3NetworkChanged(VmInstanceInventory vm, String previousL3, String nowL3) { List<String> l3Uuids = new ArrayList<String>(); if (previousL3 != null) { l3Uuids.add(previousL3); } if (nowL3 != null) { l3Uuids.add(nowL3); } SimpleQuery<L3NetworkVO> q = dbf.createQuery(L3NetworkVO.class); q.add(L3NetworkVO_.uuid, Op.IN, l3Uuids); List<L3NetworkVO> vos = q.list(); List<L3NetworkInventory> invs = L3NetworkInventory.valueOf(vos); Map<NetworkServiceProviderType, List<L3NetworkInventory>> providerMap = getNetworkServiceProviderMap(NetworkServiceType.DHCP, invs); for (Map.Entry<NetworkServiceProviderType, List<L3NetworkInventory>> e : providerMap.entrySet()) { NetworkServiceProviderType ptype = e.getKey(); NetworkServiceDhcpBackend bkd = dhcpBackends.get(ptype); if (bkd == null) { throw new CloudRuntimeException(String.format("unable to find NetworkServiceDhcpBackend[provider type: %s]", ptype)); } bkd.vmDefaultL3NetworkChanged(vm, previousL3, nowL3, new Completion(null) { @Override public void success() { // pass } @Override public void fail(ErrorCode errorCode) { N.New(VmInstanceVO.class, vm.getUuid()).warn_("unable to change the VM[uuid:%s]'s default L3 network in the DHCP backend, %s. You may need to reboot" + " the VM to use the new default L3 network setting", vm.getUuid(), errorCode); } }); } } }