blob: 3a92428683776344561bde684a5d4318115d609d [file] [log] [blame]
Andrea Campanella732ea832017-02-06 09:25:59 -08001/*
2 * Copyright 2017-present Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.ui.impl;
18
19import com.google.common.collect.ImmutableList;
Andrea Campanellaaf934682017-03-05 11:06:40 +010020import com.google.common.collect.ImmutableSet;
Andrea Campanella732ea832017-02-06 09:25:59 -080021import com.google.common.collect.Sets;
22import org.onosproject.net.DeviceId;
23import org.onosproject.net.ElementId;
24import org.onosproject.net.HostId;
25import org.onosproject.net.Link;
26import org.onosproject.net.MarkerResource;
27import org.onosproject.net.NetworkResource;
28import org.onosproject.net.behaviour.protection.TransportEndpointDescription;
29import org.onosproject.net.intent.FlowRuleIntent;
30import org.onosproject.net.intent.Intent;
31import org.onosproject.net.intent.OpticalConnectivityIntent;
32import org.onosproject.net.intent.ProtectionEndpointIntent;
33import org.onosproject.ui.impl.topo.util.ServicesBundle;
34import org.onosproject.ui.impl.topo.util.TrafficLink;
35import org.onosproject.ui.impl.topo.util.TrafficLink.StatsType;
36import org.onosproject.ui.impl.topo.util.TrafficLinkMap;
37import org.onosproject.ui.topo.AbstractTopoMonitor;
38import org.onosproject.ui.topo.DeviceHighlight;
39import org.onosproject.ui.topo.Highlights;
40import org.onosproject.ui.topo.HostHighlight;
41import org.onosproject.ui.topo.LinkHighlight.Flavor;
Andrea Campanellaaf934682017-03-05 11:06:40 +010042import org.onosproject.ui.topo.Mod;
Andrea Campanella732ea832017-02-06 09:25:59 -080043import org.onosproject.ui.topo.NodeHighlight;
44import org.slf4j.Logger;
45import org.slf4j.LoggerFactory;
46
47import java.util.Collection;
48import java.util.HashSet;
49import java.util.List;
50import java.util.Set;
51import java.util.Timer;
52import java.util.TimerTask;
53
54import static org.onosproject.ui.impl.ProtectedIntentMonitor.ProtectedMode.IDLE;
55import static org.onosproject.ui.impl.ProtectedIntentMonitor.ProtectedMode.SELECTED_INTENT;
56
57/**
58 * Encapsulates the behavior of monitoring protected intents.
59 */
60//TODO refactor duplicated methods from here and the TrafficMonitor to AbstractTopoMonitor
61public class ProtectedIntentMonitor extends AbstractTopoMonitor {
62
63 private static final Logger log =
64 LoggerFactory.getLogger(ProtectedIntentMonitor.class);
Andrea Campanellaaf934682017-03-05 11:06:40 +010065 private static final String PRIMARY_PATH_TAG = "protection1";
66
67 private static final String PROT_PRIMARY = "protPrimary";
68 private static final String PROT_BACKUP = "protBackup";
69
70
71 private static final Mod MOD_PROT_PRIMARY = new Mod(PROT_PRIMARY);
72 private static final Set<Mod> PROTECTED_MOD_PRIMARY_SET = ImmutableSet.of(MOD_PROT_PRIMARY);
73
74 private static final Mod MOD_PROT_BACKUP = new Mod(PROT_BACKUP);
75 private static final Set<Mod> PROTECTED_MOD_BACKUP_SET = ImmutableSet.of(MOD_PROT_BACKUP);
76
Andrea Campanella732ea832017-02-06 09:25:59 -080077
78 /**
79 * Designates the different modes of operation.
80 */
81 public enum ProtectedMode {
82 IDLE,
83 SELECTED_INTENT
84 }
85
86 private final long trafficPeriod;
87 private final ServicesBundle servicesBundle;
88 private final TopologyViewMessageHandler msgHandler;
89
90 private final Timer timer = new Timer("topo-protected-intents");
91
92 private TimerTask trafficTask = null;
93 private ProtectedMode mode = ProtectedMode.IDLE;
94 private Intent selectedIntent = null;
95
96
97 /**
98 * Constructs a protected intent monitor.
99 *
100 * @param trafficPeriod traffic task period in ms
101 * @param servicesBundle bundle of services
102 * @param msgHandler our message handler
103 */
104 public ProtectedIntentMonitor(long trafficPeriod, ServicesBundle servicesBundle,
105 TopologyViewMessageHandler msgHandler) {
106 this.trafficPeriod = trafficPeriod;
107 this.servicesBundle = servicesBundle;
108 this.msgHandler = msgHandler;
109
110 }
111
112 // =======================================================================
113 // === API ===
114
115 // TODO: move this out to the "h2h/multi-intent app"
116
117 /**
118 * Monitor for protected intent data to be sent back to the web client,
119 * for the given intent.
120 *
121 * @param intent the intent to monitor
122 */
123 public synchronized void monitor(Intent intent) {
124 log.debug("monitor intent: {}", intent.id());
125 selectedIntent = intent;
126 mode = SELECTED_INTENT;
127 scheduleTask();
128 sendSelectedIntents();
129 }
130
131 /**
132 * Stop all traffic monitoring.
133 */
134 public synchronized void stopMonitoring() {
135 log.debug("STOP monitoring");
136 if (mode != IDLE) {
137 sendClearAll();
138 }
139 }
140
141
142 // =======================================================================
143 // === Helper methods ===
144 private void sendClearAll() {
145 clearAll();
146 sendClearHighlights();
147 }
148
149 private void clearAll() {
150 this.mode = IDLE;
151 clearSelection();
152 cancelTask();
153 }
154
155 private void clearSelection() {
156 selectedIntent = null;
157 }
158
159 //TODO duplicate and can be brought in abstract upper class.
160 private synchronized void scheduleTask() {
161 if (trafficTask == null) {
162 log.debug("Starting up background protected intent task...");
163 trafficTask = new TrafficUpdateTask();
164 timer.schedule(trafficTask, trafficPeriod, trafficPeriod);
165 } else {
166 log.debug("(protected intent task already running)");
167 }
168 }
169
170 private synchronized void cancelTask() {
171 if (trafficTask != null) {
172 trafficTask.cancel();
173 trafficTask = null;
174 }
175 }
176
177 private void sendSelectedIntents() {
178 log.debug("sendSelectedIntents: {}", selectedIntent);
179 msgHandler.sendHighlights(protectedIntentHighlights());
180 }
181
182 private void sendClearHighlights() {
183 log.debug("sendClearHighlights");
184 msgHandler.sendHighlights(new Highlights());
185 }
186
187 // =======================================================================
188 // === Generate messages in JSON object node format
189 private Highlights protectedIntentHighlights() {
190 Highlights highlights = new Highlights();
191 TrafficLinkMap linkMap = new TrafficLinkMap();
192 if (selectedIntent != null) {
193 List<Intent> installables = servicesBundle.intentService()
194 .getInstallableIntents(selectedIntent.key());
195 Set<Link> primary = new HashSet<>();
196 Set<Link> backup = new HashSet<>();
197 if (installables != null) {
198 //There should be exactly two FlowRuleIntent and four
199 //ProtectionEndpointIntent for each ProtectedTransportIntent.
200 for (Intent installable : installables) {
201 if (installable instanceof FlowRuleIntent) {
202 handleFlowRuleIntent(primary, backup, (FlowRuleIntent) installable);
203 } else if (installable instanceof ProtectionEndpointIntent) {
204 handleProtectionEndpointIntent(primary, backup,
205 (ProtectionEndpointIntent) installable);
206 } else {
207 log.warn("Intent {} is not an expected installable type {} " +
208 "related to ProtectedTransportIntent",
209 installable.id(), installable.getClass().getSimpleName());
210 stopMonitoring();
211 }
212 }
213 boolean isOptical = selectedIntent instanceof OpticalConnectivityIntent;
214 //last parameter (traffic) signals if the link is highlited with ants or solid line
215 //Flavor is swapped so green is primary path.
216 if (usingBackup(primary)) {
217 //the backup becomes in use so we have a dotted line
Andrea Campanellaaf934682017-03-05 11:06:40 +0100218 processLinks(linkMap, backup, Flavor.PRIMARY_HIGHLIGHT,
219 isOptical, true, PROTECTED_MOD_BACKUP_SET);
Andrea Campanella732ea832017-02-06 09:25:59 -0800220 } else {
Andrea Campanellaaf934682017-03-05 11:06:40 +0100221 processLinks(linkMap, primary, Flavor.PRIMARY_HIGHLIGHT,
222 isOptical, true, PROTECTED_MOD_PRIMARY_SET);
223 processLinks(linkMap, backup, Flavor.SECONDARY_HIGHLIGHT,
224 isOptical, false, PROTECTED_MOD_BACKUP_SET);
Andrea Campanella732ea832017-02-06 09:25:59 -0800225 }
226 updateHighlights(highlights, primary);
227 updateHighlights(highlights, backup);
228 colorLinks(highlights, linkMap);
229 highlights.subdueAllElse(Highlights.Amount.MINIMALLY);
230 } else {
231 log.debug("Selected Intent has no installables intents");
232 }
233 } else {
234 log.debug("Selected Intent is null");
235 }
236 return highlights;
237 }
238
239 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
240
241 private void handleProtectionEndpointIntent(Set<Link> primary, Set<Link> backup,
242 ProtectionEndpointIntent peIntent) {
243 TransportEndpointDescription primaryDesc = peIntent
244 .description().paths().get(0);
245 TransportEndpointDescription secondaryDesc = peIntent
246 .description().paths().get(1);
247 primary.addAll(servicesBundle.linkService()
248 .getLinks(primaryDesc.output()
249 .connectPoint()));
250 backup.addAll(servicesBundle.linkService()
251 .getLinks(secondaryDesc.output()
252 .connectPoint()));
253 }
254
255 private void handleFlowRuleIntent(Set<Link> primary, Set<Link> backup,
256 FlowRuleIntent frIntent) {
257 boolean protection1 = frIntent.resources().stream()
258 .filter(r -> r instanceof MarkerResource)
259 .map(NetworkResource::toString)
260 .anyMatch(rstring -> rstring.equals(PRIMARY_PATH_TAG));
261 if (protection1) {
262 primary.addAll(linkResources(frIntent));
263 } else {
264 backup.addAll(linkResources(frIntent));
265 }
266 }
267
268 // returns true if the backup path is the one where the traffic is currently flowing
269 private boolean usingBackup(Set<Link> primary) {
270 Set<Link> activeLinks = Sets.newHashSet(servicesBundle.linkService().getActiveLinks());
271 return primary.isEmpty() || !activeLinks.containsAll(primary);
272 }
273
274 private void updateHighlights(Highlights highlights, Iterable<Link> links) {
275 for (Link link : links) {
276 ensureNodePresent(highlights, link.src().elementId());
277 ensureNodePresent(highlights, link.dst().elementId());
278 }
279 }
280
281 //TODO duplicate and can be brought in abstract upper class.
282 private void ensureNodePresent(Highlights highlights, ElementId eid) {
283 String id = eid.toString();
284 NodeHighlight nh = highlights.getNode(id);
285 if (nh == null) {
286 if (eid instanceof DeviceId) {
287 nh = new DeviceHighlight(id);
288 highlights.add((DeviceHighlight) nh);
289 } else if (eid instanceof HostId) {
290 nh = new HostHighlight(id);
291 highlights.add((HostHighlight) nh);
292 }
293 }
294 }
295
296 private void processLinks(TrafficLinkMap linkMap, Iterable<Link> links,
297 Flavor flavor, boolean isOptical,
Andrea Campanellaaf934682017-03-05 11:06:40 +0100298 boolean showTraffic, Set<Mod> mods) {
Andrea Campanella732ea832017-02-06 09:25:59 -0800299 if (links != null) {
300 for (Link link : links) {
301 TrafficLink tlink = linkMap.add(link);
302 tlink.tagFlavor(flavor);
303 tlink.optical(isOptical);
304 if (showTraffic) {
305 tlink.antMarch(true);
306 }
Andrea Campanellaaf934682017-03-05 11:06:40 +0100307 tlink.tagMods(mods);
Andrea Campanella732ea832017-02-06 09:25:59 -0800308 }
309 }
310 }
311
312 //TODO duplicate and can be brought in abstract upper class.
313 private void colorLinks(Highlights highlights, TrafficLinkMap linkMap) {
314 for (TrafficLink tlink : linkMap.biLinks()) {
315 highlights.add(tlink.highlight(StatsType.TAGGED));
316 }
317 }
318
319 //TODO duplicate and can be brought in abstract upper class.
320 // Extracts links from the specified flow rule intent resources
321 private Collection<Link> linkResources(Intent installable) {
322 ImmutableList.Builder<Link> builder = ImmutableList.builder();
323 installable.resources().stream().filter(r -> r instanceof Link)
324 .forEach(r -> builder.add((Link) r));
325 return builder.build();
326 }
327
328 // =======================================================================
329 // === Background Task
330
331 // Provides periodic update of traffic information to the client
332 private class TrafficUpdateTask extends TimerTask {
333 @Override
334 public void run() {
335 try {
336 switch (mode) {
337 case SELECTED_INTENT:
338 sendSelectedIntents();
339 break;
340
341 default:
342 // RELATED_INTENTS and IDLE modes should never invoke
343 // the background task, but if they do, they have
344 // nothing to do
345 break;
346 }
347
348 } catch (Exception e) {
349 log.warn("Unable to process protected intent task due to {}", e.getMessage());
350 log.warn("Boom!", e);
351 }
352 }
353 }
354}