Bump upstreams
[bgpcep.git] / pcep / tunnel / tunnel-provider / src / main / java / org / opendaylight / bgpcep / pcep / tunnel / provider / CreateTunnelInstructionExecutor.java
1 /*
2  * Copyright (c) 2015 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 static java.util.Objects.requireNonNull;
11
12 import com.google.common.base.Preconditions;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import java.util.Optional;
17 import java.util.Set;
18 import java.util.concurrent.ExecutionException;
19 import org.opendaylight.bgpcep.pcep.topology.spi.AbstractInstructionExecutor;
20 import org.opendaylight.bgpcep.programming.topology.TopologyProgrammingUtil;
21 import org.opendaylight.mdsal.binding.api.DataBroker;
22 import org.opendaylight.mdsal.binding.api.ReadTransaction;
23 import org.opendaylight.mdsal.binding.api.RpcService;
24 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4AddressNoZone;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6AddressNoZone;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.AdministrativeStatus;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.Arguments2Builder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.PcepCreateP2pTunnelInput1;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.lsp.object.LspBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.bandwidth.object.BandwidthBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.classtype.object.ClassTypeBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.endpoints.AddressFamily;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.endpoints.address.family.Ipv4CaseBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.endpoints.address.family.Ipv6CaseBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.endpoints.address.family.ipv4._case.Ipv4Builder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.endpoints.address.family.ipv6._case.Ipv6Builder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.endpoints.object.EndpointsObjBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.lspa.object.LspaBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.AddLsp;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.AddLspInput;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.AddLspInputBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.OperationResult;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.add.lsp.args.Arguments;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.pcep.rev220730.add.lsp.args.ArgumentsBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.pcep.programming.rev181109.PcepCreateP2pTunnelInput;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.topology.tunnel.programming.rev130930.TpReference;
49 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
50 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link;
51 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
52 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
53 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
54 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey;
55 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.nt.l3.unicast.igp.topology.rev131021.TerminationPoint1;
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.TerminationPointType;
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.Ip;
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
62 final class CreateTunnelInstructionExecutor extends AbstractInstructionExecutor {
63     private final DataBroker dataProvider;
64     private final AddLsp addLsp;
65     private final PcepCreateP2pTunnelInput p2pTunnelInput;
66
67     CreateTunnelInstructionExecutor(final PcepCreateP2pTunnelInput p2pTunnelInput, final DataBroker dataProvider,
68             final RpcService rpcService) {
69         super(p2pTunnelInput);
70         this.p2pTunnelInput = p2pTunnelInput;
71         this.dataProvider = dataProvider;
72         addLsp = rpcService.getRpc(AddLsp.class);
73     }
74
75     private static void checkLinkIsnotExistent(final InstanceIdentifier<Topology> tii,
76             final AddLspInputBuilder addLspInput, final ReadTransaction rt) {
77         final InstanceIdentifier<Link> lii = NodeChangedListener.linkIdentifier(tii, addLspInput.getNode(),
78                 addLspInput.getName());
79         try {
80             Preconditions.checkState(!rt.exists(LogicalDatastoreType.OPERATIONAL, lii).get());
81         } catch (final InterruptedException | ExecutionException e) {
82             throw new IllegalStateException("Failed to ensure link existence.", e);
83         }
84     }
85
86     private static AddressFamily buildAddressFamily(final TerminationPoint sp, final TerminationPoint dp) {
87         // We need the IGP augmentation -- it has IP addresses
88         final TerminationPoint1 sp1 = requireNonNull(sp.augmentation(TerminationPoint1.class));
89         final TerminationPoint1 dp1 = requireNonNull(dp.augmentation(TerminationPoint1.class));
90
91         // Get the types
92         final TerminationPointType spt = sp1.getIgpTerminationPointAttributes().getTerminationPointType();
93         final TerminationPointType dpt = dp1.getIgpTerminationPointAttributes().getTerminationPointType();
94
95         // The types have to match
96         Preconditions.checkArgument(spt.implementedInterface().equals(dpt.implementedInterface()));
97
98         // And they have to actually be Ip
99         final Ip sips = (Ip) spt;
100         final Ip dips = (Ip) dpt;
101
102         /*
103          * Now a bit of magic. We need to find 'like' addresses, e.g. both
104          * IPv4 or both IPv6. We are in IPv6-enabled world now, so let's
105          * prefer that.
106          */
107         Optional<AddressFamily> ret = findIpv6(sips.getIpAddress(), dips.getIpAddress());
108         if (!ret.isPresent()) {
109             ret = findIpv4(sips.getIpAddress(), dips.getIpAddress());
110         }
111
112         // We need to have a ret now
113         return ret.orElseThrow(() -> new IllegalArgumentException("Failed to find like Endpoint addresses"));
114     }
115
116     private static Optional<AddressFamily> findIpv4(final Set<IpAddress> srcs, final Set<IpAddress> dsts) {
117         for (final IpAddress sc : srcs) {
118             if (sc.getIpv4Address() != null) {
119                 for (final IpAddress dc : dsts) {
120                     if (dc.getIpv4Address() != null) {
121                         return Optional.of(new Ipv4CaseBuilder().setIpv4(new Ipv4Builder()
122                                 .setSourceIpv4Address(new Ipv4AddressNoZone(sc.getIpv4Address()))
123                                 .setDestinationIpv4Address(new Ipv4AddressNoZone(dc.getIpv4Address()))
124                                 .build()).build());
125                     }
126                 }
127             }
128         }
129
130         return Optional.empty();
131     }
132
133     private static Optional<AddressFamily> findIpv6(final Set<IpAddress> srcs, final Set<IpAddress> dsts) {
134         for (final IpAddress sc : srcs) {
135             if (sc.getIpv6Address() != null) {
136                 for (final IpAddress dc : dsts) {
137                     if (dc.getIpv6Address() != null) {
138                         return Optional.of(new Ipv6CaseBuilder().setIpv6(new Ipv6Builder()
139                                 .setSourceIpv6Address(new Ipv6AddressNoZone(sc.getIpv6Address()))
140                                 .setDestinationIpv6Address(new Ipv6AddressNoZone(dc.getIpv6Address()))
141                                 .build()).build());
142                     }
143                 }
144             }
145         }
146
147         return Optional.empty();
148     }
149
150     @Override
151     protected ListenableFuture<OperationResult> invokeOperation() {
152         try (ReadTransaction transaction = dataProvider.newReadOnlyTransaction()) {
153             AddLspInput addLspInput = createAddLspInput(transaction);
154
155             return Futures.transform(addLsp.invoke(addLspInput), RpcResult::getResult, MoreExecutors.directExecutor());
156         }
157     }
158
159     private AddLspInput createAddLspInput(final ReadTransaction transaction) {
160         final InstanceIdentifier<Topology> tii = TopologyProgrammingUtil.topologyForInput(p2pTunnelInput);
161         final TpReader dr = new TpReader(transaction, tii, p2pTunnelInput.getDestination());
162         final TerminationPoint dp = requireNonNull(dr.getTp());
163
164         final TpReader sr = new TpReader(transaction, tii, p2pTunnelInput.getSource());
165         final TerminationPoint sp = requireNonNull(sr.getTp());
166
167         final Node sn = requireNonNull(sr.getNode());
168         final AddLspInputBuilder ab = new AddLspInputBuilder();
169         ab.setNode(requireNonNull(TunelProgrammingUtil.supportingNode(sn)));
170         ab.setName(p2pTunnelInput.getSymbolicPathName());
171
172         checkLinkIsnotExistent(tii, ab, transaction);
173
174         ab.setArguments(buildArguments(sp, dp));
175         return ab.build();
176     }
177
178     private Arguments buildArguments(final TerminationPoint sp, final TerminationPoint dp) {
179         final ArgumentsBuilder args = new ArgumentsBuilder();
180         if (p2pTunnelInput.getBandwidth() != null) {
181             args.setBandwidth(new BandwidthBuilder().setBandwidth(p2pTunnelInput.getBandwidth()).build());
182         }
183         if (p2pTunnelInput.getClassType() != null) {
184             args.setClassType(new ClassTypeBuilder().setClassType(p2pTunnelInput.getClassType()).build());
185         }
186         args.setEndpointsObj(new EndpointsObjBuilder().setAddressFamily(buildAddressFamily(sp, dp)).build());
187         args.setEro(TunelProgrammingUtil.buildEro(p2pTunnelInput.getExplicitHops()));
188         args.setLspa(new LspaBuilder(p2pTunnelInput).build());
189
190         final AdministrativeStatus adminStatus = p2pTunnelInput.augmentation(PcepCreateP2pTunnelInput1.class)
191                 .getAdministrativeStatus();
192         if (adminStatus != null) {
193             args.addAugmentation(new Arguments2Builder().setLsp(new LspBuilder()
194                     .setAdministrative(adminStatus == AdministrativeStatus.Active).build()).build());
195         }
196         return args.build();
197     }
198
199     private static final class TpReader {
200         private final ReadTransaction rt;
201         private final InstanceIdentifier<Node> nii;
202         private final InstanceIdentifier<TerminationPoint> tii;
203
204         TpReader(final ReadTransaction rt, final InstanceIdentifier<Topology> topo, final TpReference ref) {
205             this.rt = requireNonNull(rt);
206
207             nii = topo.child(Node.class, new NodeKey(ref.getNode()));
208             tii = nii.child(TerminationPoint.class, new TerminationPointKey(ref.getTp()));
209         }
210
211         private DataObject read(final InstanceIdentifier<?> id) {
212             try {
213                 return rt.read(LogicalDatastoreType.OPERATIONAL, id).get().orElseThrow();
214             } catch (final InterruptedException | ExecutionException e) {
215                 throw new IllegalStateException("Failed to read data.", e);
216             }
217         }
218
219         Node getNode() {
220             return (Node) read(nii);
221         }
222
223         TerminationPoint getTp() {
224             return (TerminationPoint) read(tii);
225         }
226     }
227 }