/* * TeleStax, Open Source Cloud Communications * Copyright 2011-2017, Telestax Inc and individual contributors * by the @authors tag. * * This program is free software: you can redistribute it and/or modify * under the terms of the GNU Affero General Public License as * published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/> */ package org.mobicents.tools.heartbeat.kube; import io.fabric8.kubernetes.api.model.ContainerStatus; import io.fabric8.kubernetes.api.model.Pod; import io.fabric8.kubernetes.api.model.PodList; import io.fabric8.kubernetes.api.model.PodStatus; import io.fabric8.kubernetes.client.DefaultKubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient; import java.net.InetAddress; import java.util.List; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; import org.apache.log4j.Logger; import org.mobicents.tools.heartbeat.api.HeartbeatRequestPacket; import org.mobicents.tools.heartbeat.api.IListener; import org.mobicents.tools.heartbeat.api.IServerHeartbeatService; import org.mobicents.tools.heartbeat.api.IServerListener; import org.mobicents.tools.heartbeat.api.Node; import org.mobicents.tools.heartbeat.api.NodeShutdownRequestPacket; import org.mobicents.tools.heartbeat.api.Protocol; import org.mobicents.tools.heartbeat.api.StartRequestPacket; import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.JsonParser; /** * @author Konstantin Nosach (kostyantyn.nosach@telestax.com) */ public class ServerControllerKube implements IListener,IServerHeartbeatService<HeartbeatConfigKube> { private static Logger logger = Logger.getLogger(ServerControllerKube.class.getCanonicalName()); private IServerListener listener; private Timer timer; private KubernetesClient kube; private Gson gson = new Gson(); private JsonParser parser = new JsonParser(); private ConcurrentHashMap<String, Node> activeNodes = new ConcurrentHashMap<>(); private String lbIp; private long pullPeriod = 3000; private String nodeName = "sip-node"; @Override public void startServer() { timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { public void run() { PodList pods = kube.pods().list(); List<Pod> items = pods.getItems(); for (Pod pod : items) { if(isValidPod(pod)) { String currSessionId = getSessionId(pod); Node currNode = activeNodes.get(currSessionId); if(currNode==null) { for(ContainerStatus status : pod.getStatus().getContainerStatuses()) { if(status.getName().startsWith(nodeName)&&status.getReady()) { Node newNode = getNodeFromPod(pod); JsonObject jsonObject = parser.parse(gson.toJson(new StartRequestPacket(newNode))).getAsJsonObject(); listener.startRequestReceived(null, jsonObject); activeNodes.put(newNode.getProperties().get(Protocol.SESSION_ID), newNode); } } } else if(!isGracefulShutdown(pod) || currNode.isGracefulShutdown()) { JsonObject jsonObject = parser.parse(gson.toJson(new HeartbeatRequestPacket(currNode))).getAsJsonObject(); listener.heartbeatRequestReceived(null, jsonObject); } else { if(!currNode.isGracefulShutdown()) { currNode.setGracefulShutdown(true); JsonObject jsonObject = parser.parse(gson.toJson(new NodeShutdownRequestPacket(currNode))).getAsJsonObject(); listener.shutdownRequestReceived(null, jsonObject); } } } } } }, 2000, pullPeriod); } @Override public void stopServer() { timer.cancel(); activeNodes.clear(); kube.close(); } @Override public void init(IServerListener listener, InetAddress serverAddress, HeartbeatConfigKube config) { this.listener = listener; this.lbIp = serverAddress.getHostAddress(); this.kube = new DefaultKubernetesClient(); this.pullPeriod = config.getPullPeriod(); this.nodeName = config.getNodeName(); } @Override public void sendPacket(String ip, int parseInt) { } private Node getNodeFromPod(Pod pod) { Map<String,String> labels = pod.getMetadata().getLabels(); PodStatus status = pod.getStatus(); Node node = new Node(labels.remove(Protocol.HOST_NAME),status.getPodIP()); node.getProperties().putAll(labels); return node; } private String getSessionId(Pod pod) { return pod.getMetadata().getLabels().get(Protocol.SESSION_ID); } private boolean isValidPod(Pod pod) { return pod.getMetadata().getName().startsWith(nodeName) && pod.getStatus().getPhase().equals("Running") && hasIpOfLB(pod.getMetadata().getLabels().get(Protocol.LB_LABEL)); } private boolean isGracefulShutdown(Pod pod) { if(pod.getMetadata().getLabels().get(Protocol.GRACEFUL_SHUTDOWN)==null) return false; else return Boolean.parseBoolean(pod.getMetadata().getLabels().get(Protocol.GRACEFUL_SHUTDOWN)); } private boolean hasIpOfLB(String lbsFromPodString) { if(lbsFromPodString==null) return true; String[] lbsFromPod = lbsFromPodString.split(","); for(String lbIpFromPod : lbsFromPod) { if(lbIpFromPod.equals(lbIp)) return true; } return false; } }