Bulk merge of l2gw changes
[netvirt.git] / elanmanager / impl / src / main / java / org / opendaylight / netvirt / elan / l2gw / utils / StaleVlanBindingsCleaner.java
1 /*
2  * Copyright (c) 2018 Ericsson India Global Services Pvt Ltd. 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.netvirt.elan.l2gw.utils;
9
10 import static org.opendaylight.mdsal.binding.util.Datastore.CONFIGURATION;
11
12 import java.util.ArrayList;
13 import java.util.Collections;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.concurrent.ConcurrentHashMap;
18 import java.util.concurrent.ExecutionException;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.TimeUnit;
21 import java.util.function.BiPredicate;
22 import java.util.function.Function;
23 import java.util.function.Predicate;
24 import java.util.stream.Collectors;
25 import javax.inject.Inject;
26 import javax.inject.Singleton;
27 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
28 import org.opendaylight.genius.utils.hwvtep.HwvtepSouthboundUtils;
29 import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
30 import org.opendaylight.mdsal.binding.api.DataBroker;
31 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
32 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunnerImpl;
33 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
34 import org.opendaylight.netvirt.elan.cache.ElanInstanceCache;
35 import org.opendaylight.netvirt.elan.utils.Scheduler;
36 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayCache;
37 import org.opendaylight.netvirt.neutronvpn.api.l2gw.L2GatewayDevice;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.config.rev150710.ElanConfig;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l2gateways.rev150712.l2gateway.connections.attributes.l2gatewayconnections.L2gatewayConnection;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepPhysicalPortAugmentation;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.LogicalSwitches;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.physical.port.attributes.VlanBindings;
44 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
45 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
46 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
47 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
48 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 @Singleton
53 public class StaleVlanBindingsCleaner {
54
55     private static final Logger LOG = LoggerFactory.getLogger(StaleVlanBindingsCleaner.class);
56     private static final int DEFAULT_STALE_CLEANUP_DELAY_SECS = 900;
57
58     private static Function<VlanBindings, String> LOGICAL_SWITCH_FROM_BINDING =
59         (binding) -> binding.getLogicalSwitchRef().getValue().firstKeyOf(
60                 LogicalSwitches.class).getHwvtepNodeName().getValue();
61
62     private static BiPredicate<List<String>, String> IS_STALE_LOGICAL_SWITCH = (validNetworks, logicalSwitch) -> {
63         if (L2gwZeroDayConfigUtil.ZERO_DAY_LS_NAME.equals(logicalSwitch)) {
64             return false;
65         }
66         return !validNetworks.contains(logicalSwitch);
67     };
68
69     private static Predicate<TerminationPoint> CONTAINS_VLANBINDINGS = (port) ->
70             port.augmentation(HwvtepPhysicalPortAugmentation.class) != null
71                     && port.augmentation(HwvtepPhysicalPortAugmentation.class).getVlanBindings() != null;
72
73
74     private final DataBroker broker;
75     private final ManagedNewTransactionRunner txRunner;
76     private final ElanL2GatewayUtils elanL2GatewayUtils;
77     private final Scheduler scheduler;
78     private final ElanConfig elanConfig;
79     private final L2GatewayCache l2GatewayCache;
80     private final ElanInstanceCache elanInstanceCache;
81     private final Map<NodeId, ScheduledFuture> cleanupTasks = new ConcurrentHashMap<>();
82
83     @Inject
84     public StaleVlanBindingsCleaner(final DataBroker broker,
85                                     final ElanL2GatewayUtils elanL2GatewayUtils,
86                                     final Scheduler scheduler,
87                                     final ElanConfig elanConfig,
88                                     final L2GatewayCache l2GatewayCache,
89                                     final ElanInstanceCache elanInstanceCache) {
90         this.broker = broker;
91         this.txRunner = new ManagedNewTransactionRunnerImpl(broker);
92         this.elanL2GatewayUtils = elanL2GatewayUtils;
93         this.scheduler = scheduler;
94         this.elanConfig = elanConfig;
95         this.l2GatewayCache = l2GatewayCache;
96         this.elanInstanceCache = elanInstanceCache;
97     }
98
99     private long getCleanupDelay() {
100         return elanConfig.getL2gwStaleVlanCleanupDelaySecs() != null
101                 ? elanConfig.getL2gwStaleVlanCleanupDelaySecs().toJava() : DEFAULT_STALE_CLEANUP_DELAY_SECS;
102     }
103
104     public void scheduleStaleCleanup(final String deviceName,
105                                      final InstanceIdentifier<Node> globalNodeIid,
106                                      final InstanceIdentifier<Node> psNodeIid) {
107         NodeId psNodeId = psNodeIid.firstKeyOf(Node.class).getNodeId();
108         cleanupTasks.compute(psNodeId, (key, ft) -> {
109             if (ft != null) {
110                 ft.cancel(false);
111             }
112             return scheduler.getScheduledExecutorService().schedule(
113                 () -> {
114                     L2GatewayDevice l2GwDevice = l2GatewayCache.get(deviceName);
115                     NodeId globalNodeId = globalNodeIid.firstKeyOf(Node.class).getNodeId();
116                     try {
117                         Node configNode = SingleTransactionDataBroker.syncReadOptional(broker,
118                                 LogicalDatastoreType.CONFIGURATION, globalNodeIid).orElse(defaultNode(globalNodeId));
119                         Node configPsNode = SingleTransactionDataBroker.syncReadOptional(broker,
120                                 LogicalDatastoreType.CONFIGURATION, psNodeIid)
121                                         .orElse(defaultNode(psNodeId));
122                         cleanupStaleLogicalSwitches(l2GwDevice, configNode, configPsNode);
123                         cleanupTasks.remove(psNodeIid.firstKeyOf(Node.class).getNodeId());
124                     } catch (ExecutionException | InterruptedException e) {
125                         LOG.error("scheduleStaleCleanup: Exception while reading globalNodeIid/psNodeIid DS for "
126                                 + "the globalNodeIid {} psNodeIid {}", globalNodeId, psNodeId, e);
127                     }
128                 }, getCleanupDelay(), TimeUnit.SECONDS);
129         });
130     }
131
132     private static Node defaultNode(final NodeId nodeId) {
133         return new NodeBuilder().setNodeId(nodeId).build();
134     }
135
136     private void cleanupStaleLogicalSwitches(final L2GatewayDevice l2GwDevice,
137                                              final Node configNode,
138                                              final Node configPsNode) {
139         LOG.trace("Cleanup stale logical switches");
140         String globalNodeId = configNode.getNodeId().getValue();
141         List<L2gatewayConnection> connectionsOfDevice = L2GatewayConnectionUtils.getAssociatedL2GwConnections(
142                 broker, l2GwDevice.getL2GatewayIds());
143
144         List<String> validNetworks = connectionsOfDevice.stream()
145                 .map((connection) -> connection.getNetworkId().getValue())
146                 .filter(elan -> elanInstanceCache.get(elan).isPresent())
147                 .collect(Collectors.toList());
148         List<String> logicalSwitchesOnDevice = getLogicalSwitchesOnDevice(configNode);
149
150         //following condition handles:
151         //1. only stale vlan bindings present
152         //2. stale vlan bindings + stale logical switches present
153         Map<String, List<InstanceIdentifier<VlanBindings>>> vlansByLogicalSwitch = getVlansByLogicalSwitchOnDevice(
154                 configPsNode);
155         vlansByLogicalSwitch.entrySet().stream()
156                 .filter(entry -> IS_STALE_LOGICAL_SWITCH.test(validNetworks, entry.getKey()))
157                 .forEach(entry -> cleanupStaleBindings(globalNodeId, vlansByLogicalSwitch, entry.getKey()));
158
159         //following condition handles:
160         //1. only stale logical switches are present
161         List<String> staleLogicalSwitches = logicalSwitchesOnDevice.stream()
162                 .filter((staleLogicalSwitch) -> IS_STALE_LOGICAL_SWITCH.test(validNetworks, staleLogicalSwitch))
163                 .collect(Collectors.toList());
164
165         if (!staleLogicalSwitches.isEmpty()) {
166             staleLogicalSwitches.forEach((staleLogicalSwitch) -> {
167                 LOG.info("Cleaning the stale logical switch : {}", staleLogicalSwitch);
168                 elanL2GatewayUtils.scheduleDeleteLogicalSwitch(new NodeId(globalNodeId),
169                         staleLogicalSwitch, true); });
170         }
171     }
172
173     private Map<String, List<InstanceIdentifier<VlanBindings>>> getVlansByLogicalSwitchOnDevice(
174             final Node configPsNode) {
175         List<TerminationPoint> ports = new ArrayList<>(configPsNode.nonnullTerminationPoint().values());
176         if (ports == null) {
177             return Collections.emptyMap();
178         }
179         Map<String, List<InstanceIdentifier<VlanBindings>>> vlans = new HashMap<>();
180         ports.stream()
181                 .filter(CONTAINS_VLANBINDINGS)
182                 .forEach((port) -> {
183                     port.augmentation(HwvtepPhysicalPortAugmentation.class)
184                             .nonnullVlanBindings().values()
185                             .forEach((binding) -> putVlanBindingVsLogicalSwitch(configPsNode, vlans, port, binding));
186                 });
187         return vlans;
188     }
189
190     private static void putVlanBindingVsLogicalSwitch(final Node configPsNode,
191                                                       final Map<String, List<InstanceIdentifier<VlanBindings>>> vlans,
192                                                       final TerminationPoint port,
193                                                       final VlanBindings binding) {
194         String logicalSwitch = LOGICAL_SWITCH_FROM_BINDING.apply(binding);
195         vlans.computeIfAbsent(logicalSwitch, (name) -> new ArrayList<>())
196                 .add(createVlanIid(configPsNode.getNodeId(), port, binding));
197     }
198
199     private static InstanceIdentifier<VlanBindings> createVlanIid(final NodeId nodeId,
200                                                            final TerminationPoint tp,
201                                                            final VlanBindings vlanBinding) {
202         return HwvtepSouthboundUtils.createInstanceIdentifier(nodeId)
203                 .child(TerminationPoint.class, tp.key())
204                 .augmentation(HwvtepPhysicalPortAugmentation.class)
205                 .child(VlanBindings.class, vlanBinding.key());
206     }
207
208     private void cleanupStaleBindings(final String globalNodeId,
209                                       final Map<String, List<InstanceIdentifier<VlanBindings>>> vlans,
210                                       final String staleLogicalSwitch) {
211
212         LOG.trace("CleanupStaleBindings for logical switch {}", staleLogicalSwitch);
213         LoggingFutures.addErrorLogging(
214             txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
215                 if (vlans.containsKey(staleLogicalSwitch)) {
216                     vlans.get(staleLogicalSwitch).forEach((vlanIid) -> tx.delete(vlanIid));
217                 }
218             }),
219             LOG, "Failed to delete stale vlan bindings from node {}", globalNodeId);
220     }
221
222     private static List<String> getLogicalSwitchesOnDevice(final Node globalConfigNode) {
223         HwvtepGlobalAugmentation augmentation = globalConfigNode.augmentation(HwvtepGlobalAugmentation.class);
224         if (augmentation == null || augmentation.getLogicalSwitches() == null) {
225             return Collections.emptyList();
226         }
227         return augmentation
228                 .nonnullLogicalSwitches().values()
229                 .stream()
230                 .map((ls) -> ls.getHwvtepNodeName().getValue())
231                 .collect(Collectors.toList());
232     }
233 }