2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.bgpcep.pcep.tunnel.provider;
10 import java.util.HashSet;
14 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
15 import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
16 import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
17 import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
18 import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
19 import org.opendaylight.protocol.concepts.InstanceIdentifierUtil;
20 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.SymbolicPathName;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.endpoints.address.family.Ipv4;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.endpoints.address.family.Ipv6;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.lsp.identifiers.tlv.lsp.identifiers.AddressFamily;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.Node1;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.pcep.client.attributes.PathComputationClient;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.pcep.client.attributes.path.computation.client.ReportedLsps;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.AdministrativeStatus;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.Link1;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.Link1Builder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.SupportingNode1;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.SupportingNode1Builder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.tunnel.pcep.supporting.node.attributes.PathComputationClientBuilder;
34 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.LinkId;
35 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
36 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId;
37 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.DestinationBuilder;
38 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.SourceBuilder;
39 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
40 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
41 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkBuilder;
42 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkKey;
43 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
44 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
45 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
46 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
47 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointBuilder;
48 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey;
49 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.node.attributes.SupportingNode;
50 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.node.attributes.SupportingNodeBuilder;
51 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.node.attributes.SupportingNodeKey;
52 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.TerminationPoint1;
53 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.TerminationPoint1Builder;
54 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.igp.termination.point.attributes.IgpTerminationPointAttributesBuilder;
55 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.igp.termination.point.attributes.igp.termination.point.attributes.TerminationPointType;
56 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.igp.termination.point.attributes.igp.termination.point.attributes.termination.point.type.Ip;
57 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.igp.termination.point.attributes.igp.termination.point.attributes.termination.point.type.IpBuilder;
58 import org.opendaylight.yangtools.yang.binding.DataObject;
59 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
60 import org.opendaylight.yangtools.yang.common.RpcResult;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
64 import com.google.common.base.Preconditions;
65 import com.google.common.collect.Lists;
66 import com.google.common.util.concurrent.FutureCallback;
67 import com.google.common.util.concurrent.Futures;
68 import com.google.common.util.concurrent.JdkFutureAdapters;
70 public final class NodeChangedListener implements DataChangeListener {
71 public static final Logger LOG = LoggerFactory.getLogger(NodeChangedListener.class);
72 private final InstanceIdentifier<Topology> target;
73 private final DataProviderService dataProvider;
75 NodeChangedListener(final DataProviderService dataProvider, final InstanceIdentifier<Topology> target) {
76 this.dataProvider = Preconditions.checkNotNull(dataProvider);
77 this.target = Preconditions.checkNotNull(target);
80 private static void categorizeIdentifier(final InstanceIdentifier<?> i,
81 final Set<InstanceIdentifier<ReportedLsps>> changedLsps,
82 final Set<InstanceIdentifier<Node>> changedNodes) {
83 final InstanceIdentifier<ReportedLsps> li = InstanceIdentifierUtil.firstIdentifierOf(i, ReportedLsps.class);
85 final InstanceIdentifier<Node> ni = InstanceIdentifierUtil.firstIdentifierOf(i, Node.class);
87 LOG.warn("Ignoring uncategorized identifier {}", i);
96 private static void enumerateLsps(final InstanceIdentifier<Node> id, final Node node, final Set<InstanceIdentifier<ReportedLsps>> lsps) {
98 LOG.trace("Skipping null node", id);
101 final Node1 pccnode = node.getAugmentation(Node1.class);
102 if (pccnode == null) {
103 LOG.trace("Skipping non-PCEP-enabled node {}", id);
107 for (final ReportedLsps l : pccnode.getPathComputationClient().getReportedLsps()) {
108 lsps.add(InstanceIdentifier.builder(id).
109 augmentation(Node1.class).
110 child(PathComputationClient.class).
111 child(ReportedLsps.class, l.getKey()).toInstance());
115 private static LinkId linkIdForLsp(final InstanceIdentifier<ReportedLsps> i, final ReportedLsps lsp) {
116 return new LinkId(InstanceIdentifierUtil.firstKeyOf(i, Node.class, NodeKey.class).getNodeId().getValue() + "/lsps/" + lsp.getName());
119 private InstanceIdentifier<Link> linkForLsp(final LinkId linkId) {
120 return InstanceIdentifier.builder(target).child(Link.class, new LinkKey(linkId)).toInstance();
123 private SupportingNode createSupportingNode(final NodeId sni, final Boolean inControl) {
124 final SupportingNodeKey sk = new SupportingNodeKey(sni);
125 final SupportingNodeBuilder snb = new SupportingNodeBuilder();
128 snb.addAugmentation(SupportingNode1.class, new SupportingNode1Builder().setPathComputationClient(
129 new PathComputationClientBuilder().setControlling(inControl).build()).build());
134 private InstanceIdentifier<TerminationPoint> getIpTerminationPoint(final DataModificationTransaction trans, final IpAddress addr, final InstanceIdentifier<Node> sni, final Boolean inControl) {
135 for (final Node n : ((Topology) trans.readOperationalData(target)).getNode()) {
136 for (final TerminationPoint tp : n.getTerminationPoint()) {
137 final TerminationPoint1 tpa = tp.getAugmentation(TerminationPoint1.class);
140 final TerminationPointType tpt = tpa.getIgpTerminationPointAttributes().getTerminationPointType();
142 if (tpt instanceof Ip) {
143 for (final IpAddress a : ((Ip) tpt).getIpAddress()) {
144 if (addr.equals(a.getIpv6Address())) {
146 final NodeKey k = InstanceIdentifierUtil.keyOf(sni);
147 boolean have = false;
150 * We may have found a termination point which has been created as a destination,
151 * so it does not have a supporting node pointer. Since we now know what it is,
154 for (SupportingNode sn : n.getSupportingNode()) {
155 if (sn.getNodeRef().equals(k.getNodeId())) {
162 final SupportingNode sn = createSupportingNode(k.getNodeId(), inControl);
164 trans.putOperationalData(InstanceIdentifier.builder(target).
165 child(Node.class, n.getKey()).
166 child(SupportingNode.class, sn.getKey()).toInstance(), sn);
169 return InstanceIdentifier.builder(target).child(Node.class, n.getKey()).child(TerminationPoint.class, tp.getKey()).toInstance();
173 LOG.debug("Ignoring termination point type {}", tpt);
179 LOG.debug("Termination point for {} not found, creating a new one", addr);
181 final String url = "ip://" + addr.toString();
182 final TerminationPointKey tpk = new TerminationPointKey(new TpId(url));
183 final TerminationPointBuilder tpb = new TerminationPointBuilder();
184 tpb.setKey(tpk).setTpId(tpk.getTpId());
185 tpb.addAugmentation(TerminationPoint1.class,
186 new TerminationPoint1Builder().
187 setIgpTerminationPointAttributes(
188 new IgpTerminationPointAttributesBuilder().
189 setTerminationPointType(new IpBuilder().setIpAddress(Lists.newArrayList(addr)).build()).build()).build());
191 final NodeKey nk = new NodeKey(new NodeId(url));
192 final NodeBuilder nb = new NodeBuilder();
193 nb.setKey(nk).setNodeId(nk.getNodeId());
194 nb.setTerminationPoint(Lists.newArrayList(tpb.build()));
196 nb.setSupportingNode(Lists.newArrayList(createSupportingNode(InstanceIdentifierUtil.keyOf(sni).getNodeId(), inControl)));
199 trans.putOperationalData(InstanceIdentifier.builder(target).child(Node.class, nb.getKey()).toInstance(), nb.build());
200 return InstanceIdentifier.builder(target).child(Node.class, nb.getKey()).child(TerminationPoint.class, tpb.getKey()).toInstance();
203 private void create(final DataModificationTransaction trans,
204 final InstanceIdentifier<ReportedLsps> i, final ReportedLsps value) {
206 final InstanceIdentifier<Node> ni = InstanceIdentifierUtil.firstIdentifierOf(i, Node.class);
207 final AddressFamily af = value.getLsp().getTlvs().getLspIdentifiers().getAddressFamily();
210 * We are trying to ensure we have source and destination nodes.
212 final IpAddress srcIp, dstIp;
213 if (af instanceof Ipv4) {
214 final Ipv4 ipv4 = (Ipv4) af;
215 srcIp = new IpAddress(ipv4.getSourceIpv4Address());
216 dstIp = new IpAddress(ipv4.getDestinationIpv4Address());
217 } else if (af instanceof Ipv6) {
218 final Ipv6 ipv6 = (Ipv6) af;
219 srcIp = new IpAddress(ipv6.getSourceIpv6Address());
220 dstIp = new IpAddress(ipv6.getDestinationIpv6Address());
222 throw new IllegalArgumentException("Unsupported address family: " + af.getImplementedInterface());
225 final InstanceIdentifier<TerminationPoint> src = getIpTerminationPoint(trans, srcIp, ni, value.getLsp().isDelegate());
226 final InstanceIdentifier<TerminationPoint> dst = getIpTerminationPoint(trans, dstIp, null, Boolean.FALSE);
228 final Link1Builder lab = new Link1Builder(value.getPath().getLspa());
229 lab.setAdministrativeStatus(value.getLsp().isAdministrative() ? AdministrativeStatus.Active : AdministrativeStatus.Inactive);
230 lab.setBandwidth(value.getPath().getBandwidth().getBandwidth());
231 lab.setClassType(value.getPath().getClassType().getClassType());
232 lab.setSymbolicPathName(value.getName());
233 lab.setOperationalStatus(value.getLsp().getOperational());
235 final LinkId id = linkIdForLsp(i, value);
236 final LinkBuilder lb = new LinkBuilder();
239 lb.setSource(new SourceBuilder().
240 setSourceNode(InstanceIdentifierUtil.firstKeyOf(src, Node.class, NodeKey.class).getNodeId()).
241 setSourceTp(InstanceIdentifierUtil.firstKeyOf(src, TerminationPoint.class, TerminationPointKey.class).getTpId()).build());
242 lb.setDestination(new DestinationBuilder().
243 setDestNode(InstanceIdentifierUtil.firstKeyOf(dst, Node.class, NodeKey.class).getNodeId()).
244 setDestTp(InstanceIdentifierUtil.firstKeyOf(dst, TerminationPoint.class, TerminationPointKey.class).getTpId()).build());
245 lb.addAugmentation(Link1.class, lab.build());
247 trans.putOperationalData(linkForLsp(id), lb.build());
250 private void remove(final DataModificationTransaction trans,
251 final InstanceIdentifier<ReportedLsps> i, final ReportedLsps value) {
252 final InstanceIdentifier<Link> li = linkForLsp(linkIdForLsp(i, value));
254 final Link l = (Link)trans.readOperationalData(li);
256 trans.removeOperationalData(li);
258 // FIXME: clean up/garbage collect nodes/termination types
263 public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
264 final DataModificationTransaction trans = dataProvider.beginTransaction();
266 final Set<InstanceIdentifier<ReportedLsps>> lsps = new HashSet<>();
267 final Set<InstanceIdentifier<Node>> nodes = new HashSet<>();
269 // Categorize reported identifiers
270 for (final InstanceIdentifier<?> i : change.getRemovedOperationalData()) {
271 categorizeIdentifier(i, lsps, nodes);
273 for (final InstanceIdentifier<?> i : change.getUpdatedOperationalData().keySet()) {
274 categorizeIdentifier(i, lsps, nodes);
276 for (final InstanceIdentifier<?> i : change.getCreatedOperationalData().keySet()) {
277 categorizeIdentifier(i, lsps, nodes);
281 final Map<InstanceIdentifier<?>, DataObject> o = change.getOriginalOperationalData();
282 final Map<InstanceIdentifier<?>, DataObject> n = change.getUpdatedOperationalData();
284 // Now walk all nodes, check for removals/additions and cascade them to LSPs
285 for (final InstanceIdentifier<Node> i : nodes) {
286 enumerateLsps(i, (Node) o.get(i), lsps);
287 enumerateLsps(i, (Node) n.get(i), lsps);
290 // We now have list of all affected LSPs. Walk them create/remove them
291 for (final InstanceIdentifier<ReportedLsps> i : lsps) {
292 final ReportedLsps oldValue = (ReportedLsps) o.get(i);
293 final ReportedLsps newValue = (ReportedLsps) n.get(i);
295 LOG.debug("Updating lsp {} value {} -> {}", i, oldValue, newValue);
296 if (oldValue != null) {
297 remove(trans, i, oldValue);
299 if (newValue != null) {
300 create(trans, i, newValue);
304 Futures.addCallback(JdkFutureAdapters.listenInPoolThread(trans.commit()),
305 new FutureCallback<RpcResult<TransactionStatus>>() {
307 public void onSuccess(final RpcResult<TransactionStatus> result) {
312 public void onFailure(final Throwable t) {
313 LOG.error("Failed to propagate a topology change, target topology became inconsistent", t);
318 public static InstanceIdentifier<Link> linkIdentifier(final InstanceIdentifier<Topology> topology,
319 final NodeId node, final SymbolicPathName name) {
320 return InstanceIdentifier.builder(topology).
321 node(Link.class, new LinkKey(new LinkId(node.getValue() + "/lsp/" + name))).toInstance();