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;
11 import java.util.List;
14 import java.util.concurrent.ExecutionException;
15 import java.util.concurrent.Future;
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;
70 import com.google.common.base.Preconditions;
71 import com.google.common.collect.Iterables;
72 import com.google.common.collect.Lists;
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;
79 NodeChangedListener(final DataProviderService dataProvider, final InstanceIdentifier<Topology> target) {
80 this.dataProvider = Preconditions.checkNotNull(dataProvider);
81 this.target = Preconditions.checkNotNull(target);
84 private static <T extends DataObject> InstanceIdentifier<T> firstIdentifierOf(final InstanceIdentifier<?> id, final Class<T> type) {
85 final List<PathArgument> p = id.getPath();
88 for (final PathArgument a : p) {
89 if (type.equals(a.getType())) {
90 new InstanceIdentifier<>(p.subList(0, i), type);
96 LOG.debug("Identifier {} does not contain type {}", id, type);
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();
109 LOG.debug("Identifier {} does not contain type {}", id, listItem);
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();
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);
124 final InstanceIdentifier<Node> ni = firstIdentifierOf(i, Node.class);
126 LOG.warn("Ignoring uncategorized identifier {}", i);
128 changedNodes.add(ni);
135 private static void enumerateLsps(final InstanceIdentifier<Node> id, final Node node, final Set<InstanceIdentifier<ReportedLsps>> lsps) {
137 LOG.trace("Skipping null node", id);
140 final Node1 pccnode = node.getAugmentation(Node1.class);
141 if (pccnode == null) {
142 LOG.trace("Skipping non-PCEP-enabled node {}", id);
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());
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());
158 private InstanceIdentifier<Link> linkForLsp(final LinkId linkId) {
159 return InstanceIdentifier.builder(target).child(Link.class, new LinkKey(linkId)).toInstance();
162 private SupportingNode createSupportingNode(final NodeId sni, final Boolean inControl) {
163 final SupportingNodeKey sk = new SupportingNodeKey(sni);
164 final SupportingNodeBuilder snb = new SupportingNodeBuilder();
167 snb.addAugmentation(SupportingNode1.class, new SupportingNode1Builder().setPathComputationClient(
168 new PathComputationClientBuilder().setControlling(inControl).build()).build());
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);
179 final TerminationPointType tpt = tpa.getIgpTerminationPointAttributes().getTerminationPointType();
181 if (tpt instanceof Ip) {
182 for (final IpAddress a : ((Ip) tpt).getIpAddress()) {
183 if (addr.equals(a.getIpv6Address())) {
185 final NodeKey k = keyOf(sni);
186 boolean have = false;
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,
193 for (SupportingNode sn : n.getSupportingNode()) {
194 if (sn.getNodeRef().equals(k.getNodeId())) {
201 final SupportingNode sn = createSupportingNode(k.getNodeId(), inControl);
203 trans.putOperationalData(InstanceIdentifier.builder(target).
204 child(Node.class, n.getKey()).
205 child(SupportingNode.class, sn.getKey()).toInstance(), sn);
208 return InstanceIdentifier.builder(target).child(Node.class, n.getKey()).child(TerminationPoint.class, tp.getKey()).toInstance();
212 LOG.debug("Ignoring termination point type {}", tpt);
218 LOG.debug("Termination point for {} not found, creating a new one", addr);
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());
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()));
235 nb.setSupportingNode(Lists.newArrayList(createSupportingNode(keyOf(sni).getNodeId(), inControl)));
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();
242 private void create(final DataModificationTransaction trans,
243 final InstanceIdentifier<ReportedLsps> i, final ReportedLsps value) {
245 final InstanceIdentifier<Node> ni = firstIdentifierOf(i, Node.class);
246 final AddressFamily af = value.getLsp().getTlvs().getLspIdentifiers().getAddressFamily();
249 * We are trying to ensure we have source and destination nodes.
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());
261 throw new IllegalArgumentException("Unsupported address family: " + af.getImplementedInterface());
264 final InstanceIdentifier<TerminationPoint> src = getIpTerminationPoint(trans, srcIp, ni, value.getLsp().isDelegate());
265 final InstanceIdentifier<TerminationPoint> dst = getIpTerminationPoint(trans, dstIp, null, Boolean.FALSE);
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());
274 final LinkId id = linkIdForLsp(i, value);
275 final LinkBuilder lb = new LinkBuilder();
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());
286 trans.putOperationalData(linkForLsp(id), lb.build());
289 private void remove(final DataModificationTransaction trans,
290 final InstanceIdentifier<ReportedLsps> i, final ReportedLsps value) {
291 final InstanceIdentifier<Link> li = linkForLsp(linkIdForLsp(i, value));
293 final Link l = (Link)trans.readOperationalData(li);
295 trans.removeOperationalData(li);
297 // FIXME: clean up/garbage collect nodes/termination types
302 public void onDataChanged(final DataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
303 final DataModificationTransaction trans = dataProvider.beginTransaction();
305 final Set<InstanceIdentifier<ReportedLsps>> lsps = new HashSet<>();
306 final Set<InstanceIdentifier<Node>> nodes = new HashSet<>();
308 // Categorize reported identifiers
309 for (final InstanceIdentifier<?> i : change.getRemovedOperationalData()) {
310 categorizeIdentifier(i, lsps, nodes);
312 for (final InstanceIdentifier<?> i : change.getUpdatedOperationalData().keySet()) {
313 categorizeIdentifier(i, lsps, nodes);
315 for (final InstanceIdentifier<?> i : change.getCreatedOperationalData().keySet()) {
316 categorizeIdentifier(i, lsps, nodes);
320 final Map<InstanceIdentifier<?>, DataObject> o = change.getOriginalOperationalData();
321 final Map<InstanceIdentifier<?>, DataObject> n = change.getUpdatedOperationalData();
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);
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);
334 LOG.debug("Updating lsp {} value {} -> {}", i, oldValue, newValue);
335 if (oldValue != null) {
336 remove(trans, i, oldValue);
338 if (newValue != null) {
339 create(trans, i, newValue);
343 final Future<RpcResult<TransactionStatus>> f = trans.commit();
345 // FIXME: change to a subscribtion once that is possible
348 } catch (InterruptedException | ExecutionException e) {
349 LOG.error("Failed to propagate a topology change, target topology became inconsistent", e);
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();