/* * Copyright (c) 2015 Huawei, Inc and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.usc.manager; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collections; import java.util.Hashtable; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.usc.manager.api.UscConfigurationService; import org.opendaylight.usc.manager.api.UscShardService; import org.opendaylight.usc.manager.topology.UscTopologyFactory; import org.opendaylight.usc.util.UscDtoUtils; import org.opendaylight.usc.util.UscServiceUtils; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.usc.channel.rev150101.NodeId; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.usc.channel.rev150101.TerminationPointId; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.usc.channel.rev150101.TopologyId; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.usc.channel.rev150101.UscTopology; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.usc.channel.rev150101.UscTopologyBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.usc.channel.rev150101.channel.attributes.ChannelAlarm; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.usc.channel.rev150101.channel.attributes.Session; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.usc.channel.rev150101.session.attributes.SessionAlarm; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.usc.channel.rev150101.session.attributes.TerminationPoint; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.usc.channel.rev150101.topology.attributes.Channel; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.usc.channel.rev150101.topology.attributes.Node; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.usc.channel.rev150101.topology.attributes.NodeBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.usc.channel.rev150101.topology.attributes.NodeKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.usc.channel.rev150101.usc.topology.Topology; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.usc.channel.rev150101.usc.topology.TopologyBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.usc.channel.rev150101.usc.topology.TopologyKey; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.util.concurrent.FutureCallback; /** * Manager all of nodes and Channels of topology, which contains only local * controller and USC related staffs All of methods should be thread safe for * asynchronous event handler */ public class UscTopologyService { /** * controller node type string */ public static final String NODE_TYPE_CONTROLLER = "Controller"; /** * channel type */ public static final String Channel_TYPE = "type"; /** * channel channel type string */ public static final String Channel_TYPE_CHANNEL = "channel"; /** * network device node type string */ public static final String NODE_TYPE_NETWORK_DEVICE = "Device"; private static final Logger LOG = LoggerFactory.getLogger(UscTopologyService.class); private static UscTopologyService topoService = new UscTopologyService(); private Node localController; private Topology localTopology; private String localHostName; @SuppressWarnings("rawtypes") private UscShardService shardService; private UscConfigurationService configService; private long maxErrorNumber = 0; private InstanceIdentifier<Topology> topoIdentifier; private List<Channel> localChannelList = new CopyOnWriteArrayList<Channel>(); private List<Node> localNodeList = new CopyOnWriteArrayList<Node>(); private Hashtable<String, Integer> nodeReferList = new Hashtable<String, Integer>(); private boolean logError = true; private UscTopologyService() { } /** * get the unique topology service instance * * @return topology service instance */ public static UscTopologyService getInstance() { return topoService; } /** * create topology manager of USC using a given shard data manger */ public synchronized void init() { shardService = UscServiceUtils.getService(UscShardService.class); configService = UscServiceUtils.getService(UscConfigurationService.class); maxErrorNumber = configService.getConfigIntValue(UscConfigurationService.USC_MAX_ERROR_NUMER); logError = configService.isConfigAsTure(UscConfigurationService.USC_LOG_ERROR_EVENT); initLocalHostName(); TopologyBuilder topoBuilder = new TopologyBuilder(); TopologyId topoId = new TopologyId(localHostName); localTopology = topoBuilder.setTopologyId(topoId).setKey(new TopologyKey(topoId)).setNode(localNodeList) .setChannel(localChannelList).build(); topoIdentifier = UscDtoUtils.getTopologyIdentifier(localHostName); initLocalController(); localNodeList.add(localController); updateUscTopology(); } private void initLocalHostName() { try { localHostName = InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { if (LOG.isDebugEnabled()) { e.printStackTrace(); } localHostName = "Random" + Math.random() + ""; LOG.warn("Failed to get local hostname!create a random key for local controller.nodeId = " + localHostName + ", error message is " + e.getMessage()); } } private void initLocalController() { NodeBuilder nodeBuilder = new NodeBuilder(); NodeId nodeId = new NodeId(localHostName); localController = nodeBuilder.setNodeType(NODE_TYPE_CONTROLLER).setNodeId(nodeId).setKey(new NodeKey(nodeId)) .build(); } @SuppressWarnings("unchecked") private void updateUscTopology() { UscTopology UscTopology = null; if (shardService != null) { if (existLocalTopology()) { // reset since USC service were restarted. updateShard(); LOG.info("The local topology already exists in Shard, and has been reseted "); return; } UscTopology = (UscTopology) shardService.read(LogicalDatastoreType.OPERATIONAL, UscDtoUtils.getUscTopologyIdentifier()); boolean UscTopologyExist = false; if (UscTopology != null) { LOG.info("Before initialize USC root, Topologies already has " + UscTopology.getTopology().size() + ",which is set by other controller."); UscTopologyExist = true; } UscTopologyBuilder UscTopologyBuilder = new UscTopologyBuilder(); List<Topology> topoList = new ArrayList<Topology>(); topoList.add(localTopology); UscTopology = UscTopologyBuilder.setTopology(topoList).build(); if (UscTopologyExist) { shardService.merge(LogicalDatastoreType.OPERATIONAL, UscDtoUtils.getUscTopologyIdentifier(), UscTopology); } else { shardService.write(LogicalDatastoreType.OPERATIONAL, UscDtoUtils.getUscTopologyIdentifier(), UscTopology); } } else { LOG.error("The shard manager is not initialized!UscTopology can't be initialized for Shard."); } } private boolean existLocalTopology() { if (shardService != null) { @SuppressWarnings("unchecked") DataObject tmp = shardService.read(LogicalDatastoreType.OPERATIONAL, topoIdentifier); if (tmp != null) { return true; } } return false; } @SuppressWarnings("unchecked") private synchronized void updateShard() { if (shardService != null) { final CountDownLatch finished = new CountDownLatch(1); shardService.write(LogicalDatastoreType.OPERATIONAL, topoIdentifier, localTopology, new FutureCallback<Void>() { @Override public void onSuccess(final Void result) { finished.countDown(); } @Override public void onFailure(final Throwable t) { finished.countDown(); LOG.error("Failed to update topology data using shard service."); } }); // wait for shard non-synchronized operation finished for // synchronize // this operation try { finished.await(60, TimeUnit.SECONDS); } catch (InterruptedException e) { LOG.error("Thread sleep has a exception:" + e.getMessage()); } } else { LOG.error("The shard manager is not initialized!"); } } /** * get local controller node * * @return controller node */ public Node getLocalController() { return localController; } /** * get local topology * * @return local topology */ public Topology getLocalTopolgy() { return localTopology; } /** * get the whole USC topology from shard data * * @return the whole USC topology */ @SuppressWarnings({ "unchecked" }) public synchronized Topology getWholeUscTopology() { if (shardService == null) { LOG.error("UscShardService is not initialized!"); return null; } UscTopology uscTopology = (UscTopology) shardService.read(LogicalDatastoreType.OPERATIONAL, UscDtoUtils.getUscTopologyIdentifier()); if (uscTopology == null) { LOG.error("Failed to get usc topology root data."); return null; } String id = null; Topology topology = null; Topology wholeUscTopology = UscTopologyFactory.createTopology("usc", new ArrayList<Node>(), new ArrayList<Channel>()); for (Topology topo : uscTopology.getTopology()) { id = topo.getTopologyId().getValue(); topology = (Topology) shardService.read(LogicalDatastoreType.OPERATIONAL, UscDtoUtils.getTopologyIdentifier(id)); if (topology != null) { UscDtoUtils.mergeChannelList(UscDtoUtils.mergeNodeList(wholeUscTopology, topology), topology); } } return wholeUscTopology; } /** * get node through the id of node * * @param nodeId * node id * @return if node id exists than return it, other wise create a new node * using node id */ public synchronized Node getNode(String nodeId) { for (Node node : localTopology.getNode()) { if (sameNodeId(nodeId, node)) { return node; } } return null; } /** * add a node to local topology, when the node exists in node list of * topology, then only update the refer number of the node * * @param node * the adding node */ public synchronized void addNode(Node node) { if (node != null) { String nodeId = node.getKey().getNodeId().getValue(); Integer num = nodeReferList.get(nodeId); // filter same node using nodeId // Note: here only has the nodes related with current controller, // but all of nodes including nodes related with other controllers // should have unique nodeId if (num == null) { // not exsits, add node and one refer number localNodeList.add(node); nodeReferList.put(nodeId, 1); } else { // exsits,only add refer number nodeReferList.put(nodeId, num + 1); } } } /** * remove the node specified by the node id, when the refer number of the * node is more than one,then only minus one refer number. when the refer * number is only one,the will remove the node too * * @param nodeId * node id of the removing node * @return the node of the removing node,if node is not exists then return * null */ public synchronized Node removeNode(String nodeId) { if (nodeId != null && !nodeId.equals("")) { Integer num = nodeReferList.get(nodeId); Node node = getNode(nodeId); if (node == null) { LOG.warn("removeNode:Node is not found for device id = " + nodeId); return null; } if (num == null) { return null; } else if (num <= 1) { // only one, so can remove node and // node refer key localNodeList.remove(node); nodeReferList.remove(nodeId); } else { // more than one ,only minus refer number nodeReferList.put(nodeId, num - 1); } return node; } return null; } /** * check the node has same id with the specified id * * @param id * a node id * @param node * a node * @return if the node has same id with the specified id then return * true,other wise return false */ public synchronized boolean sameNodeId(String id, Node node) { return id.equals(node.getNodeId().getValue()); } /** * add a channel to topology channel list * * @param channel * the adding channel */ public synchronized void addChannel(Channel channel) { if (channel != null) { addChannelWithoutUpdateShard(channel); updateShard(); } } /** * add a channel to topology channel list without update shard * * @param channel * the adding channel */ public synchronized void addChannelWithoutUpdateShard(Channel channel) { if (channel != null) { localTopology.getChannel().add(channel); addNode(UscTopologyFactory.createNode(channel.getDestination().getDestNode().getValue(), UscTopologyService.NODE_TYPE_NETWORK_DEVICE)); } } /** * remove the channel specified by the destination id,and same time will * remove the corresponding source and destination nodes * * @param destinationId * node id of the destination node * @param type * the type of channel * @return the channel of the removing channel,if the channel related with * specified destination id is not exists then return null */ public synchronized Channel removeChannel(String destinationId, String type) { if (destinationId != null) { Channel channel = removeChannelWithoutUpdateShard(destinationId, type); if(channel != null){ updateShard(); return channel; } } return null; } /** * remove the channel specified by the destination id,and same time will * remove the corresponding source and destination nodes without update Shard * * @param destinationId * node id of the destination node * @param type * the type of channel * @return the channel of the removing channel,if the channel related with * specified destination id is not exists then return null */ public synchronized Channel removeChannelWithoutUpdateShard(String destinationId, String type) { if (destinationId != null) { Channel channel = getChannel(destinationId, type); if (channel != null) { localTopology.getChannel().remove(channel); // source controller node only add once on initializing // removeNode(channel.getSource().getSourceNode().getValue()); removeNode(channel.getDestination().getDestNode().getValue()); return channel; } else { LOG.warn("Not found specified destionation.id =" + destinationId); } } return null; } /** * update channel information, and update the shard data of local topology * * @param channel * the new channel * @return old channel */ public synchronized Channel updateChannel(Channel channel) { if (channel != null) { Channel oldChannel = removeChannelWithoutUpdateShard(channel.getDestination().getDestNode().getValue(), channel.getChannelType()); addChannelWithoutUpdateShard(channel); updateShard(); return oldChannel; } return null; } public synchronized void updateChannel(Channel channel, Session session, boolean removeFlag) { Session oldSession = getSession(channel, session.getSessionId().getValue()); if (oldSession != null) { channel.getSession().remove(oldSession); } if (!removeFlag) { channel.getSession().add(session); } String destinationId = channel.getDestination().getDestNode().getValue(); Node deviceNode = UscTopologyFactory.createNode(destinationId, UscTopologyService.NODE_TYPE_NETWORK_DEVICE); channel = UscTopologyFactory.createChannel(getLocalController(), deviceNode, channel.getKey().getChannelId() .getValue(), channel.getChannelType(), UscTopologyFactory.isCallHome(channel.getCallHome()), channel.getBytesIn(), channel.getBytesOut(), channel.getChannelAlarm(), channel.getSession()); updateChannel(channel); } public synchronized void updateTransaction(Channel channel, Session session, long bytesIn, long bytesOut) { Session oldSession = getSession(channel, session.getSessionId().getValue()); if (oldSession != null) { channel.getSession().remove(oldSession); } channel.getSession().add(session); String destinationId = channel.getDestination().getDestNode().getValue(); Node deviceNode = UscTopologyFactory.createNode(destinationId, UscTopologyService.NODE_TYPE_NETWORK_DEVICE); channel = UscTopologyFactory.createChannel(getLocalController(), deviceNode, channel.getKey().getChannelId() .getValue(), channel.getChannelType(), UscTopologyFactory.isCallHome(channel.getCallHome()), channel.getBytesIn() + bytesIn, channel.getBytesOut() + bytesOut, channel.getChannelAlarm(), channel.getSession()); updateChannel(channel); } /** * get first channel of specified destination id * * @param destinationId * destination node id * @return the channel, if the channel related with specified destination id * is not exists then return null */ public synchronized Channel getChannel(String destinationId, String type) { for (Channel channel : localTopology.getChannel()) { if (channel.getDestination().getDestNode().getValue().equals(destinationId) && channel.getChannelType().equals(type)) { return channel; } } return null; } /** * add session to the channel which has the specified destination id * * @param destinationId * destination id * @param type * channel type * @param session * the adding session */ public synchronized void addSession(String destinationId, String type, Session session) { Channel channel = getChannel(destinationId, type); if (channel != null) { updateChannel(channel, session, false); } else { LOG.warn("Not found specified destionation.id =" + destinationId); } } /** * remove session from the channel which has the specified destination id * * @param destinationId * destination id * @param type * channel type * @param sessionId * the removing session */ public synchronized Session removeSession(String destinationId, String type, String sessionId) { Channel channel = getChannel(destinationId, type); if (channel != null) { Session session = getSession(channel, sessionId); if (session != null) { updateChannel(channel, session, true); return session; } else { LOG.warn("Not found specified Session.id =" + sessionId); } } else { LOG.warn("Not found specified destionation.id =" + destinationId); } return null; } /** * get session from the channel with specified session id * * @param channel * the target channel for getting * @param sessionId * specified session id * @return if find the session which has the session id, return the session * other wise return null */ public synchronized Session getSession(Channel channel, String sessionId) { for (Session session : channel.getSession()) { if (session.getSessionId().getValue().equals(sessionId)) { return session; } } return null; } /** * get session which has specified session id from the channel which has the * destination id * * @param destinationId * the target channel for getting * @param sessionId * specified session id * @return if find the session which has the session id, return the session * other wise return null */ public synchronized Session getSession(String destinationId, String sessionId, String type) { for (Channel channel : localTopology.getChannel()) { if (channel.getDestination().getDestNode().getValue().equals(destinationId) && channel.getChannelType().equals(type)) { for (Session session : channel.getSession()) { if (session.getSessionId().getValue().equals(sessionId)) { return session; } } } } return null; } /** * update the transaction data values of the session specified by session * id, the session belongs to the channel which specified by the destination * id * * @param destinationId * specified destination id * @param sessionId * specified session id * @param type * channel type * @param bytesIn * bytes in number * @param bytesOut * bytes out number */ public synchronized void updateSessionTransaction(String destinationId, String type, String sessionId, long bytesIn, long bytesOut) { Channel channel = getChannel(destinationId, type); if (channel == null) { LOG.warn("channel is not found for device({}),type({})", destinationId, type); return; } Session session = getSession(destinationId, sessionId, type); if (session == null) { LOG.warn("Session is not found for device[" + destinationId + "] and session[" + sessionId + "]."); return; } TerminationPoint tp = session.getTerminationPoint(); if (tp == null) { LOG.warn("TerminationPoint is not found for device[" + destinationId + "] and session[" + sessionId + "]."); return; } TerminationPointId tpid = tp.getTerminationPointId(); if (tpid == null) { LOG.warn("TerminationPointId is not found for device[" + destinationId + "] and session[" + sessionId + "]."); return; } String tpPort = tpid.getValue(); session = UscTopologyFactory.createSession(sessionId, tpPort, session.getBytesIn() + bytesIn, session.getBytesOut() + bytesOut, session.getSessionAlarm()); updateTransaction(channel, session, bytesIn, bytesOut); } /** * add error information to the channel which has the specified destination * id * * @param destinationId * specified destination id * @param type * channel type * @param alarm * error information object */ public synchronized void addChannelError(String destinationId, String type, ChannelAlarm alarm) { if (alarm == null) { LOG.error("Channel Error Event: alarm is null for device id = " + destinationId); return; } if (logError) { LOG.error("Channel Error Event: device Id = " + destinationId + ",Id = " + alarm.getAlarmId().getValue() + ",Code = " + alarm.getAlarmCode() + ",Message = " + alarm.getAlarmMessage()); return; } Channel channel = getChannel(destinationId, type); if (channel == null) { LOG.warn("Channel is not found for device id = " + destinationId); return; } Node deviceNode = UscTopologyFactory.createNode(destinationId, UscTopologyService.NODE_TYPE_NETWORK_DEVICE); List<ChannelAlarm> alarmList = channel.getChannelAlarm(); addAlarm(alarmList, alarm); channel = UscTopologyFactory.createChannel(getLocalController(), deviceNode, channel.getChannelId().getValue(), channel.getChannelType(), UscTopologyFactory.isCallHome(channel.getCallHome()), channel.getBytesIn(), channel.getBytesOut(), alarmList, channel.getSession()); updateChannel(channel); } /** * add error information to the session specified by session id, the session * belongs to the channel which specified by the destination id * * @param destinationId * specified destination id * @param type * channel type * @param sessionId * specified session id * @param alarm * error information object */ public synchronized void addSessionError(String destinationId, String type, String sessionId, SessionAlarm alarm) { if (alarm == null) { LOG.error("Session Error Event: alarm is null for device id = " + destinationId + ",sessionId = " + sessionId); return; } if (logError) { LOG.error("Session Error Event: deviceId = " + destinationId + ",sessionId = " + sessionId + ",Id = " + alarm.getAlarmId().getValue() + ",Code = " + alarm.getAlarmCode() + ",Message = " + alarm.getAlarmMessage()); return; } Channel channel = getChannel(destinationId, type); if (channel == null) { LOG.warn("Channel is not found for device id = " + destinationId); return; } Session session = getSession(destinationId, sessionId, type); if (session == null) { LOG.warn("Session is not found for device[" + destinationId + "] and session[" + sessionId + "]."); return; } List<SessionAlarm> alarmList = session.getSessionAlarm(); addAlarm(alarmList, alarm); session = UscTopologyFactory.createSession(sessionId, session.getTerminationPoint().getTerminationPointId() .getValue(), session.getBytesIn(), session.getBytesOut(), alarmList); updateChannel(channel, session, false); } @SuppressWarnings({ "rawtypes", "unchecked" }) private void addAlarm(List list, Object alarm) { if (list == null) { list = Collections.synchronizedList(new LinkedList()); } // control the max number of errors for (int i = 0; i < list.size() - maxErrorNumber; i++) { ((LinkedList) list).pop(); } list.add(alarm); } /** * remove all of topology manager used shard data */ @SuppressWarnings("unchecked") public synchronized void destory() { if (shardService != null) { // remove all of shard data used by USC UscTopology uscTopology = (UscTopology) shardService.read(LogicalDatastoreType.OPERATIONAL, UscDtoUtils.getUscTopologyIdentifier()); for (Topology topo : uscTopology.getTopology()) { shardService.delete(LogicalDatastoreType.OPERATIONAL, UscDtoUtils.getTopologyIdentifier(topo.getTopologyId().getValue())); } shardService.delete(LogicalDatastoreType.OPERATIONAL, UscDtoUtils.getUscTopologyIdentifier()); } } }