6bf7a5ba2eb1801f0633621418f364600fc2c2bc
[netvirt.git] / vpnservice / 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.opendaylight.controller.md.sal.binding.api.DataBroker;
23 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
24 import org.opendaylight.genius.mdsalutil.MDSALUtil;
25 import org.opendaylight.netvirt.sfc.classifier.providers.GeniusProvider;
26 import org.opendaylight.netvirt.sfc.classifier.providers.NetvirtProvider;
27 import org.opendaylight.netvirt.sfc.classifier.providers.OpenFlow13Provider;
28 import org.opendaylight.netvirt.sfc.classifier.providers.SfcProvider;
29 import org.opendaylight.netvirt.sfc.classifier.service.domain.ClassifierEntry;
30 import org.opendaylight.netvirt.sfc.classifier.service.domain.api.ClassifierRenderableEntry;
31 import org.opendaylight.netvirt.sfc.classifier.service.domain.api.ClassifierState;
32 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
33 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.sfc.sff.logical.rev160620.DpnIdType;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.AccessLists;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.Acl;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.AccessListEntries;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.Ace;
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.Matches;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.sfc.acl.rev150105.NetvirtsfcAclActions;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.sfc.acl.rev150105.NeutronNetwork;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.sfc.acl.rev150105.NeutronPorts;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.sfc.acl.rev150105.RedirectToSfc;
45 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 public class ConfigurationClassifierImpl implements ClassifierState {
50
51     private final SfcProvider sfcProvider;
52     private final DataBroker dataBroker;
53     private final GeniusProvider geniusProvider;
54     private final NetvirtProvider netvirtProvider;
55     private static final Logger LOG = LoggerFactory.getLogger(ConfigurationClassifierImpl.class);
56     private static final String LOCAL_HOST_IP = "127.0.0.1";
57
58     public ConfigurationClassifierImpl(GeniusProvider geniusProvider,
59                                        NetvirtProvider netvirtProvider,
60                                        SfcProvider sfcProvider,
61                                        DataBroker dataBroker) {
62         this.geniusProvider = geniusProvider;
63         this.netvirtProvider = netvirtProvider;
64         this.sfcProvider = sfcProvider;
65         this.dataBroker = dataBroker;
66     }
67
68     @Override
69     public Set<ClassifierRenderableEntry> getAllEntries() {
70         return readAcls().stream()
71                 .map(Acl::getAccessListEntries)
72                 .filter(Objects::nonNull)
73                 .map(AccessListEntries::getAce)
74                 .filter(Objects::nonNull)
75                 .flatMap(List::stream)
76                 .map(this::getEntries)
77                 .flatMap(Set::stream)
78                 .collect(Collectors.toSet());
79     }
80
81     public List<Acl> readAcls() {
82         InstanceIdentifier<AccessLists> aclsIID = InstanceIdentifier.builder(AccessLists.class).build();
83         com.google.common.base.Optional<AccessLists> acls =
84                 MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, aclsIID);
85         LOG.trace("Acls read from datastore: {}", acls);
86         return acls.transform(AccessLists::getAcl).or(Collections.emptyList());
87     }
88
89     public Set<ClassifierRenderableEntry> getEntries(Ace ace) {
90
91         LOG.trace("Get entries for Ace {}", ace);
92
93         Matches matches = ace.getMatches();
94
95         if (matches == null) {
96             LOG.trace("Ace has no matches");
97             return Collections.emptySet();
98         }
99
100         RenderedServicePath rsp = Optional.ofNullable(ace.getActions())
101                 .map(actions -> actions.getAugmentation(RedirectToSfc.class))
102                 .map(NetvirtsfcAclActions::getRspName)
103                 .flatMap(sfcProvider::getRenderedServicePath)
104                 .orElse(null);
105
106         if (rsp == null) {
107             LOG.trace("Ace has no valid SFC redirect action");
108             return Collections.emptySet();
109         }
110
111         Long nsp = rsp.getPathId();
112         Short nsi = rsp.getStartingIndex();
113
114         if (nsp == null || nsi == null) {
115             LOG.trace("RSP has no valid NSI or NSP");
116             return Collections.emptySet();
117         }
118
119         DpnIdType firstHopDpn = sfcProvider.getFirstHopSfInterfaceFromRsp(rsp)
120                 .flatMap(geniusProvider::getDpnIdFromInterfaceName)
121                 .orElse(null);
122
123         if (firstHopDpn == null) {
124             LOG.error("RSP has no valid first hop DPN");
125             return Collections.emptySet();
126         }
127
128         List<String> interfaces = new ArrayList<>();
129         NeutronNetwork network = matches.getAugmentation(NeutronNetwork.class);
130         if (network != null) {
131             interfaces.addAll(netvirtProvider.getLogicalInterfacesFromNeutronNetwork(network));
132         }
133
134         Optional.ofNullable(matches.getAugmentation(NeutronPorts.class))
135                 .map(NeutronPorts::getSourcePortUuid)
136                 .filter(port -> !Strings.isNullOrEmpty(port) && !interfaces.contains(port))
137                 .ifPresent(interfaces::add);
138
139         if (interfaces.isEmpty()) {
140             LOG.error("Ace has no neutron entity to match against");
141             return Collections.emptySet();
142         }
143
144         Map<NodeId, List<InterfaceKey>> nodeToInterfaces = new HashMap<>();
145         for (String iface : interfaces) {
146             geniusProvider.getNodeIdFromLogicalInterface(iface).ifPresent(
147                 nodeId -> nodeToInterfaces.computeIfAbsent(nodeId, key -> new ArrayList<>()).add(
148                         new InterfaceKey(iface)));
149         }
150
151         LOG.trace("Got classifier nodes and interfaces: {}", nodeToInterfaces);
152
153         String firstHopIp = geniusProvider.getIpFromDpnId(firstHopDpn).orElse(null);
154         Set<ClassifierRenderableEntry> entries = new HashSet<>();
155         nodeToInterfaces.forEach((nodeId, ifaces) -> {
156             // Get node info
157             DpnIdType nodeDpn = new DpnIdType(OpenFlow13Provider.getDpnIdFromNodeId(nodeId));
158             String nodeIp = geniusProvider.getIpFromDpnId(nodeDpn).orElse(LOCAL_HOST_IP);
159
160             if (firstHopIp == null && !nodeDpn.equals(firstHopDpn)) {
161                 LOG.warn("Classifier on node {} has no IP to reach first hop on node {}", nodeDpn, firstHopDpn);
162                 return;
163             }
164
165             // Add entries that are not based on ingress or egress interface
166             entries.add(ClassifierEntry.buildNodeEntry(nodeId));
167             entries.add(ClassifierEntry.buildPathEntry(
168                     nodeId,
169                     nsp,
170                     nodeDpn.equals(firstHopDpn) ? null : firstHopIp));
171
172             // Add entries based on ingress interface
173             ifaces.forEach(interfaceKey -> {
174                 entries.add(ClassifierEntry.buildIngressEntry(interfaceKey));
175                 entries.add(ClassifierEntry.buildMatchEntry(
176                         nodeId,
177                         geniusProvider.getNodeConnectorIdFromInterfaceName(interfaceKey.getName()).get(),
178                         matches,
179                         nsp,
180                         nsi));
181             });
182
183             // Egress services must bind to egress ports. Since we dont know before-hand what
184             // the egress ports will be, we will bind on all switch ports. If the packet
185             // doesnt have NSH, it will be returned to the the egress dispatcher table.
186             List<String> interfaceUuidStrList = geniusProvider.getInterfacesFromNode(nodeId);
187             interfaceUuidStrList.forEach(interfaceUuidStr -> {
188                 InterfaceKey interfaceKey = new InterfaceKey(interfaceUuidStr);
189                 Optional<String> remoteIp = geniusProvider.getRemoteIpAddress(interfaceUuidStr);
190                 entries.add(ClassifierEntry.buildEgressEntry(interfaceKey, remoteIp.orElse(nodeIp)));
191             });
192         });
193
194         return entries;
195     }
196 }