2 * Copyright (c) 2017 Ericsson 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
9 package org.opendaylight.netvirt.sfc.classifier.service.domain.impl;
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.HashMap;
14 import java.util.HashSet;
15 import java.util.List;
17 import java.util.Objects;
18 import java.util.Optional;
20 import java.util.stream.Collectors;
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
23 import org.opendaylight.genius.mdsalutil.MDSALUtil;
24 import org.opendaylight.netvirt.sfc.classifier.providers.GeniusProvider;
25 import org.opendaylight.netvirt.sfc.classifier.providers.NetvirtProvider;
26 import org.opendaylight.netvirt.sfc.classifier.providers.OpenFlow13Provider;
27 import org.opendaylight.netvirt.sfc.classifier.providers.SfcProvider;
28 import org.opendaylight.netvirt.sfc.classifier.service.domain.ClassifierEntry;
29 import org.opendaylight.netvirt.sfc.classifier.service.domain.api.ClassifierRenderableEntry;
30 import org.opendaylight.netvirt.sfc.classifier.service.domain.api.ClassifierState;
31 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
32 import org.opendaylight.yang.gen.v1.urn.ericsson.params.xml.ns.yang.sfc.sff.logical.rev160620.DpnIdType;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.AccessLists;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.Acl;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.AccessListEntries;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160218.access.lists.acl.access.list.entries.Ace;
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.Matches;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.sfc.acl.rev150105.NetvirtsfcAclActions;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.sfc.acl.rev150105.NeutronNetwork;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.sfc.acl.rev150105.RedirectToSfc;
43 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
47 public class ConfigurationClassifierImpl implements ClassifierState {
49 private final SfcProvider sfcProvider;
50 private final DataBroker dataBroker;
51 private final GeniusProvider geniusProvider;
52 private final NetvirtProvider netvirtProvider;
53 private static final Logger LOG = LoggerFactory.getLogger(ConfigurationClassifierImpl.class);
54 private static final String LOCAL_HOST_IP = "127.0.0.1";
56 public ConfigurationClassifierImpl(GeniusProvider geniusProvider,
57 NetvirtProvider netvirtProvider,
58 SfcProvider sfcProvider,
59 DataBroker dataBroker) {
60 this.geniusProvider = geniusProvider;
61 this.netvirtProvider = netvirtProvider;
62 this.sfcProvider = sfcProvider;
63 this.dataBroker = dataBroker;
67 public Set<ClassifierRenderableEntry> getAllEntries() {
68 return readAcls().stream()
69 .map(Acl::getAccessListEntries)
70 .filter(Objects::nonNull)
71 .map(AccessListEntries::getAce)
72 .filter(Objects::nonNull)
73 .flatMap(List::stream)
74 .map(this::getEntries)
76 .collect(Collectors.toSet());
79 public List<Acl> readAcls() {
80 InstanceIdentifier<AccessLists> aclsIID = InstanceIdentifier.builder(AccessLists.class).build();
81 com.google.common.base.Optional<AccessLists> acls =
82 MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, aclsIID);
83 LOG.trace("Acls read from datastore: {}", acls);
84 return acls.transform(AccessLists::getAcl).or(Collections.emptyList());
87 public Set<ClassifierRenderableEntry> getEntries(Ace ace) {
89 LOG.trace("Get entries for Ace {}", ace);
91 Matches matches = ace.getMatches();
93 if (matches == null) {
94 LOG.trace("Ace has no matches");
95 return Collections.emptySet();
98 RenderedServicePath rsp = Optional.ofNullable(ace.getActions())
99 .map(actions -> actions.getAugmentation(RedirectToSfc.class))
100 .map(NetvirtsfcAclActions::getRspName)
101 .flatMap(sfcProvider::getRenderedServicePath)
105 LOG.trace("Ace has no valid SFC redirect action");
106 return Collections.emptySet();
109 Long nsp = rsp.getPathId();
110 Short nsi = rsp.getStartingIndex();
112 if (nsp == null || nsi == null) {
113 LOG.trace("RSP has no valid NSI or NSP");
114 return Collections.emptySet();
117 DpnIdType firstHopDpn = sfcProvider.getFirstHopSfInterfaceFromRsp(rsp)
118 .flatMap(geniusProvider::getDpnIdFromInterfaceName)
121 if (firstHopDpn == null) {
122 LOG.error("RSP has no valid first hop DPN");
123 return Collections.emptySet();
127 Map<NodeId, List<InterfaceKey>> nodeToInterfaces = new HashMap<>();
128 NeutronNetwork neutronNetwork = matches.getAugmentation(NeutronNetwork.class);
129 if (neutronNetwork != null) {
130 for (String iface : netvirtProvider.getLogicalInterfacesFromNeutronNetwork(neutronNetwork)) {
131 geniusProvider.getNodeIdFromLogicalInterface(iface).ifPresent(
132 nodeId -> nodeToInterfaces.computeIfAbsent(nodeId, key -> new ArrayList<>()).add(
133 new InterfaceKey(iface)));
137 LOG.trace("Got classifier nodes and interfaces: {}", nodeToInterfaces);
139 String firstHopIp = geniusProvider.getIpFromDpnId(firstHopDpn).orElse(null);
140 Set<ClassifierRenderableEntry> entries = new HashSet<>();
141 nodeToInterfaces.forEach((nodeId, ifaces) -> {
143 DpnIdType nodeDpn = new DpnIdType(OpenFlow13Provider.getDpnIdFromNodeId(nodeId));
144 String nodeIp = geniusProvider.getIpFromDpnId(nodeDpn).orElse(LOCAL_HOST_IP);
146 if (firstHopIp == null && !nodeDpn.equals(firstHopDpn)) {
147 LOG.warn("Classifier on node {} has no IP to reach first hop on node {}", nodeDpn, firstHopDpn);
151 // Add entries that are not based on ingress or egress interface
152 entries.add(ClassifierEntry.buildNodeEntry(nodeId));
153 entries.add(ClassifierEntry.buildPathEntry(
156 nodeDpn.equals(firstHopDpn) ? null : firstHopIp));
158 // Add entries based on ingress interface
159 ifaces.forEach(interfaceKey -> {
160 entries.add(ClassifierEntry.buildIngressEntry(interfaceKey));
161 entries.add(ClassifierEntry.buildMatchEntry(
163 geniusProvider.getNodeConnectorIdFromInterfaceName(interfaceKey.getName()).get(),
169 // Egress services must bind to egress ports. Since we dont know before-hand what
170 // the egress ports will be, we will bind on all switch ports. If the packet
171 // doesnt have NSH, it will be returned to the the egress dispatcher table.
172 List<String> interfaceUuidStrList = geniusProvider.getInterfacesFromNode(nodeId);
173 interfaceUuidStrList.forEach(interfaceUuidStr -> {
174 InterfaceKey interfaceKey = new InterfaceKey(interfaceUuidStr);
175 Optional<String> remoteIp = geniusProvider.getRemoteIpAddress(interfaceUuidStr);
176 entries.add(ClassifierEntry.buildEgressEntry(interfaceKey, remoteIp.orElse(nodeIp)));