Split off useful utilities into InstanceIdentifierUtil
[bgpcep.git] / pcep / tunnel-provider / src / main / java / org / opendaylight / bgpcep / pcep / tunnel / provider / NodeChangedListener.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.bgpcep.pcep.tunnel.provider;
9
10 import java.util.HashSet;
11 import java.util.Map;
12 import java.util.Set;
13
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;
63
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;
69
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;
74
75         NodeChangedListener(final DataProviderService dataProvider, final InstanceIdentifier<Topology> target) {
76                 this.dataProvider = Preconditions.checkNotNull(dataProvider);
77                 this.target = Preconditions.checkNotNull(target);
78         }
79
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);
84                 if (li == null) {
85                         final InstanceIdentifier<Node> ni = InstanceIdentifierUtil.firstIdentifierOf(i, Node.class);
86                         if (ni == null) {
87                                 LOG.warn("Ignoring uncategorized identifier {}", i);
88                         } else {
89                                 changedNodes.add(ni);
90                         }
91                 } else {
92                         changedLsps.add(li);
93                 }
94         }
95
96         private static void enumerateLsps(final InstanceIdentifier<Node> id, final Node node, final Set<InstanceIdentifier<ReportedLsps>> lsps) {
97                 if (node == null) {
98                         LOG.trace("Skipping null node", id);
99                         return;
100                 }
101                 final Node1 pccnode = node.getAugmentation(Node1.class);
102                 if (pccnode == null) {
103                         LOG.trace("Skipping non-PCEP-enabled node {}", id);
104                         return;
105                 }
106
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());
112                 }
113         }
114
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());
117         }
118
119         private InstanceIdentifier<Link> linkForLsp(final LinkId linkId) {
120                 return InstanceIdentifier.builder(target).child(Link.class, new LinkKey(linkId)).toInstance();
121         }
122
123         private SupportingNode createSupportingNode(final NodeId sni, final Boolean inControl) {
124                 final SupportingNodeKey sk = new SupportingNodeKey(sni);
125                 final SupportingNodeBuilder snb = new SupportingNodeBuilder();
126                 snb.setNodeRef(sni);
127                 snb.setKey(sk);
128                 snb.addAugmentation(SupportingNode1.class, new SupportingNode1Builder().setPathComputationClient(
129                                 new PathComputationClientBuilder().setControlling(inControl).build()).build());
130
131                 return snb.build();
132         }
133
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);
138
139                                 if (tpa != null) {
140                                         final TerminationPointType tpt = tpa.getIgpTerminationPointAttributes().getTerminationPointType();
141
142                                         if (tpt instanceof Ip) {
143                                                 for (final IpAddress a : ((Ip) tpt).getIpAddress()) {
144                                                         if (addr.equals(a.getIpv6Address())) {
145                                                                 if (sni != null) {
146                                                                         final NodeKey k = InstanceIdentifierUtil.keyOf(sni);
147                                                                         boolean have = false;
148
149                                                                         /*
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,
152                                                                          * fill it in.
153                                                                          */
154                                                                         for (SupportingNode sn : n.getSupportingNode()) {
155                                                                                 if (sn.getNodeRef().equals(k.getNodeId())) {
156                                                                                         have = true;
157                                                                                         break;
158                                                                                 }
159                                                                         }
160
161                                                                         if (!have) {
162                                                                                 final SupportingNode sn = createSupportingNode(k.getNodeId(), inControl);
163
164                                                                                 trans.putOperationalData(InstanceIdentifier.builder(target).
165                                                                                                 child(Node.class, n.getKey()).
166                                                                                                 child(SupportingNode.class, sn.getKey()).toInstance(), sn);
167                                                                         }
168                                                                 }
169                                                                 return InstanceIdentifier.builder(target).child(Node.class, n.getKey()).child(TerminationPoint.class, tp.getKey()).toInstance();
170                                                         }
171                                                 }
172                                         } else {
173                                                 LOG.debug("Ignoring termination point type {}", tpt);
174                                         }
175                                 }
176                         }
177                 }
178
179                 LOG.debug("Termination point for {} not found, creating a new one", addr);
180
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());
190
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()));
195                 if (sni != null) {
196                         nb.setSupportingNode(Lists.newArrayList(createSupportingNode(InstanceIdentifierUtil.keyOf(sni).getNodeId(), inControl)));
197                 }
198
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();
201         }
202
203         private void create(final DataModificationTransaction trans,
204                         final InstanceIdentifier<ReportedLsps> i, final ReportedLsps value) {
205
206                 final InstanceIdentifier<Node> ni = InstanceIdentifierUtil.firstIdentifierOf(i, Node.class);
207                 final AddressFamily af = value.getLsp().getTlvs().getLspIdentifiers().getAddressFamily();
208
209                 /*
210                  * We are trying to ensure we have source and destination nodes.
211                  */
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());
221                 } else {
222                         throw new IllegalArgumentException("Unsupported address family: " + af.getImplementedInterface());
223                 }
224
225                 final InstanceIdentifier<TerminationPoint> src = getIpTerminationPoint(trans, srcIp, ni, value.getLsp().isDelegate());
226                 final InstanceIdentifier<TerminationPoint> dst = getIpTerminationPoint(trans, dstIp, null, Boolean.FALSE);
227
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());
234
235                 final LinkId id = linkIdForLsp(i, value);
236                 final LinkBuilder lb = new LinkBuilder();
237                 lb.setLinkId(id);
238
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());
246
247                 trans.putOperationalData(linkForLsp(id), lb.build());
248         }
249
250         private void remove(final DataModificationTransaction trans,
251                         final InstanceIdentifier<ReportedLsps> i, final ReportedLsps value) {
252                 final InstanceIdentifier<Link> li = linkForLsp(linkIdForLsp(i, value));
253
254                 final Link l = (Link)trans.readOperationalData(li);
255                 if (l != null) {
256                         trans.removeOperationalData(li);
257
258                         // FIXME: clean up/garbage collect nodes/termination types
259                 }
260         }
261
262         @Override
263         public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
264                 final DataModificationTransaction trans = dataProvider.beginTransaction();
265
266                 final Set<InstanceIdentifier<ReportedLsps>> lsps = new HashSet<>();
267                 final Set<InstanceIdentifier<Node>> nodes = new HashSet<>();
268
269                 // Categorize reported identifiers
270                 for (final InstanceIdentifier<?> i : change.getRemovedOperationalData()) {
271                         categorizeIdentifier(i, lsps, nodes);
272                 }
273                 for (final InstanceIdentifier<?> i : change.getUpdatedOperationalData().keySet()) {
274                         categorizeIdentifier(i, lsps, nodes);
275                 }
276                 for (final InstanceIdentifier<?> i : change.getCreatedOperationalData().keySet()) {
277                         categorizeIdentifier(i, lsps, nodes);
278                 }
279
280                 // Get the subtrees
281                 final Map<InstanceIdentifier<?>, DataObject> o = change.getOriginalOperationalData();
282                 final Map<InstanceIdentifier<?>, DataObject> n = change.getUpdatedOperationalData();
283
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);
288                 }
289
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);
294
295                         LOG.debug("Updating lsp {} value {} -> {}", i, oldValue, newValue);
296                         if (oldValue != null) {
297                                 remove(trans, i, oldValue);
298                         }
299                         if (newValue != null) {
300                                 create(trans, i, newValue);
301                         }
302                 }
303
304                 Futures.addCallback(JdkFutureAdapters.listenInPoolThread(trans.commit()),
305                                 new FutureCallback<RpcResult<TransactionStatus>>() {
306                         @Override
307                         public void onSuccess(final RpcResult<TransactionStatus> result) {
308                                 // Nothing to do
309                         }
310
311                         @Override
312                         public void onFailure(final Throwable t) {
313                                 LOG.error("Failed to propagate a topology change, target topology became inconsistent", t);
314                         }
315                 });
316         }
317
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();
322         }
323 }