move vpnservice and cleanup poms
[netvirt.git] / sfc / classifier / impl / src / main / java / org / opendaylight / netvirt / sfc / classifier / service / domain / impl / ConfigurationClassifierImpl.java
1 /*
2  * Copyright (c) 2017 Ericsson 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.netvirt.sfc.classifier.service.domain.impl;
10
11 import com.google.common.base.Strings;
12 import java.util.ArrayList;
13 import java.util.Collections;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Objects;
19 import java.util.Optional;
20 import java.util.Set;
21 import java.util.stream.Collectors;
22 import org.eclipse.jdt.annotation.NonNull;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
25 import org.opendaylight.genius.mdsalutil.MDSALUtil;
26 import org.opendaylight.netvirt.sfc.classifier.providers.GeniusProvider;
27 import org.opendaylight.netvirt.sfc.classifier.providers.NetvirtProvider;
28 import org.opendaylight.netvirt.sfc.classifier.providers.OpenFlow13Provider;
29 import org.opendaylight.netvirt.sfc.classifier.providers.SfcProvider;
30 import org.opendaylight.netvirt.sfc.classifier.service.domain.ClassifierEntry;
31 import org.opendaylight.netvirt.sfc.classifier.service.domain.api.ClassifierRenderableEntry;
32 import org.opendaylight.netvirt.sfc.classifier.service.domain.api.ClassifierState;
33 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
34 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.sfc.sff.logical.rev160620.DpnIdType;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.AccessLists;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.Acl;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.AccessListEntries;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.Ace;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.ace.Matches;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.get.dpn._interface.list.output.Interfaces;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.sfc.acl.rev150105.NetvirtsfcAclActions;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.sfc.acl.rev150105.NeutronNetwork;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.sfc.acl.rev150105.NeutronPorts;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.sfc.acl.rev150105.RedirectToSfc;
47 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
50
51 public class ConfigurationClassifierImpl implements ClassifierState {
52
53     private final SfcProvider sfcProvider;
54     private final DataBroker dataBroker;
55     private final GeniusProvider geniusProvider;
56     private final NetvirtProvider netvirtProvider;
57     private static final Logger LOG = LoggerFactory.getLogger(ConfigurationClassifierImpl.class);
58     private static final String LOCAL_HOST_IP = "127.0.0.1";
59
60     public ConfigurationClassifierImpl(GeniusProvider geniusProvider,
61                                        NetvirtProvider netvirtProvider,
62                                        SfcProvider sfcProvider,
63                                        DataBroker dataBroker) {
64         this.geniusProvider = geniusProvider;
65         this.netvirtProvider = netvirtProvider;
66         this.sfcProvider = sfcProvider;
67         this.dataBroker = dataBroker;
68     }
69
70     @Override
71     public Set<ClassifierRenderableEntry> getAllEntries() {
72         return readAcls().stream()
73                 .map(Acl::getAccessListEntries)
74                 .filter(Objects::nonNull)
75                 .map(AccessListEntries::getAce)
76                 .filter(Objects::nonNull)
77                 .flatMap(List::stream)
78                 .map(this::getEntriesForAce)
79                 .flatMap(Set::stream)
80                 .collect(Collectors.toSet());
81     }
82
83     private List<Acl> readAcls() {
84         InstanceIdentifier<AccessLists> aclsIID = InstanceIdentifier.builder(AccessLists.class).build();
85         Optional<AccessLists> acls;
86         acls = MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, aclsIID).toJavaUtil();
87         LOG.trace("Acls read from datastore: {}", acls);
88         return acls.map(AccessLists::getAcl).orElse(Collections.emptyList());
89     }
90
91     private Set<ClassifierRenderableEntry> getEntriesForAce(Ace ace) {
92         LOG.info("Generating classifier entries for Ace: {}", ace.getRuleName());
93         LOG.trace("Ace details: {}", ace);
94
95         Optional<NetvirtsfcAclActions> sfcActions = Optional.ofNullable(ace.getActions())
96                 .map(actions -> actions.getAugmentation(RedirectToSfc.class));
97         String rspName = sfcActions.map(NetvirtsfcAclActions::getRspName).map(Strings::emptyToNull).orElse(null);
98         String sfpName = sfcActions.map(NetvirtsfcAclActions::getSfpName).map(Strings::emptyToNull).orElse(null);
99
100         if (rspName == null && sfpName == null) {
101             LOG.debug("Ace has no valid SFC redirect action, ignoring");
102             return Collections.emptySet();
103         }
104         if (rspName != null && sfpName != null) {
105             LOG.error("Ace has both a SFP and a RSP as redirect actions, ignoring as not supported");
106             return Collections.emptySet();
107         }
108
109         Matches matches = ace.getMatches();
110         if (matches == null) {
111             LOG.warn("Ace has no matches, ignoring");
112             return Collections.emptySet();
113         }
114
115         NeutronNetwork network = matches.getAugmentation(NeutronNetwork.class);
116         if (sfpName != null && network != null) {
117             LOG.error("Ace has a SFP redirect action with a neutron network match, ignoring as not supported");
118             return Collections.emptySet();
119         }
120
121         String sourcePort = Optional.ofNullable(matches.getAugmentation(NeutronPorts.class))
122                 .map(NeutronPorts::getSourcePortUuid)
123                 .map(Strings::emptyToNull)
124                 .orElse(null);
125         String destinationPort = Optional.ofNullable(matches.getAugmentation(NeutronPorts.class))
126                 .map(NeutronPorts::getDestinationPortUuid)
127                 .map(Strings::emptyToNull)
128                 .orElse(null);
129
130         if (rspName != null) {
131             return getEntriesForRspRedirect(sourcePort, destinationPort, network, rspName, matches);
132         }
133
134         return getEntriesForSfpRedirect(sourcePort, destinationPort, sfpName, matches);
135     }
136
137     private Set<ClassifierRenderableEntry> getEntriesForRspRedirect(
138             String sourcePort,
139             String destinationPort,
140             NeutronNetwork neutronNetwork,
141             String rspName,
142             Matches matches) {
143
144         List<String> interfaces = new ArrayList<>();
145         if (neutronNetwork != null) {
146             interfaces.addAll(netvirtProvider.getLogicalInterfacesFromNeutronNetwork(neutronNetwork));
147         }
148
149         RenderedServicePath rsp = sfcProvider.getRenderedServicePath(rspName).orElse(null);
150         if (rsp == null) {
151             LOG.error("RSP {} could not be read from database", rspName);
152             return Collections.emptySet();
153         }
154
155         if (rsp.isReversePath()) {
156             interfaces.add(destinationPort);
157             if (sourcePort != null) {
158                 LOG.warn("Source port ignored with redirect to reverse RSP");
159             }
160         } else {
161             if (destinationPort != null) {
162                 LOG.warn("Destination port ignored with redirect to forward RSP");
163             }
164             interfaces.add(sourcePort);
165         }
166
167         if (interfaces.isEmpty()) {
168             LOG.warn("Ace has no interfaces to match against");
169             return Collections.emptySet();
170         }
171
172         return this.buildEntries(interfaces, matches, rsp);
173     }
174
175     private Set<ClassifierRenderableEntry> getEntriesForSfpRedirect(
176             String sourcePort,
177             String destinationPort,
178             String sfpName,
179             Matches matches) {
180
181         if (sourcePort == null && destinationPort == null) {
182             LOG.warn("Ace has no interfaces to match against");
183             return Collections.emptySet();
184         }
185
186         if (Objects.equals(sourcePort, destinationPort)) {
187             LOG.error("Specifying the same source and destination port is not valid configuration");
188             return Collections.emptySet();
189         }
190
191         List<String> rspNames = sfcProvider.readServicePathState(sfpName).orElse(Collections.emptyList());
192         if (rspNames.isEmpty()) {
193             LOG.warn("There is currently no RSPs for SFP {}", sfpName);
194             return Collections.emptySet();
195         }
196
197         Set<ClassifierRenderableEntry> entries = new HashSet<>();
198         boolean haveAllRsps = false;
199         RenderedServicePath forwardRsp = null;
200         RenderedServicePath reverseRsp = null;
201         for (String anRspName : rspNames) {
202             RenderedServicePath rsp = sfcProvider.getRenderedServicePath(anRspName).orElse(null);
203             if (rsp.isReversePath() && destinationPort != null) {
204                 reverseRsp = rsp;
205                 haveAllRsps = forwardRsp != null || sourcePort == null;
206             }
207             if (!rsp.isReversePath() && sourcePort != null) {
208                 forwardRsp = rsp;
209                 haveAllRsps = reverseRsp != null || destinationPort == null;
210             }
211             if (haveAllRsps) {
212                 break;
213             }
214         }
215
216         if (reverseRsp != null) {
217             entries.addAll(this.buildEntries(Collections.singletonList(destinationPort), matches, reverseRsp));
218         } else if (destinationPort != null) {
219             LOG.warn("No reverse RSP found for SFP {} and destination port {}", sfpName, destinationPort);
220         }
221
222         if (forwardRsp != null) {
223             entries.addAll(this.buildEntries(Collections.singletonList(sourcePort), matches, forwardRsp));
224         } else if (sourcePort != null) {
225             LOG.warn("No forward RSP found for SFP {} and source port {}", sfpName, sourcePort);
226         }
227
228         return entries;
229     }
230
231     private Set<ClassifierRenderableEntry> buildEntries(
232             @NonNull List<String> interfaces,
233             @NonNull Matches matches,
234             @NonNull RenderedServicePath rsp) {
235
236         Long nsp = rsp.getPathId();
237         Short nsi = rsp.getStartingIndex();
238         Short nsl = rsp.getRenderedServicePathHop() == null ? null : (short) rsp.getRenderedServicePathHop().size();
239
240         if (nsp == null || nsi == null || nsl == null) {
241             LOG.error("RSP has no valid NSI or NSP or length");
242             return Collections.emptySet();
243         }
244
245         DpnIdType firstHopDpn = sfcProvider.getFirstHopSfInterfaceFromRsp(rsp)
246                 .flatMap(geniusProvider::getDpnIdFromInterfaceName)
247                 .orElse(null);
248
249         if (firstHopDpn == null) {
250             LOG.error("RSP has no valid first hop DPN");
251             return Collections.emptySet();
252         }
253
254         String lastHopInterface = sfcProvider.getLastHopSfInterfaceFromRsp(rsp).orElse(null);
255         if (lastHopInterface == null) {
256             LOG.error("RSP has no valid last hop interface");
257             return Collections.emptySet();
258         }
259
260         DpnIdType lastHopDpn = geniusProvider.getDpnIdFromInterfaceName(lastHopInterface).orElse(null);
261         if (lastHopDpn == null) {
262             LOG.error("RSP has no valid last hop DPN");
263             return Collections.emptySet();
264         }
265
266         Map<NodeId, List<InterfaceKey>> nodeToInterfaces = new HashMap<>();
267         for (String iface : interfaces) {
268             geniusProvider.getNodeIdFromLogicalInterface(iface).ifPresent(nodeId ->
269                     nodeToInterfaces.computeIfAbsent(nodeId, key -> new ArrayList<>()).add(new InterfaceKey(iface)));
270         }
271
272         LOG.trace("Got classifier nodes and interfaces: {}", nodeToInterfaces);
273
274         String firstHopIp = geniusProvider.getIpFromDpnId(firstHopDpn).orElse(null);
275         Set<ClassifierRenderableEntry> entries = new HashSet<>();
276         nodeToInterfaces.forEach((nodeId, ifaces) -> {
277             // Get node info
278             DpnIdType nodeDpn = new DpnIdType(OpenFlow13Provider.getDpnIdFromNodeId(nodeId));
279             String nodeIp = geniusProvider.getIpFromDpnId(nodeDpn).orElse(LOCAL_HOST_IP);
280
281             if (firstHopIp == null && !nodeDpn.equals(firstHopDpn)) {
282                 LOG.warn("Classifier on node {} has no IP to reach first hop on node {}", nodeDpn, firstHopDpn);
283                 return;
284             }
285
286             // Add entries that are not based on ingress or egress interface
287             entries.add(ClassifierEntry.buildNodeEntry(nodeId));
288             entries.add(ClassifierEntry.buildPathEntry(
289                     nodeId,
290                     nsp,
291                     nsi,
292                     nsl,
293                     nodeDpn.equals(firstHopDpn) ? null : firstHopIp));
294
295             // Add entries based on ingress interface
296             ifaces.forEach(interfaceKey -> {
297                 entries.add(ClassifierEntry.buildIngressEntry(interfaceKey));
298                 entries.add(ClassifierEntry.buildMatchEntry(
299                         nodeId,
300                         geniusProvider.getNodeConnectorIdFromInterfaceName(interfaceKey.getName()).get(),
301                         matches,
302                         nsp,
303                         nsi));
304             });
305
306             // To handle chain egress when origin, last SF and destination are on the same node,
307             // we need to bind to the SF interface so that SFC pipeline to classifier pipeline
308             // hand-off can happen through the dispatcher table
309             if (nodeDpn.equals(lastHopDpn)) {
310                 entries.add(ClassifierEntry.buildIngressEntry(new InterfaceKey(lastHopInterface)));
311             }
312
313             // Egress services must bind to egress ports. Since we dont know before-hand what
314             // the egress ports will be, we will bind on all switch ports. If the packet
315             // doesnt have NSH, it will be returned to the the egress dispatcher table.
316             List<Interfaces> interfaceUuidStrList = geniusProvider.getInterfacesFromNode(nodeId);
317             interfaceUuidStrList.forEach(interfaceUuidStr -> {
318                 InterfaceKey interfaceKey = new InterfaceKey(interfaceUuidStr.getInterfaceName());
319                 Optional<String> remoteIp = geniusProvider.getRemoteIpAddress(interfaceUuidStr.getInterfaceName());
320                 entries.add(ClassifierEntry.buildEgressEntry(interfaceKey, remoteIp.orElse(nodeIp)));
321             });
322         });
323
324         return entries;
325     }
326 }