blob: 935141de5031a79fc399e1537ab215d2dd49c869 [file] [log] [blame]
Simon Huntd7f7bcc2015-05-08 14:13:17 -07001/*
2 * Copyright 2015 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 */
17
18package org.onosproject.ui.impl;
19
20import com.fasterxml.jackson.databind.node.ObjectNode;
21import com.google.common.collect.ImmutableSet;
Simon Huntc54cd1b2015-05-11 13:43:44 -070022import com.google.common.collect.Maps;
Simon Huntd7f7bcc2015-05-08 14:13:17 -070023import org.onlab.osgi.ServiceDirectory;
24import org.onosproject.core.CoreService;
Simon Huntc54cd1b2015-05-11 13:43:44 -070025import org.onosproject.ui.JsonUtils;
Simon Huntd7f7bcc2015-05-08 14:13:17 -070026import org.onosproject.ui.RequestHandler;
27import org.onosproject.ui.UiConnection;
28import org.onosproject.ui.UiMessageHandler;
Simon Huntc54cd1b2015-05-11 13:43:44 -070029import org.onosproject.ui.impl.topo.OverlayService;
30import org.onosproject.ui.impl.topo.SummaryData;
Simon Huntd7f7bcc2015-05-08 14:13:17 -070031import org.onosproject.ui.impl.topo.TopoUiEvent;
32import org.onosproject.ui.impl.topo.TopoUiListener;
33import org.onosproject.ui.impl.topo.TopoUiModelService;
Simon Huntc54cd1b2015-05-11 13:43:44 -070034import org.onosproject.ui.impl.topo.overlay.AbstractSummaryGenerator;
35import org.onosproject.ui.impl.topo.overlay.SummaryGenerator;
Simon Huntd7f7bcc2015-05-08 14:13:17 -070036import org.slf4j.Logger;
37import org.slf4j.LoggerFactory;
38
39import java.util.Collection;
Simon Huntc54cd1b2015-05-11 13:43:44 -070040import java.util.HashMap;
41import java.util.Map;
Simon Huntd7f7bcc2015-05-08 14:13:17 -070042
43import static com.google.common.base.Preconditions.checkNotNull;
Simon Hunt732bb2e2015-05-13 18:32:16 -070044import static java.lang.System.currentTimeMillis;
Simon Huntc54cd1b2015-05-11 13:43:44 -070045import static org.onosproject.ui.impl.topo.TopoUiEvent.Type.SUMMARY_UPDATE;
Simon Huntd7f7bcc2015-05-08 14:13:17 -070046
47/**
48 * Facility for handling inbound messages from the topology view, and
49 * generating outbound messages for the same.
50 */
Simon Huntc54cd1b2015-05-11 13:43:44 -070051public class AltTopoViewMessageHandler extends UiMessageHandler
52 implements OverlayService {
Simon Huntd7f7bcc2015-05-08 14:13:17 -070053
54 private static final String TOPO_START = "topoStart";
Simon Hunt732bb2e2015-05-13 18:32:16 -070055 private static final String TOPO_HEARTBEAT = "topoHeartbeat";
Simon Huntd7f7bcc2015-05-08 14:13:17 -070056 private static final String TOPO_STOP = "topoStop";
Simon Huntc54cd1b2015-05-11 13:43:44 -070057 private static final String REQ_SUMMARY = "requestSummary";
Simon Huntda580882015-05-12 20:58:18 -070058 private static final String CANCEL_SUMMARY = "cancelSummary";
Simon Huntd7f7bcc2015-05-08 14:13:17 -070059
60 private final Logger log = LoggerFactory.getLogger(getClass());
61
62 protected ServiceDirectory directory;
63 protected TopoUiModelService modelService;
64
Simon Hunt732bb2e2015-05-13 18:32:16 -070065 private ModelListener modelListener;
Simon Huntd7f7bcc2015-05-08 14:13:17 -070066 private String version;
Simon Huntc54cd1b2015-05-11 13:43:44 -070067 private SummaryGenerator defaultSummaryGenerator;
68 private SummaryGenerator currentSummaryGenerator;
69
Simon Huntd7f7bcc2015-05-08 14:13:17 -070070
71 private boolean topoActive = false;
72
73 @Override
74 public void init(UiConnection connection, ServiceDirectory directory) {
75 super.init(connection, directory);
76 this.directory = checkNotNull(directory, "Directory cannot be null");
77 modelService = directory.get(TopoUiModelService.class);
Simon Huntc54cd1b2015-05-11 13:43:44 -070078 defaultSummaryGenerator = new DefSummaryGenerator("node", "ONOS Summary");
Simon Huntd7f7bcc2015-05-08 14:13:17 -070079
Simon Huntc54cd1b2015-05-11 13:43:44 -070080 bindEventHandlers();
Simon Huntd7f7bcc2015-05-08 14:13:17 -070081 modelListener = new ModelListener();
82 version = getVersion();
Simon Huntc54cd1b2015-05-11 13:43:44 -070083 currentSummaryGenerator = defaultSummaryGenerator;
Simon Huntd7f7bcc2015-05-08 14:13:17 -070084 }
85
Simon Huntda580882015-05-12 20:58:18 -070086 @Override
87 public void destroy() {
Simon Hunt732bb2e2015-05-13 18:32:16 -070088 cancelAllMonitoring();
89 stopListeningToModel();
Simon Huntda580882015-05-12 20:58:18 -070090 super.destroy();
91 }
92
Simon Huntd7f7bcc2015-05-08 14:13:17 -070093
Simon Huntd7f7bcc2015-05-08 14:13:17 -070094 @Override
Simon Huntda580882015-05-12 20:58:18 -070095 protected Collection<RequestHandler> createRequestHandlers() {
Simon Huntd7f7bcc2015-05-08 14:13:17 -070096 return ImmutableSet.of(
97 new TopoStart(),
Simon Hunt732bb2e2015-05-13 18:32:16 -070098 new TopoHeartbeat(),
Simon Huntc54cd1b2015-05-11 13:43:44 -070099 new TopoStop(),
Simon Huntda580882015-05-12 20:58:18 -0700100 new ReqSummary(),
101 new CancelSummary()
Simon Huntc54cd1b2015-05-11 13:43:44 -0700102 // TODO: add more handlers here.....
Simon Huntd7f7bcc2015-05-08 14:13:17 -0700103 );
104 }
105
106 // =====================================================================
Simon Hunt732bb2e2015-05-13 18:32:16 -0700107
108 private void cancelAllMonitoring() {
109 // TODO:
110 }
111
112 private void startListeningToModel() {
113 topoActive = true;
114 modelService.addListener(modelListener);
115 }
116
117 private void stopListeningToModel() {
118 topoActive = false;
119 modelService.removeListener(modelListener);
120 }
121
122 private String getVersion() {
123 String ver = directory.get(CoreService.class).version().toString();
124 return ver.replace(".SNAPSHOT", "*").replaceFirst("~.*$", "");
125 }
126
127 // =====================================================================
Simon Huntc54cd1b2015-05-11 13:43:44 -0700128 // Overlay Service
129 // TODO: figure out how we are going to switch overlays in and out...
130
131 private final Map<String, SummaryGenerator> summGenCache = Maps.newHashMap();
132
133 @Override
134 public void addSummaryGenerator(String overlayId, SummaryGenerator generator) {
135 log.info("Adding custom Summary Generator for overlay [{}]", overlayId);
136 summGenCache.put(overlayId, generator);
137 }
138
139 @Override
140 public void removeSummaryGenerator(String overlayId) {
141 summGenCache.remove(overlayId);
142 log.info("Custom Summary Generator for overlay [{}] removed", overlayId);
143 }
144
145
146
147 // =====================================================================
Simon Huntd7f7bcc2015-05-08 14:13:17 -0700148 // Request Handlers for (topo view) events from the UI...
149
150 private final class TopoStart extends RequestHandler {
151 private TopoStart() {
152 super(TOPO_START);
153 }
154
155 @Override
156 public void process(long sid, ObjectNode payload) {
Simon Hunt732bb2e2015-05-13 18:32:16 -0700157 startListeningToModel();
Simon Huntd7f7bcc2015-05-08 14:13:17 -0700158 sendMessages(modelService.getInitialState());
Simon Huntd7f7bcc2015-05-08 14:13:17 -0700159 }
160 }
161
Simon Hunt732bb2e2015-05-13 18:32:16 -0700162 private final class TopoHeartbeat extends RequestHandler {
163 private TopoHeartbeat() {
164 super(TOPO_HEARTBEAT);
165 }
166 @Override
167 public void process(long sid, ObjectNode payload) {
168 modelListener.nudge();
169 }
170 }
171
Simon Huntd7f7bcc2015-05-08 14:13:17 -0700172 private final class TopoStop extends RequestHandler {
173 private TopoStop() {
174 super(TOPO_STOP);
175 }
176
177 @Override
178 public void process(long sid, ObjectNode payload) {
Simon Hunt732bb2e2015-05-13 18:32:16 -0700179 stopListeningToModel();
Simon Huntc54cd1b2015-05-11 13:43:44 -0700180 }
181 }
182
183 private final class ReqSummary extends RequestHandler {
184 private ReqSummary() {
185 super(REQ_SUMMARY);
186 }
187
188 @Override
189 public void process(long sid, ObjectNode payload) {
190 modelService.startSummaryMonitoring();
191 // NOTE: showSummary messages forwarded through the model listener
192 }
193 }
194
Simon Huntda580882015-05-12 20:58:18 -0700195 private final class CancelSummary extends RequestHandler {
196 private CancelSummary() {
197 super(CANCEL_SUMMARY);
198 }
199
200 @Override
201 public void process(long sid, ObjectNode payload) {
202 modelService.stopSummaryMonitoring();
203 }
204 }
205
Simon Huntc54cd1b2015-05-11 13:43:44 -0700206 // =====================================================================
207
208 private final class DefSummaryGenerator extends AbstractSummaryGenerator {
209 public DefSummaryGenerator(String iconId, String title) {
210 super(iconId, title);
211 }
212
213 @Override
214 public ObjectNode generateSummary() {
215 SummaryData data = modelService.getSummaryData();
216 iconId("node");
217 title("ONOS Summary");
218 clearProps();
219 prop("Devices", format(data.deviceCount()));
220 prop("Links", format(data.linkCount()));
221 prop("Hosts", format(data.hostCount()));
222 prop("Topology SCCs", format(data.clusterCount()));
223 separator();
224 prop("Intents", format(data.intentCount()));
225 prop("Flows", format(data.flowRuleCount()));
226 prop("Version", version);
227 return buildObjectNode();
Simon Huntd7f7bcc2015-05-08 14:13:17 -0700228 }
229 }
230
231 // =====================================================================
232 // Private Helper Methods...
233
234 private void sendMessages(Collection<ObjectNode> messages) {
235 if (topoActive) {
236 UiConnection connection = connection();
237 if (connection != null) {
238 messages.forEach(connection::sendMessage);
239 }
240 }
241 }
242
Simon Huntc54cd1b2015-05-11 13:43:44 -0700243 private void sendMessages(ObjectNode message) {
244 if (topoActive) {
245 UiConnection connection = connection();
246 if (connection != null) {
247 connection.sendMessage(message);
248 }
249 }
250 }
251
Simon Huntd7f7bcc2015-05-08 14:13:17 -0700252 // =====================================================================
253 // Our listener for model events so we can push changes out to the UI...
254
255 private class ModelListener implements TopoUiListener {
Simon Hunt732bb2e2015-05-13 18:32:16 -0700256 private static final long AWAKE_THRESHOLD_MS = 6000;
257
258 private long lastNudged = currentTimeMillis();
259
Simon Huntd7f7bcc2015-05-08 14:13:17 -0700260 @Override
261 public void event(TopoUiEvent event) {
262 log.debug("Handle Event: {}", event);
Simon Huntc54cd1b2015-05-11 13:43:44 -0700263 ModelEventHandler handler = eventHandlerBinding.get(event.type());
264
265 // any handlers not bound explicitly are assumed to be pass-thru...
266 if (handler == null) {
267 handler = passThruHandler;
268 }
269 handler.handleEvent(event);
Simon Huntd7f7bcc2015-05-08 14:13:17 -0700270 }
Simon Hunt732bb2e2015-05-13 18:32:16 -0700271
272 @Override
273 public boolean isAwake() {
274 return currentTimeMillis() - lastNudged < AWAKE_THRESHOLD_MS;
275 }
276
277 public void nudge() {
278 lastNudged = currentTimeMillis();
279 }
Simon Huntd7f7bcc2015-05-08 14:13:17 -0700280 }
Simon Huntc54cd1b2015-05-11 13:43:44 -0700281
282
283 // =====================================================================
284 // Model Event Handler definitions and bindings...
285
286 private interface ModelEventHandler {
287 void handleEvent(TopoUiEvent event);
288 }
289
290 private ModelEventHandler passThruHandler = event -> {
291 // simply forward the event message as is
292 ObjectNode message = event.subject();
293 if (message != null) {
294 sendMessages(event.subject());
295 }
296 };
297
298 private ModelEventHandler summaryHandler = event -> {
299 // use the currently selected summary generator to create the body..
300 ObjectNode payload = currentSummaryGenerator.generateSummary();
301 sendMessages(JsonUtils.envelope("showSummary", payload));
302 };
303
304
305 // TopoUiEvent type binding of handlers
306 private final Map<TopoUiEvent.Type, ModelEventHandler>
307 eventHandlerBinding = new HashMap<>();
308
309 private void bindEventHandlers() {
310 eventHandlerBinding.put(SUMMARY_UPDATE, summaryHandler);
311 // NOTE: no need to bind pass-thru handlers
312 }
Simon Huntd7f7bcc2015-05-08 14:13:17 -0700313}