blob: d47367b6c7a738046f77e2fee7990b2275a65feb [file] [log] [blame]
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -08003 *
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 */
16package org.onosproject.ui.impl;
17
Thomas Vachuska9ed335b2015-04-14 12:07:47 -070018import com.fasterxml.jackson.databind.JsonNode;
Thomas Vachuska0af26912016-03-21 21:37:30 -070019import com.fasterxml.jackson.databind.ObjectMapper;
20import com.fasterxml.jackson.databind.node.ArrayNode;
21import com.fasterxml.jackson.databind.node.BooleanNode;
22import com.fasterxml.jackson.databind.node.DoubleNode;
23import com.fasterxml.jackson.databind.node.IntNode;
24import com.fasterxml.jackson.databind.node.JsonNodeFactory;
25import com.fasterxml.jackson.databind.node.LongNode;
26import com.fasterxml.jackson.databind.node.NullNode;
27import com.fasterxml.jackson.databind.node.ObjectNode;
28import com.fasterxml.jackson.databind.node.ShortNode;
29import com.fasterxml.jackson.databind.node.TextNode;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080030import com.google.common.collect.ImmutableList;
Thomas Vachuska0af26912016-03-21 21:37:30 -070031import com.google.common.collect.ImmutableMap;
Thomas Vachuska9ed335b2015-04-14 12:07:47 -070032import com.google.common.collect.ImmutableSet;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080033import com.google.common.collect.Lists;
34import com.google.common.collect.Maps;
Madan Jampanibf8ee802016-05-04 14:07:36 -070035import org.onlab.util.Tools;
Thomas Vachuskaa5e986d2021-04-06 11:14:09 -070036import org.onosproject.cfg.ComponentConfigService;
Thomas Vachuska51f540f2015-05-27 17:26:57 -070037import org.onosproject.mastership.MastershipService;
Thomas Vachuska0af26912016-03-21 21:37:30 -070038import org.onosproject.store.serializers.KryoNamespaces;
Madan Jampani7b93ceb2016-05-04 09:58:40 -070039import org.onosproject.store.service.ConsistentMap;
40import org.onosproject.store.service.MapEvent;
41import org.onosproject.store.service.MapEventListener;
42import org.onosproject.store.service.Serializer;
Thomas Vachuska0af26912016-03-21 21:37:30 -070043import org.onosproject.store.service.StorageService;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080044import org.onosproject.ui.UiExtension;
45import org.onosproject.ui.UiExtensionService;
Laszlo Papp759f0d32018-03-05 13:24:30 +000046import org.onosproject.ui.UiGlyph;
47import org.onosproject.ui.UiGlyphFactory;
Thomas Vachuska3553b302015-03-07 14:49:43 -080048import org.onosproject.ui.UiMessageHandlerFactory;
Thomas Vachuska0af26912016-03-21 21:37:30 -070049import org.onosproject.ui.UiPreferencesService;
Simon Hunt1169c952017-06-05 11:20:11 -070050import org.onosproject.ui.UiSessionToken;
51import org.onosproject.ui.UiTokenService;
Simon Hunt6cc86452017-04-27 17:46:22 -070052import org.onosproject.ui.UiTopo2OverlayFactory;
Thomas Vachuska2b4de872021-03-30 16:31:34 -070053import org.onosproject.ui.UiTopoHighlighterFactory;
Simon Huntd5b96732016-07-08 13:22:27 -070054import org.onosproject.ui.UiTopoMap;
Steven Burrows3a9a6442016-05-05 15:31:16 +010055import org.onosproject.ui.UiTopoMapFactory;
Simon Huntd5b96732016-07-08 13:22:27 -070056import org.onosproject.ui.UiTopoOverlayFactory;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080057import org.onosproject.ui.UiView;
Simon Hunt1002cd82015-04-23 14:44:03 -070058import org.onosproject.ui.UiViewHidden;
Simon Hunte6f64612017-04-28 00:01:48 -070059import org.onosproject.ui.impl.topo.Topo2TrafficMessageHandler;
Simon Huntd5b96732016-07-08 13:22:27 -070060import org.onosproject.ui.impl.topo.Topo2ViewMessageHandler;
Simon Hunt6cc86452017-04-27 17:46:22 -070061import org.onosproject.ui.impl.topo.Traffic2Overlay;
Thomas Vachuska1b1355d2018-02-06 16:53:58 -080062import org.onosproject.ui.impl.topo.model.UiSharedTopologyModel;
Simon Hunt23f9c7b2017-07-10 20:00:30 -070063import org.onosproject.ui.lion.LionBundle;
Simon Huntb8042032017-06-13 15:03:23 -070064import org.onosproject.ui.lion.LionUtils;
Thomas Vachuskaa5e986d2021-04-06 11:14:09 -070065import org.onosproject.ui.topo.AbstractTopoMonitor;
66import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070067import org.osgi.service.component.annotations.Activate;
68import org.osgi.service.component.annotations.Component;
69import org.osgi.service.component.annotations.Deactivate;
Thomas Vachuskaa5e986d2021-04-06 11:14:09 -070070import org.osgi.service.component.annotations.Modified;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070071import org.osgi.service.component.annotations.Reference;
72import org.osgi.service.component.annotations.ReferenceCardinality;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080073import org.slf4j.Logger;
74import org.slf4j.LoggerFactory;
75
Simon Hunt1169c952017-06-05 11:20:11 -070076import java.math.BigInteger;
77import java.security.SecureRandom;
Thomas Vachuskaa5e986d2021-04-06 11:14:09 -070078import java.util.Dictionary;
Thomas Vachuska0af26912016-03-21 21:37:30 -070079import java.util.LinkedHashMap;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080080import java.util.List;
Simon Huntd6d3ad32017-06-21 15:27:06 -070081import java.util.Locale;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080082import java.util.Map;
Thomas Vachuska9ed335b2015-04-14 12:07:47 -070083import java.util.Set;
Madan Jampanibf8ee802016-05-04 14:07:36 -070084import java.util.concurrent.ExecutorService;
85import java.util.concurrent.Executors;
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -080086
87import static com.google.common.collect.ImmutableList.of;
88import static java.util.stream.Collectors.toSet;
Heedo Kang4a47a302016-02-29 17:40:23 +090089import static org.onosproject.security.AppGuard.checkPermission;
Laszlo Papp759f0d32018-03-05 13:24:30 +000090import static org.onosproject.security.AppPermission.Type.GLYPH_READ;
91import static org.onosproject.security.AppPermission.Type.GLYPH_WRITE;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070092import static org.onosproject.security.AppPermission.Type.UI_READ;
93import static org.onosproject.security.AppPermission.Type.UI_WRITE;
Thomas Vachuska0af26912016-03-21 21:37:30 -070094import static org.onosproject.ui.UiView.Category.NETWORK;
95import static org.onosproject.ui.UiView.Category.PLATFORM;
Thomas Vachuskaa5e986d2021-04-06 11:14:09 -070096import static org.onosproject.ui.impl.OsgiPropertyConstants.TRAFFIC_REFRESH_MS;
97import static org.onosproject.ui.impl.OsgiPropertyConstants.TRAFFIC_REFRESH_MS_DEFAULT;
Simon Huntd8754652017-06-21 11:45:22 -070098import static org.onosproject.ui.impl.lion.BundleStitcher.generateBundles;
Heedo Kang4a47a302016-02-29 17:40:23 +090099
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800100/**
101 * Manages the user interface extensions.
102 */
Thomas Vachuskaa5e986d2021-04-06 11:14:09 -0700103@Component(immediate = true,
104 service = {
105 UiExtensionService.class,
106 UiPreferencesService.class,
107 SpriteService.class,
108 UiTokenService.class
109 },
110 property = {
111 TRAFFIC_REFRESH_MS + ":Integer=" + TRAFFIC_REFRESH_MS_DEFAULT,
112 })
Simon Hunt3678c2a2016-03-28 14:48:07 -0700113public class UiExtensionManager
Simon Hunt1169c952017-06-05 11:20:11 -0700114 implements UiExtensionService, UiPreferencesService, SpriteService,
115 UiTokenService {
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800116
Thomas Vachuskafa74dd72016-03-20 19:11:12 -0700117 private static final ClassLoader CL = UiExtensionManager.class.getClassLoader();
Simon Hunt3678c2a2016-03-28 14:48:07 -0700118
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700119 private static final String ONOS_USER_PREFERENCES = "onos-ui-user-preferences";
Simon Hunt1169c952017-06-05 11:20:11 -0700120 private static final String ONOS_SESSION_TOKENS = "onos-ui-session-tokens";
Simon Hunte05cae42015-07-23 17:35:24 -0700121 private static final String CORE = "core";
Thomas Vachuskafa74dd72016-03-20 19:11:12 -0700122 private static final String GUI_ADDED = "guiAdded";
123 private static final String GUI_REMOVED = "guiRemoved";
Laszlo Papp759f0d32018-03-05 13:24:30 +0000124 private static final String GLYPH_ADDED = "glyphAdded";
125 private static final String GLYPH_REMOVED = "glyphRemoved";
Thomas Vachuska0af26912016-03-21 21:37:30 -0700126 private static final String UPDATE_PREFS = "updatePrefs";
Simon Hunt3678c2a2016-03-28 14:48:07 -0700127 private static final String SLASH = "/";
128
129 private static final int IDX_USER = 0;
130 private static final int IDX_KEY = 1;
Simon Hunte05cae42015-07-23 17:35:24 -0700131
Simon Hunt7379a3d2017-06-20 16:50:39 -0700132 private static final String LION_BASE = "/org/onosproject/ui/lion";
133
134 private static final String[] LION_TAGS = {
Simon Huntcffb2862017-06-23 12:02:43 -0700135 // framework component localization
136 "core.fw.Mast",
137 "core.fw.Nav",
138 "core.fw.QuickHelp",
139
140 // view component localization
141 "core.view.App",
Simon Hunt7379a3d2017-06-20 16:50:39 -0700142 "core.view.Cluster",
Simon Hunt5989ddf2017-08-02 20:38:12 -0700143 "core.view.Topo",
ArturoF Velasco604ad0b2018-01-03 00:43:58 +0100144 "core.view.Flow",
Simon Hunt7379a3d2017-06-20 16:50:39 -0700145
Simon Huntcffb2862017-06-23 12:02:43 -0700146 // TODO: More to come...
Simon Hunt7379a3d2017-06-20 16:50:39 -0700147 };
148
149
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800150 private final Logger log = LoggerFactory.getLogger(getClass());
151
Simon Huntd6d3ad32017-06-21 15:27:06 -0700152 // First thing to do is to set the locale (before creating core extension).
153 private final Locale runtimeLocale = LionUtils.setupRuntimeLocale();
154
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800155 // List of all extensions
156 private final List<UiExtension> extensions = Lists.newArrayList();
157
Laszlo Papp759f0d32018-03-05 13:24:30 +0000158 private final List<UiGlyph> glyphs = Lists.newArrayList();
Thomas Vachuska2b4de872021-03-30 16:31:34 -0700159 private final List<UiTopoHighlighterFactory> highlighterFactories = Lists.newArrayList();
Laszlo Papp759f0d32018-03-05 13:24:30 +0000160
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800161 // Map of views to extensions
162 private final Map<String, UiExtension> views = Maps.newHashMap();
163
164 // Core views & core extension
Thomas Vachuska3553b302015-03-07 14:49:43 -0800165 private final UiExtension core = createCoreExtension();
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800166
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700167 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuska51f540f2015-05-27 17:26:57 -0700168 protected MastershipService mastershipService;
Thomas Vachuska3553b302015-03-07 14:49:43 -0800169
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700170 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuska0af26912016-03-21 21:37:30 -0700171 protected StorageService storageService;
172
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700173 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskaa5e986d2021-04-06 11:14:09 -0700174 protected ComponentConfigService cfgService;
175
176 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuska1b1355d2018-02-06 16:53:58 -0800177 private UiSharedTopologyModel sharedModel;
178
Thomas Vachuska0af26912016-03-21 21:37:30 -0700179 // User preferences
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700180 private ConsistentMap<String, ObjectNode> prefsConsistentMap;
181 private Map<String, ObjectNode> prefs;
182 private final MapEventListener<String, ObjectNode> prefsListener =
Thomas Vachuska0af26912016-03-21 21:37:30 -0700183 new InternalPrefsListener();
184
Simon Hunt1169c952017-06-05 11:20:11 -0700185 // Session tokens
186 private ConsistentMap<UiSessionToken, String> tokensConsistentMap;
187 private Map<UiSessionToken, String> tokens;
188 private final SessionTokenGenerator tokenGen =
189 new SessionTokenGenerator();
190
Thomas Vachuska0af26912016-03-21 21:37:30 -0700191 private final ObjectMapper mapper = new ObjectMapper();
192
Madan Jampanibf8ee802016-05-04 14:07:36 -0700193 private final ExecutorService eventHandlingExecutor =
Simon Huntd5b96732016-07-08 13:22:27 -0700194 Executors.newSingleThreadExecutor(
195 Tools.groupedThreads("onos/ui-ext-manager", "event-handler", log));
Madan Jampanibf8ee802016-05-04 14:07:36 -0700196
Simon Hunt23f9c7b2017-07-10 20:00:30 -0700197 private LionBundle navLion;
198
Thomas Vachuskaa5e986d2021-04-06 11:14:09 -0700199 protected int trafficRefreshMs = TRAFFIC_REFRESH_MS_DEFAULT;
Simon Hunt23f9c7b2017-07-10 20:00:30 -0700200
201 private String lionNavText(String id) {
202 return navLion.getValue("nav_item_" + id);
203 }
204
205 private UiView mkView(UiView.Category cat, String id, String iconId) {
206 return new UiView(cat, id, lionNavText(id), iconId);
207 }
208
Simon Huntc54cd1b2015-05-11 13:43:44 -0700209 private UiExtension createCoreExtension() {
Simon Hunt23f9c7b2017-07-10 20:00:30 -0700210 List<LionBundle> lionBundles = generateBundles(LION_BASE, LION_TAGS);
211
212 navLion = lionBundles.stream()
213 .filter(f -> f.id().equals("core.fw.Nav")).findFirst().get();
214
Simon Hunt20e16792015-04-24 14:29:39 -0700215 List<UiView> coreViews = of(
Simon Hunt23f9c7b2017-07-10 20:00:30 -0700216 mkView(PLATFORM, "app", "nav_apps"),
217 mkView(PLATFORM, "settings", "nav_settings"),
218 mkView(PLATFORM, "cluster", "nav_cluster"),
219 mkView(PLATFORM, "processor", "nav_processors"),
220 mkView(PLATFORM, "partition", "nav_partitions"),
221
222 mkView(NETWORK, "topo", "nav_topo"),
Thomas Vachuska175c33e2019-01-31 15:02:27 -0800223// mkView(NETWORK, "topo2", "nav_topo2"),
Simon Hunt23f9c7b2017-07-10 20:00:30 -0700224 mkView(NETWORK, "device", "nav_devs"),
225
Simon Hunt20e16792015-04-24 14:29:39 -0700226 new UiViewHidden("flow"),
Bri Prebilic Coleac829e42015-05-05 13:42:06 -0700227 new UiViewHidden("port"),
Bri Prebilic Coleff3dc672015-05-06 12:59:38 -0700228 new UiViewHidden("group"),
Jian Li1f544732015-12-30 23:36:37 -0800229 new UiViewHidden("meter"),
Yi Tsenga87b40c2017-09-10 00:59:03 -0700230 new UiViewHidden("pipeconf"),
Simon Hunt23f9c7b2017-07-10 20:00:30 -0700231
232 mkView(NETWORK, "link", "nav_links"),
233 mkView(NETWORK, "host", "nav_hosts"),
Thomas Vachuska52f2cd12018-11-08 21:20:04 -0800234 mkView(NETWORK, "intent", "nav_intents")
Simon Hunt20e16792015-04-24 14:29:39 -0700235 );
Simon Hunt1002cd82015-04-23 14:44:03 -0700236
Simon Hunt4c7edd32015-03-11 10:42:53 -0700237 UiMessageHandlerFactory messageHandlerFactory =
238 () -> ImmutableList.of(
Thomas Vachuska0af26912016-03-21 21:37:30 -0700239 new UserPreferencesMessageHandler(),
Thomas Vachuskae586b792015-03-26 13:59:38 -0700240 new TopologyViewMessageHandler(),
Simon Huntd5b96732016-07-08 13:22:27 -0700241 new Topo2ViewMessageHandler(),
Simon Hunte6f64612017-04-28 00:01:48 -0700242 new Topo2TrafficMessageHandler(),
Thomas Vachuska26be4f32016-03-31 01:10:27 -0700243 new MapSelectorMessageHandler(),
Thomas Vachuskae586b792015-03-26 13:59:38 -0700244 new DeviceViewMessageHandler(),
Thomas Vachuska583bc632015-04-14 10:10:57 -0700245 new LinkViewMessageHandler(),
Thomas Vachuska0fa583c2015-03-30 23:07:41 -0700246 new HostViewMessageHandler(),
Bri Prebilic Colecdc188d2015-04-24 16:40:11 -0700247 new FlowViewMessageHandler(),
Bri Prebilic Coleac829e42015-05-05 13:42:06 -0700248 new PortViewMessageHandler(),
Bri Prebilic Coleff3dc672015-05-06 12:59:38 -0700249 new GroupViewMessageHandler(),
Jian Li1f544732015-12-30 23:36:37 -0800250 new MeterViewMessageHandler(),
Bri Prebilic Cole384e8dc2015-04-13 15:51:14 -0700251 new IntentViewMessageHandler(),
Thomas Vachuska583bc632015-04-14 10:10:57 -0700252 new ApplicationViewMessageHandler(),
Thomas Vachuskaaa8b0eb2015-05-22 09:54:15 -0700253 new SettingsViewMessageHandler(),
chengfanb466a7e2015-08-21 09:59:29 -0500254 new ClusterViewMessageHandler(),
Thomas Vachuska3ece3732015-09-22 23:58:50 -0700255 new ProcessorViewMessageHandler(),
Yi Tsenga87b40c2017-09-10 00:59:03 -0700256 new PartitionViewMessageHandler(),
257 new PipeconfViewMessageHandler()
Simon Hunt4c7edd32015-03-11 10:42:53 -0700258 );
Simon Hunt1002cd82015-04-23 14:44:03 -0700259
Simon Hunte05cae42015-07-23 17:35:24 -0700260 UiTopoOverlayFactory topoOverlayFactory =
261 () -> ImmutableList.of(
Andrea Campanella732ea832017-02-06 09:25:59 -0800262 new TrafficOverlay(),
263 new ProtectedIntentOverlay()
Simon Hunte05cae42015-07-23 17:35:24 -0700264 );
265
Simon Hunt6cc86452017-04-27 17:46:22 -0700266 UiTopo2OverlayFactory topo2OverlayFactory =
267 () -> ImmutableList.of(
268 new Traffic2Overlay()
269 );
270
Steven Burrows3a9a6442016-05-05 15:31:16 +0100271 UiTopoMapFactory topoMapFactory =
272 () -> ImmutableList.of(
273 new UiTopoMap("australia", "Australia", "*australia", 1.0),
274 new UiTopoMap("americas", "North, Central and South America", "*americas", 0.7),
275 new UiTopoMap("n_america", "North America", "*n_america", 0.9),
276 new UiTopoMap("s_america", "South America", "*s_america", 0.9),
Thomas Vachuskacc0b7d62016-07-12 14:03:11 -0700277 new UiTopoMap("usa", "United States", "*continental_us", 1.3),
Steven Burrows3a9a6442016-05-05 15:31:16 +0100278 new UiTopoMap("bayareaGEO", "Bay Area, California", "*bayarea", 1.0),
Thomas Vachuskacc0b7d62016-07-12 14:03:11 -0700279 new UiTopoMap("europe", "Europe", "*europe", 10.0),
Steven Burrows3a9a6442016-05-05 15:31:16 +0100280 new UiTopoMap("italy", "Italy", "*italy", 0.8),
Thomas Vachuskacc0b7d62016-07-12 14:03:11 -0700281 new UiTopoMap("uk", "United Kingdom and Ireland", "*uk", 2.0),
Steven Burrows3a9a6442016-05-05 15:31:16 +0100282 new UiTopoMap("japan", "Japan", "*japan", 0.8),
283 new UiTopoMap("s_korea", "South Korea", "*s_korea", 0.75),
284 new UiTopoMap("taiwan", "Taiwan", "*taiwan", 0.7),
285 new UiTopoMap("africa", "Africa", "*africa", 0.7),
286 new UiTopoMap("oceania", "Oceania", "*oceania", 0.7),
287 new UiTopoMap("asia", "Asia", "*asia", 0.7)
288 );
289
Simon Hunte05cae42015-07-23 17:35:24 -0700290 return new UiExtension.Builder(CL, coreViews)
Simon Hunt23f9c7b2017-07-10 20:00:30 -0700291 .lionBundles(lionBundles)
Simon Hunte05cae42015-07-23 17:35:24 -0700292 .messageHandlerFactory(messageHandlerFactory)
293 .topoOverlayFactory(topoOverlayFactory)
Simon Hunt6cc86452017-04-27 17:46:22 -0700294 .topo2OverlayFactory(topo2OverlayFactory)
Steven Burrows3a9a6442016-05-05 15:31:16 +0100295 .topoMapFactory(topoMapFactory)
Simon Hunte05cae42015-07-23 17:35:24 -0700296 .resourcePath(CORE)
Sean Condon98b6ddb2019-12-24 08:07:40 +0000297 .ui2()
Simon Hunte05cae42015-07-23 17:35:24 -0700298 .build();
Thomas Vachuska3553b302015-03-07 14:49:43 -0800299 }
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800300
Simon Hunt7379a3d2017-06-20 16:50:39 -0700301
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800302 @Activate
Thomas Vachuskaa5e986d2021-04-06 11:14:09 -0700303 public void activate(ComponentContext context) {
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700304 Serializer serializer = Serializer.using(KryoNamespaces.API,
Simon Huntd6d3ad32017-06-21 15:27:06 -0700305 ObjectNode.class, ArrayNode.class,
306 JsonNodeFactory.class, LinkedHashMap.class,
307 TextNode.class, BooleanNode.class,
308 LongNode.class, DoubleNode.class, ShortNode.class,
309 IntNode.class, NullNode.class, UiSessionToken.class);
Thomas Vachuska0af26912016-03-21 21:37:30 -0700310
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700311 prefsConsistentMap = storageService.<String, ObjectNode>consistentMapBuilder()
Simon Hunt3678c2a2016-03-28 14:48:07 -0700312 .withName(ONOS_USER_PREFERENCES)
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700313 .withSerializer(serializer)
314 .withRelaxedReadConsistency()
Thomas Vachuska0af26912016-03-21 21:37:30 -0700315 .build();
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700316 prefsConsistentMap.addListener(prefsListener);
317 prefs = prefsConsistentMap.asJavaMap();
Simon Hunt1169c952017-06-05 11:20:11 -0700318
319 tokensConsistentMap = storageService.<UiSessionToken, String>consistentMapBuilder()
320 .withName(ONOS_SESSION_TOKENS)
321 .withSerializer(serializer)
322 .withRelaxedReadConsistency()
323 .build();
324 tokens = tokensConsistentMap.asJavaMap();
Thomas Vachuskaa5e986d2021-04-06 11:14:09 -0700325 cfgService.registerProperties(getClass());
Simon Hunt1169c952017-06-05 11:20:11 -0700326
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800327 register(core);
Simon Huntb8042032017-06-13 15:03:23 -0700328
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800329 log.info("Started");
330 }
331
332 @Deactivate
Thomas Vachuskaa5e986d2021-04-06 11:14:09 -0700333 public void deactivate(ComponentContext context) {
334 cfgService.unregisterProperties(getClass(), false);
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700335 prefsConsistentMap.removeListener(prefsListener);
Madan Jampanibf8ee802016-05-04 14:07:36 -0700336 eventHandlingExecutor.shutdown();
Thomas Vachuska51f540f2015-05-27 17:26:57 -0700337 UiWebSocketServlet.closeAll();
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800338 unregister(core);
339 log.info("Stopped");
340 }
341
Thomas Vachuskaa5e986d2021-04-06 11:14:09 -0700342 @Modified
343 protected void modified(ComponentContext context) {
344 Dictionary<?, ?> properties = context.getProperties();
345 Integer trafficRefresh = Tools.getIntegerProperty(properties, TRAFFIC_REFRESH_MS);
346
347 if (trafficRefresh != null && trafficRefresh > 10) {
348 AbstractTopoMonitor.setTrafficPeriod(trafficRefresh);
349 } else if (trafficRefresh != null) {
350 log.warn("trafficRefresh must be greater than 10");
351 }
352
353 log.info("Settings: trafficRefresh={}", trafficRefresh);
354 }
355
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800356 @Override
357 public synchronized void register(UiExtension extension) {
Heedo Kang4a47a302016-02-29 17:40:23 +0900358 checkPermission(UI_WRITE);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800359 if (!extensions.contains(extension)) {
360 extensions.add(extension);
361 for (UiView view : extension.views()) {
362 views.put(view.id(), extension);
363 }
Thomas Vachuskafa74dd72016-03-20 19:11:12 -0700364 UiWebSocketServlet.sendToAll(GUI_ADDED, null);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800365 }
366 }
367
368 @Override
369 public synchronized void unregister(UiExtension extension) {
Heedo Kang4a47a302016-02-29 17:40:23 +0900370 checkPermission(UI_WRITE);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800371 extensions.remove(extension);
Simon Huntd6d3ad32017-06-21 15:27:06 -0700372 extension.views().stream()
373 .map(UiView::id).collect(toSet()).forEach(views::remove);
Thomas Vachuskafa74dd72016-03-20 19:11:12 -0700374 UiWebSocketServlet.sendToAll(GUI_REMOVED, null);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800375 }
376
377 @Override
Laszlo Papp759f0d32018-03-05 13:24:30 +0000378 public synchronized void register(UiGlyphFactory glyphFactory) {
379 checkPermission(GLYPH_WRITE);
380 boolean glyphAdded = false;
381 for (UiGlyph glyph : glyphFactory.glyphs()) {
382 if (!glyphs.contains(glyph)) {
383 glyphs.add(glyph);
384 glyphAdded = true;
385 }
386 }
387 if (glyphAdded) {
388 UiWebSocketServlet.sendToAll(GLYPH_ADDED, null);
389 }
390 }
391
392 @Override
393 public synchronized void unregister(UiGlyphFactory glyphFactory) {
394 checkPermission(GLYPH_WRITE);
395 boolean glyphRemoved = false;
396 for (UiGlyph glyph : glyphFactory.glyphs()) {
397 glyphs.remove(glyph);
398 glyphRemoved = true;
399 }
400 if (glyphRemoved) {
401 UiWebSocketServlet.sendToAll(GLYPH_REMOVED, null);
402 }
403 }
404
405 @Override
Thomas Vachuska2b4de872021-03-30 16:31:34 -0700406 public synchronized void register(UiTopoHighlighterFactory factory) {
407 checkPermission(UI_WRITE);
408 if (!highlighterFactories.contains(factory)) {
409 highlighterFactories.add(factory);
410 UiWebSocketServlet.sendToAll(GUI_ADDED, null);
411 }
412 }
413
414 @Override
415 public synchronized void unregister(UiTopoHighlighterFactory factory) {
416 checkPermission(UI_WRITE);
417 highlighterFactories.remove(factory);
418 UiWebSocketServlet.sendToAll(GUI_REMOVED, null);
419 }
420
421 @Override
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800422 public synchronized List<UiExtension> getExtensions() {
Heedo Kang4a47a302016-02-29 17:40:23 +0900423 checkPermission(UI_READ);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800424 return ImmutableList.copyOf(extensions);
425 }
426
427 @Override
Laszlo Papp759f0d32018-03-05 13:24:30 +0000428 public synchronized List<UiGlyph> getGlyphs() {
429 checkPermission(GLYPH_READ);
430 return ImmutableList.copyOf(glyphs);
431 }
432
433 @Override
Thomas Vachuska2b4de872021-03-30 16:31:34 -0700434 public synchronized List<UiTopoHighlighterFactory> getTopoHighlighterFactories() {
435 checkPermission(UI_READ);
436 return ImmutableList.copyOf(highlighterFactories);
437 }
438
439 @Override
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800440 public synchronized UiExtension getViewExtension(String viewId) {
Heedo Kang4a47a302016-02-29 17:40:23 +0900441 checkPermission(UI_READ);
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800442 return views.get(viewId);
443 }
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700444
Thomas Vachuska0af26912016-03-21 21:37:30 -0700445 @Override
Simon Hunt23f9c7b2017-07-10 20:00:30 -0700446 public synchronized LionBundle getNavLionBundle() {
447 return navLion;
448 }
449
450 @Override
Thomas Vachuska1b1355d2018-02-06 16:53:58 -0800451 public void refreshModel() {
452 sharedModel.reload();
453 }
454
455 @Override
Thomas Vachuska0af26912016-03-21 21:37:30 -0700456 public Set<String> getUserNames() {
457 ImmutableSet.Builder<String> builder = ImmutableSet.builder();
458 prefs.keySet().forEach(k -> builder.add(userName(k)));
459 return builder.build();
460 }
461
462 @Override
Simon Huntcfef6f92017-07-25 16:58:42 -0700463 public Map<String, ObjectNode> getPreferences(String username) {
Thomas Vachuska0af26912016-03-21 21:37:30 -0700464 ImmutableMap.Builder<String, ObjectNode> builder = ImmutableMap.builder();
465 prefs.entrySet().stream()
Simon Huntcfef6f92017-07-25 16:58:42 -0700466 .filter(e -> e.getKey().startsWith(username + SLASH))
Thomas Vachuska0af26912016-03-21 21:37:30 -0700467 .forEach(e -> builder.put(keyName(e.getKey()), e.getValue()));
468 return builder.build();
469 }
470
471 @Override
Simon Huntcfef6f92017-07-25 16:58:42 -0700472 public ObjectNode getPreference(String username, String key) {
473 return prefs.get(key(username, key));
474 }
475
476 @Override
477 public void setPreference(String username, String key, ObjectNode value) {
Thomas Vachuska99b7bbe2018-02-01 15:29:46 -0800478 if (value != null) {
479 prefs.put(key(username, key), value);
480 } else {
481 prefs.remove(key(username, key));
482 }
Thomas Vachuska0af26912016-03-21 21:37:30 -0700483 }
484
Simon Huntc54cd1b2015-05-11 13:43:44 -0700485 // =====================================================================
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700486 // Provisional tracking of sprite definitions
Simon Huntc54cd1b2015-05-11 13:43:44 -0700487
488 private final Map<String, JsonNode> sprites = Maps.newHashMap();
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700489
490 @Override
491 public Set<String> getNames() {
492 return ImmutableSet.copyOf(sprites.keySet());
493 }
494
495 @Override
496 public void put(String name, JsonNode spriteData) {
Simon Huntfd8c7d72015-04-14 17:53:37 -0700497 log.info("Registered sprite definition [{}]", name);
Thomas Vachuska9ed335b2015-04-14 12:07:47 -0700498 sprites.put(name, spriteData);
499 }
500
501 @Override
502 public JsonNode get(String name) {
503 return sprites.get(name);
504 }
505
Thomas Vachuska0af26912016-03-21 21:37:30 -0700506 private String key(String userName, String keyName) {
Simon Hunt3678c2a2016-03-28 14:48:07 -0700507 return userName + SLASH + keyName;
Thomas Vachuska0af26912016-03-21 21:37:30 -0700508 }
509
Simon Hunt3678c2a2016-03-28 14:48:07 -0700510
Thomas Vachuska0af26912016-03-21 21:37:30 -0700511 private String userName(String key) {
Simon Hunt3678c2a2016-03-28 14:48:07 -0700512 return key.split(SLASH)[IDX_USER];
Thomas Vachuska0af26912016-03-21 21:37:30 -0700513 }
514
515 private String keyName(String key) {
Simon Hunt3678c2a2016-03-28 14:48:07 -0700516 return key.split(SLASH)[IDX_KEY];
Thomas Vachuska0af26912016-03-21 21:37:30 -0700517 }
518
Simon Hunt1169c952017-06-05 11:20:11 -0700519
520 // =====================================================================
521 // UiTokenService
522
523 @Override
524 public UiSessionToken issueToken(String username) {
525 UiSessionToken token = new UiSessionToken(tokenGen.nextSessionId());
526 tokens.put(token, username);
527 log.debug("UiSessionToken issued: {}", token);
528 return token;
529 }
530
531 @Override
532 public void revokeToken(UiSessionToken token) {
533 if (token != null) {
534 tokens.remove(token);
535 log.debug("UiSessionToken revoked: {}", token);
536 }
537 }
538
539 @Override
540 public boolean isTokenValid(UiSessionToken token) {
541 return token != null && tokens.containsKey(token);
542 }
543
544 private final class SessionTokenGenerator {
545 private final SecureRandom random = new SecureRandom();
546
547 /*
548 This works by choosing 130 bits from a cryptographically secure
549 random bit generator, and encoding them in base-32.
550
551 128 bits is considered to be cryptographically strong, but each
552 digit in a base 32 number can encode 5 bits, so 128 is rounded up
553 to the next multiple of 5.
554
555 This encoding is compact and efficient, with 5 random bits per
556 character. Compare this to a random UUID, which only has 3.4 bits
557 per character in standard layout, and only 122 random bits in total.
558
559 Note that SecureRandom objects are expensive to initialize, so
560 we'll want to keep it around and re-use it.
561 */
562
563 private String nextSessionId() {
564 return new BigInteger(130, random).toString(32);
565 }
566 }
567
Thomas Vachuska0af26912016-03-21 21:37:30 -0700568 // Auxiliary listener to preference map events.
569 private class InternalPrefsListener
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700570 implements MapEventListener<String, ObjectNode> {
Thomas Vachuska0af26912016-03-21 21:37:30 -0700571 @Override
Madan Jampani7b93ceb2016-05-04 09:58:40 -0700572 public void event(MapEvent<String, ObjectNode> event) {
Madan Jampanibf8ee802016-05-04 14:07:36 -0700573 eventHandlingExecutor.execute(() -> {
574 String userName = userName(event.key());
575 if (event.type() == MapEvent.Type.INSERT || event.type() == MapEvent.Type.UPDATE) {
576 UiWebSocketServlet.sendToUser(userName, UPDATE_PREFS, jsonPrefs());
577 }
578 });
Thomas Vachuska0af26912016-03-21 21:37:30 -0700579 }
580
581 private ObjectNode jsonPrefs() {
582 ObjectNode json = mapper.createObjectNode();
Simon Hunt1169c952017-06-05 11:20:11 -0700583 prefs.forEach((key, value) -> json.set(keyName(key), value));
Thomas Vachuska0af26912016-03-21 21:37:30 -0700584 return json;
585 }
586 }
Thomas Vachuskafe8c98a2015-02-04 01:24:32 -0800587}