BUG-109: implementation
[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.List;
12 import java.util.Map;
13 import java.util.Set;
14 import java.util.concurrent.ExecutionException;
15 import java.util.concurrent.Future;
16
17 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
18 import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
19 import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
20 import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
21 import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
22 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.SymbolicPathName;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.endpoints.address.family.Ipv4;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.endpoints.address.family.Ipv6;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.lsp.identifiers.tlv.lsp.identifiers.AddressFamily;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.Node1;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.pcep.client.attributes.PathComputationClient;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev131024.pcep.client.attributes.path.computation.client.ReportedLsps;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.AdministrativeStatus;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.Link1;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.Link1Builder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.SupportingNode1;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.SupportingNode1Builder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.rev130820.tunnel.pcep.supporting.node.attributes.PathComputationClientBuilder;
36 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.LinkId;
37 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
38 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TpId;
39 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.DestinationBuilder;
40 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.link.attributes.SourceBuilder;
41 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
42 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
43 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkBuilder;
44 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.LinkKey;
45 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
46 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
47 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
48 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
49 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointBuilder;
50 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey;
51 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.node.attributes.SupportingNode;
52 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.node.attributes.SupportingNodeBuilder;
53 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.node.attributes.SupportingNodeKey;
54 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.TerminationPoint1;
55 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.TerminationPoint1Builder;
56 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.igp.termination.point.attributes.IgpTerminationPointAttributesBuilder;
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.TerminationPointType;
58 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;
59 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;
60 import org.opendaylight.yangtools.yang.binding.DataObject;
61 import org.opendaylight.yangtools.yang.binding.Identifiable;
62 import org.opendaylight.yangtools.yang.binding.Identifier;
63 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
64 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
65 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
66 import org.opendaylight.yangtools.yang.common.RpcResult;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
69
70 import com.google.common.base.Preconditions;
71 import com.google.common.collect.Iterables;
72 import com.google.common.collect.Lists;
73
74 final class NodeChangedListener implements DataChangeListener {
75         private static final Logger LOG = LoggerFactory.getLogger(NodeChangedListener.class);
76         private final InstanceIdentifier<Topology> target;
77         private final DataProviderService dataProvider;
78
79         NodeChangedListener(final DataProviderService dataProvider, final InstanceIdentifier<Topology> target) {
80                 this.dataProvider = Preconditions.checkNotNull(dataProvider);
81                 this.target = Preconditions.checkNotNull(target);
82         }
83
84         private static <T extends DataObject> InstanceIdentifier<T> firstIdentifierOf(final InstanceIdentifier<?> id, final Class<T> type) {
85                 final List<PathArgument> p = id.getPath();
86
87                 int i = 1;
88                 for (final PathArgument a : p) {
89                         if (type.equals(a.getType())) {
90                                 new InstanceIdentifier<>(p.subList(0, i), type);
91                         }
92
93                         ++i;
94                 }
95
96                 LOG.debug("Identifier {} does not contain type {}", id, type);
97                 return null;
98         }
99
100         private static <N extends Identifiable<K> & DataObject, K extends Identifier<N>> K firstKeyOf(final InstanceIdentifier<?> id,  final Class<N> listItem, final Class<K> listKey) {
101                 for (PathArgument i : id.getPath()) {
102                         if (i.getType().equals(listItem)) {
103                                 @SuppressWarnings("unchecked")
104                                 final K ret = ((IdentifiableItem<N, K>)i).getKey();
105                                 return ret;
106                         }
107                 }
108
109                 LOG.debug("Identifier {} does not contain type {}", id, listItem);
110                 return null;
111         }
112
113         private static <N extends Identifiable<K> & DataObject, K extends Identifier<N>> K keyOf(final InstanceIdentifier<N> id) {
114                 @SuppressWarnings("unchecked")
115                 final K ret = ((IdentifiableItem<N, K>)Iterables.getLast(id.getPath())).getKey();
116                 return ret;
117         }
118
119         private static void categorizeIdentifier(final InstanceIdentifier<?> i,
120                         final Set<InstanceIdentifier<ReportedLsps>> changedLsps,
121                         final Set<InstanceIdentifier<Node>> changedNodes) {
122                 final InstanceIdentifier<ReportedLsps> li = firstIdentifierOf(i, ReportedLsps.class);
123                 if (li == null) {
124                         final InstanceIdentifier<Node> ni = firstIdentifierOf(i, Node.class);
125                         if (ni == null) {
126                                 LOG.warn("Ignoring uncategorized identifier {}", i);
127                         } else {
128                                 changedNodes.add(ni);
129                         }
130                 } else {
131                         changedLsps.add(li);
132                 }
133         }
134
135         private static void enumerateLsps(final InstanceIdentifier<Node> id, final Node node, final Set<InstanceIdentifier<ReportedLsps>> lsps) {
136                 if (node == null) {
137                         LOG.trace("Skipping null node", id);
138                         return;
139                 }
140                 final Node1 pccnode = node.getAugmentation(Node1.class);
141                 if (pccnode == null) {
142                         LOG.trace("Skipping non-PCEP-enabled node {}", id);
143                         return;
144                 }
145
146                 for (final ReportedLsps l : pccnode.getPathComputationClient().getReportedLsps()) {
147                         lsps.add(InstanceIdentifier.builder(id).
148                                         augmentation(Node1.class).
149                                         child(PathComputationClient.class).
150                                         child(ReportedLsps.class, l.getKey()).toInstance());
151                 }
152         }
153
154         private static LinkId linkIdForLsp(final InstanceIdentifier<ReportedLsps> i, final ReportedLsps lsp) {
155                 return new LinkId(firstKeyOf(i, Node.class, NodeKey.class).getNodeId().getValue() + "/lsps/" + lsp.getName());
156         }
157
158         private InstanceIdentifier<Link> linkForLsp(final LinkId linkId) {
159                 return InstanceIdentifier.builder(target).child(Link.class, new LinkKey(linkId)).toInstance();
160         }
161
162         private SupportingNode createSupportingNode(final NodeId sni, final Boolean inControl) {
163                 final SupportingNodeKey sk = new SupportingNodeKey(sni);
164                 final SupportingNodeBuilder snb = new SupportingNodeBuilder();
165                 snb.setNodeRef(sni);
166                 snb.setKey(sk);
167                 snb.addAugmentation(SupportingNode1.class, new SupportingNode1Builder().setPathComputationClient(
168                                 new PathComputationClientBuilder().setControlling(inControl).build()).build());
169
170                 return snb.build();
171         }
172
173         private InstanceIdentifier<TerminationPoint> getIpTerminationPoint(final DataModificationTransaction trans, final IpAddress addr, final InstanceIdentifier<Node> sni, final Boolean inControl) {
174                 for (final Node n : ((Topology) trans.readOperationalData(target)).getNode()) {
175                         for (final TerminationPoint tp : n.getTerminationPoint()) {
176                                 final TerminationPoint1 tpa = tp.getAugmentation(TerminationPoint1.class);
177
178                                 if (tpa != null) {
179                                         final TerminationPointType tpt = tpa.getIgpTerminationPointAttributes().getTerminationPointType();
180
181                                         if (tpt instanceof Ip) {
182                                                 for (final IpAddress a : ((Ip) tpt).getIpAddress()) {
183                                                         if (addr.equals(a.getIpv6Address())) {
184                                                                 if (sni != null) {
185                                                                         final NodeKey k = keyOf(sni);
186                                                                         boolean have = false;
187
188                                                                         /*
189                                                                          * We may have found a termination point which has been created as a destination,
190                                                                          * so it does not have a supporting node pointer. Since we now know what it is,
191                                                                          * fill it in.
192                                                                          */
193                                                                         for (SupportingNode sn : n.getSupportingNode()) {
194                                                                                 if (sn.getNodeRef().equals(k.getNodeId())) {
195                                                                                         have = true;
196                                                                                         break;
197                                                                                 }
198                                                                         }
199
200                                                                         if (!have) {
201                                                                                 final SupportingNode sn = createSupportingNode(k.getNodeId(), inControl);
202
203                                                                                 trans.putOperationalData(InstanceIdentifier.builder(target).
204                                                                                                 child(Node.class, n.getKey()).
205                                                                                                 child(SupportingNode.class, sn.getKey()).toInstance(), sn);
206                                                                         }
207                                                                 }
208                                                                 return InstanceIdentifier.builder(target).child(Node.class, n.getKey()).child(TerminationPoint.class, tp.getKey()).toInstance();
209                                                         }
210                                                 }
211                                         } else {
212                                                 LOG.debug("Ignoring termination point type {}", tpt);
213                                         }
214                                 }
215                         }
216                 }
217
218                 LOG.debug("Termination point for {} not found, creating a new one", addr);
219
220                 final String url = "ip://" + addr.toString();
221                 final TerminationPointKey tpk = new TerminationPointKey(new TpId(url));
222                 final TerminationPointBuilder tpb = new TerminationPointBuilder();
223                 tpb.setKey(tpk).setTpId(tpk.getTpId());
224                 tpb.addAugmentation(TerminationPoint1.class,
225                                 new TerminationPoint1Builder().
226                                 setIgpTerminationPointAttributes(
227                                                 new IgpTerminationPointAttributesBuilder().
228                                                 setTerminationPointType(new IpBuilder().setIpAddress(Lists.newArrayList(addr)).build()).build()).build());
229
230                 final NodeKey nk = new NodeKey(new NodeId(url));
231                 final NodeBuilder nb = new NodeBuilder();
232                 nb.setKey(nk).setNodeId(nk.getNodeId());
233                 nb.setTerminationPoint(Lists.newArrayList(tpb.build()));
234                 if (sni != null) {
235                         nb.setSupportingNode(Lists.newArrayList(createSupportingNode(keyOf(sni).getNodeId(), inControl)));
236                 }
237
238                 trans.putOperationalData(InstanceIdentifier.builder(target).child(Node.class, nb.getKey()).toInstance(), nb.build());
239                 return InstanceIdentifier.builder(target).child(Node.class, nb.getKey()).child(TerminationPoint.class, tpb.getKey()).toInstance();
240         }
241
242         private void create(final DataModificationTransaction trans,
243                         final InstanceIdentifier<ReportedLsps> i, final ReportedLsps value) {
244
245                 final InstanceIdentifier<Node> ni = firstIdentifierOf(i, Node.class);
246                 final AddressFamily af = value.getLsp().getTlvs().getLspIdentifiers().getAddressFamily();
247
248                 /*
249                  * We are trying to ensure we have source and destination nodes.
250                  */
251                 final IpAddress srcIp, dstIp;
252                 if (af instanceof Ipv4) {
253                         final Ipv4 ipv4 = (Ipv4) af;
254                         srcIp = new IpAddress(ipv4.getSourceIpv4Address());
255                         dstIp = new IpAddress(ipv4.getDestinationIpv4Address());
256                 } else if (af instanceof Ipv6) {
257                         final Ipv6 ipv6 = (Ipv6) af;
258                         srcIp = new IpAddress(ipv6.getSourceIpv6Address());
259                         dstIp = new IpAddress(ipv6.getDestinationIpv6Address());
260                 } else {
261                         throw new IllegalArgumentException("Unsupported address family: " + af.getImplementedInterface());
262                 }
263
264                 final InstanceIdentifier<TerminationPoint> src = getIpTerminationPoint(trans, srcIp, ni, value.getLsp().isDelegate());
265                 final InstanceIdentifier<TerminationPoint> dst = getIpTerminationPoint(trans, dstIp, null, Boolean.FALSE);
266
267                 final Link1Builder lab = new Link1Builder(value.getPath().getLspa());
268                 lab.setAdministrativeStatus(value.getLsp().isAdministrative() ? AdministrativeStatus.Active : AdministrativeStatus.Inactive);
269                 lab.setBandwidth(value.getPath().getBandwidth().getBandwidth());
270                 lab.setClassType(value.getPath().getClassType().getClassType());
271                 lab.setSymbolicPathName(value.getName());
272                 lab.setOperationalStatus(value.getLsp().getOperational());
273
274                 final LinkId id = linkIdForLsp(i, value);
275                 final LinkBuilder lb = new LinkBuilder();
276                 lb.setLinkId(id);
277
278                 lb.setSource(new SourceBuilder().
279                                 setSourceNode(firstKeyOf(src, Node.class, NodeKey.class).getNodeId()).
280                                 setSourceTp(firstKeyOf(src, TerminationPoint.class, TerminationPointKey.class).getTpId()).build());
281                 lb.setDestination(new DestinationBuilder().
282                                 setDestNode(firstKeyOf(dst, Node.class, NodeKey.class).getNodeId()).
283                                 setDestTp(firstKeyOf(dst, TerminationPoint.class, TerminationPointKey.class).getTpId()).build());
284                 lb.addAugmentation(Link1.class, lab.build());
285
286                 trans.putOperationalData(linkForLsp(id), lb.build());
287         }
288
289         private void remove(final DataModificationTransaction trans,
290                         final InstanceIdentifier<ReportedLsps> i, final ReportedLsps value) {
291                 final InstanceIdentifier<Link> li = linkForLsp(linkIdForLsp(i, value));
292
293                 final Link l = (Link)trans.readOperationalData(li);
294                 if (l != null) {
295                         trans.removeOperationalData(li);
296
297                         // FIXME: clean up/garbage collect nodes/termination types
298                 }
299         }
300
301         @Override
302         public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
303                 final DataModificationTransaction trans = dataProvider.beginTransaction();
304
305                 final Set<InstanceIdentifier<ReportedLsps>> lsps = new HashSet<>();
306                 final Set<InstanceIdentifier<Node>> nodes = new HashSet<>();
307
308                 // Categorize reported identifiers
309                 for (final InstanceIdentifier<?> i : change.getRemovedOperationalData()) {
310                         categorizeIdentifier(i, lsps, nodes);
311                 }
312                 for (final InstanceIdentifier<?> i : change.getUpdatedOperationalData().keySet()) {
313                         categorizeIdentifier(i, lsps, nodes);
314                 }
315                 for (final InstanceIdentifier<?> i : change.getCreatedOperationalData().keySet()) {
316                         categorizeIdentifier(i, lsps, nodes);
317                 }
318
319                 // Get the subtrees
320                 final Map<InstanceIdentifier<?>, DataObject> o = change.getOriginalOperationalData();
321                 final Map<InstanceIdentifier<?>, DataObject> n = change.getUpdatedOperationalData();
322
323                 // Now walk all nodes, check for removals/additions and cascade them to LSPs
324                 for (final InstanceIdentifier<Node> i : nodes) {
325                         enumerateLsps(i, (Node) o.get(i), lsps);
326                         enumerateLsps(i, (Node) n.get(i), lsps);
327                 }
328
329                 // We now have list of all affected LSPs. Walk them create/remove them
330                 for (final InstanceIdentifier<ReportedLsps> i : lsps) {
331                         final ReportedLsps oldValue = (ReportedLsps) o.get(i);
332                         final ReportedLsps newValue = (ReportedLsps) n.get(i);
333
334                         LOG.debug("Updating lsp {} value {} -> {}", i, oldValue, newValue);
335                         if (oldValue != null) {
336                                 remove(trans, i, oldValue);
337                         }
338                         if (newValue != null) {
339                                 create(trans, i, newValue);
340                         }
341                 }
342
343                 final Future<RpcResult<TransactionStatus>> f = trans.commit();
344
345                 // FIXME: change to a subscribtion once that is possible
346                 try {
347                         f.get();
348                 } catch (InterruptedException | ExecutionException e) {
349                         LOG.error("Failed to propagate a topology change, target topology became inconsistent", e);
350                 }
351         }
352
353         public static InstanceIdentifier<Link> linkIdentifier(final InstanceIdentifier<Topology> topology,
354                         final NodeId node, final SymbolicPathName name) {
355                 return InstanceIdentifier.builder(topology).
356                                 node(Link.class, new LinkKey(new LinkId(node.getValue() + "/lsp/" + name))).toInstance();
357         }
358 }