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