blob: e9d76fb491e0acc507d313074eba7c32cf59e355 [file] [log] [blame]
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -08001/*
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 */
16package org.onosproject.net.group.impl;
17
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080018import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
Charles Chan0c7c43b2016-01-14 17:39:20 -080021import org.apache.felix.scr.annotations.Modified;
22import org.apache.felix.scr.annotations.Property;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080023import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.apache.felix.scr.annotations.Service;
Charles Chan0c7c43b2016-01-14 17:39:20 -080026import org.onosproject.cfg.ComponentConfigService;
Thomas Vachuska42e8cce2015-07-29 19:25:18 -070027import org.onosproject.net.provider.AbstractListenerProviderRegistry;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080028import org.onosproject.core.ApplicationId;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080029import org.onosproject.net.DeviceId;
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080030import org.onosproject.net.device.DeviceEvent;
31import org.onosproject.net.device.DeviceListener;
32import org.onosproject.net.device.DeviceService;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080033import org.onosproject.net.group.Group;
34import org.onosproject.net.group.GroupBuckets;
35import org.onosproject.net.group.GroupDescription;
36import org.onosproject.net.group.GroupEvent;
37import org.onosproject.net.group.GroupKey;
38import org.onosproject.net.group.GroupListener;
39import org.onosproject.net.group.GroupOperation;
40import org.onosproject.net.group.GroupOperations;
41import org.onosproject.net.group.GroupProvider;
42import org.onosproject.net.group.GroupProviderRegistry;
43import org.onosproject.net.group.GroupProviderService;
44import org.onosproject.net.group.GroupService;
45import org.onosproject.net.group.GroupStore;
46import org.onosproject.net.group.GroupStore.UpdateType;
47import org.onosproject.net.group.GroupStoreDelegate;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080048import org.onosproject.net.provider.AbstractProviderService;
Charles Chan0c7c43b2016-01-14 17:39:20 -080049import org.osgi.service.component.ComponentContext;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080050import org.slf4j.Logger;
51
Thomas Vachuska42e8cce2015-07-29 19:25:18 -070052import java.util.Collection;
53import java.util.Collections;
Charles Chan0c7c43b2016-01-14 17:39:20 -080054import java.util.Dictionary;
Thomas Vachuska42e8cce2015-07-29 19:25:18 -070055
Charles Chan0c7c43b2016-01-14 17:39:20 -080056import static com.google.common.base.Strings.isNullOrEmpty;
Changhoon Yoon541ef712015-05-23 17:18:34 +090057import static org.onosproject.security.AppGuard.checkPermission;
Thomas Vachuska42e8cce2015-07-29 19:25:18 -070058import static org.slf4j.LoggerFactory.getLogger;
Changhoon Yoonb856b812015-08-10 03:47:19 +090059import static org.onosproject.security.AppPermission.Type.*;
60
Changhoon Yoon541ef712015-05-23 17:18:34 +090061
62
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080063/**
64 * Provides implementation of the group service APIs.
65 */
66@Component(immediate = true)
67@Service
68public class GroupManager
Thomas Vachuska42e8cce2015-07-29 19:25:18 -070069 extends AbstractListenerProviderRegistry<GroupEvent, GroupListener,
70 GroupProvider, GroupProviderService>
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080071 implements GroupService, GroupProviderRegistry {
72
73 private final Logger log = getLogger(getClass());
74
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080075 private final GroupStoreDelegate delegate = new InternalGroupStoreDelegate();
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080076 private final DeviceListener deviceListener = new InternalDeviceListener();
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080077
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected GroupStore store;
80
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080082 protected DeviceService deviceService;
83
Charles Chan0c7c43b2016-01-14 17:39:20 -080084 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected ComponentConfigService cfgService;
86
87 @Property(name = "purgeOnDisconnection", boolValue = false,
88 label = "Purge entries associated with a device when the device goes offline")
89 private boolean purgeOnDisconnection = false;
Andrea Campanella6ee73922016-02-03 18:00:00 -080090 private final GroupDriverProvider defaultProvider = new GroupDriverProvider();
Charles Chan0c7c43b2016-01-14 17:39:20 -080091
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080092 @Activate
Charles Chan0c7c43b2016-01-14 17:39:20 -080093 public void activate(ComponentContext context) {
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080094 store.setDelegate(delegate);
95 eventDispatcher.addSink(GroupEvent.class, listenerRegistry);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -080096 deviceService.addListener(deviceListener);
Charles Chan0c7c43b2016-01-14 17:39:20 -080097 cfgService.registerProperties(getClass());
98 modified(context);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -080099 log.info("Started");
100 }
101
102 @Deactivate
103 public void deactivate() {
Charles Chan0c7c43b2016-01-14 17:39:20 -0800104 cfgService.unregisterProperties(getClass(), false);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800105 store.unsetDelegate(delegate);
106 eventDispatcher.removeSink(GroupEvent.class);
107 log.info("Stopped");
108 }
109
Charles Chan0c7c43b2016-01-14 17:39:20 -0800110 @Modified
111 public void modified(ComponentContext context) {
112 if (context != null) {
113 readComponentConfiguration(context);
114 }
Andrea Campanella6ee73922016-02-03 18:00:00 -0800115 defaultProvider.init(deviceService);
116 }
117
118 @Override
119 protected GroupProvider defaultProvider() {
120 return defaultProvider;
Charles Chan0c7c43b2016-01-14 17:39:20 -0800121 }
122
123 /**
124 * Extracts properties from the component configuration context.
125 *
126 * @param context the component context
127 */
128 private void readComponentConfiguration(ComponentContext context) {
129 Dictionary<?, ?> properties = context.getProperties();
130 Boolean flag;
131
132 flag = isPropertyEnabled(properties, "purgeOnDisconnection");
133 if (flag == null) {
134 log.info("PurgeOnDisconnection is not configured, " +
135 "using current value of {}", purgeOnDisconnection);
136 } else {
137 purgeOnDisconnection = flag;
138 log.info("Configured. PurgeOnDisconnection is {}",
139 purgeOnDisconnection ? "enabled" : "disabled");
140 }
141 }
142
143 /**
144 * Check property name is defined and set to true.
145 *
146 * @param properties properties to be looked up
147 * @param propertyName the name of the property to look up
148 * @return value when the propertyName is defined or return null
149 */
150 private static Boolean isPropertyEnabled(Dictionary<?, ?> properties,
151 String propertyName) {
152 Boolean value = null;
153 try {
154 String s = (String) properties.get(propertyName);
155 value = isNullOrEmpty(s) ? null : s.trim().equals("true");
156 } catch (ClassCastException e) {
157 // No propertyName defined.
158 value = null;
159 }
160 return value;
161 }
162
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800163 /**
164 * Create a group in the specified device with the provided parameters.
165 *
166 * @param groupDesc group creation parameters
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800167 */
168 @Override
169 public void addGroup(GroupDescription groupDesc) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900170 checkPermission(GROUP_WRITE);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800171 store.storeGroupDescription(groupDesc);
172 }
173
174 /**
175 * Return a group object associated to an application cookie.
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700176 * <p>
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800177 * NOTE1: The presence of group object in the system does not
178 * guarantee that the "group" is actually created in device.
179 * GROUP_ADDED notification would confirm the creation of
180 * this group in data plane.
181 *
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700182 * @param deviceId device identifier
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800183 * @param appCookie application cookie to be used for lookup
184 * @return group associated with the application cookie or
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700185 * NULL if Group is not found for the provided cookie
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800186 */
187 @Override
188 public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900189 checkPermission(GROUP_READ);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800190 return store.getGroup(deviceId, appCookie);
191 }
192
193 /**
194 * Append buckets to existing group. The caller can optionally
195 * associate a new cookie during this updation. GROUP_UPDATED or
196 * GROUP_UPDATE_FAILED notifications would be provided along with
197 * cookie depending on the result of the operation on the device.
198 *
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700199 * @param deviceId device identifier
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800200 * @param oldCookie cookie to be used to retrieve the existing group
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700201 * @param buckets immutable list of group bucket to be added
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800202 * @param newCookie immutable cookie to be used post update operation
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700203 * @param appId Application Id
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800204 */
205 @Override
206 public void addBucketsToGroup(DeviceId deviceId,
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700207 GroupKey oldCookie,
208 GroupBuckets buckets,
209 GroupKey newCookie,
210 ApplicationId appId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900211 checkPermission(GROUP_WRITE);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800212 store.updateGroupDescription(deviceId,
213 oldCookie,
214 UpdateType.ADD,
215 buckets,
216 newCookie);
217 }
218
219 /**
220 * Remove buckets from existing group. The caller can optionally
221 * associate a new cookie during this updation. GROUP_UPDATED or
222 * GROUP_UPDATE_FAILED notifications would be provided along with
223 * cookie depending on the result of the operation on the device.
224 *
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700225 * @param deviceId device identifier
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800226 * @param oldCookie cookie to be used to retrieve the existing group
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700227 * @param buckets immutable list of group bucket to be removed
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800228 * @param newCookie immutable cookie to be used post update operation
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700229 * @param appId Application Id
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800230 */
231 @Override
232 public void removeBucketsFromGroup(DeviceId deviceId,
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700233 GroupKey oldCookie,
234 GroupBuckets buckets,
235 GroupKey newCookie,
236 ApplicationId appId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900237 checkPermission(GROUP_WRITE);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800238 store.updateGroupDescription(deviceId,
239 oldCookie,
240 UpdateType.REMOVE,
241 buckets,
242 newCookie);
243 }
244
245 /**
246 * Delete a group associated to an application cookie.
247 * GROUP_DELETED or GROUP_DELETE_FAILED notifications would be
248 * provided along with cookie depending on the result of the
249 * operation on the device.
250 *
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700251 * @param deviceId device identifier
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800252 * @param appCookie application cookie to be used for lookup
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700253 * @param appId Application Id
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800254 */
255 @Override
256 public void removeGroup(DeviceId deviceId,
257 GroupKey appCookie,
258 ApplicationId appId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900259 checkPermission(GROUP_WRITE);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800260 store.deleteGroupDescription(deviceId, appCookie);
261 }
262
263 /**
264 * Retrieve all groups created by an application in the specified device
265 * as seen by current controller instance.
266 *
267 * @param deviceId device identifier
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700268 * @param appId application id
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800269 * @return collection of immutable group objects created by the application
270 */
271 @Override
272 public Iterable<Group> getGroups(DeviceId deviceId,
273 ApplicationId appId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900274 checkPermission(GROUP_READ);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800275 return store.getGroups(deviceId);
276 }
277
Jonathan Hart32600692015-03-09 10:38:40 -0700278 @Override
279 public Iterable<Group> getGroups(DeviceId deviceId) {
Changhoon Yoonb856b812015-08-10 03:47:19 +0900280 checkPermission(GROUP_READ);
Jonathan Hart32600692015-03-09 10:38:40 -0700281 return store.getGroups(deviceId);
282 }
283
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800284 @Override
285 protected GroupProviderService createProviderService(GroupProvider provider) {
286 return new InternalGroupProviderService(provider);
287 }
288
289 private class InternalGroupStoreDelegate implements GroupStoreDelegate {
290 @Override
291 public void notify(GroupEvent event) {
292 final Group group = event.subject();
293 GroupProvider groupProvider =
294 getProvider(group.deviceId());
295 GroupOperations groupOps = null;
296 switch (event.type()) {
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700297 case GROUP_ADD_REQUESTED:
298 log.debug("GROUP_ADD_REQUESTED for Group {} on device {}",
299 group.id(), group.deviceId());
300 GroupOperation groupAddOp = GroupOperation.
301 createAddGroupOperation(group.id(),
302 group.type(),
303 group.buckets());
304 groupOps = new GroupOperations(
305 Collections.singletonList(groupAddOp));
306 groupProvider.performGroupOperation(group.deviceId(), groupOps);
307 break;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800308
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700309 case GROUP_UPDATE_REQUESTED:
310 log.debug("GROUP_UPDATE_REQUESTED for Group {} on device {}",
311 group.id(), group.deviceId());
312 GroupOperation groupModifyOp = GroupOperation.
313 createModifyGroupOperation(group.id(),
314 group.type(),
315 group.buckets());
316 groupOps = new GroupOperations(
317 Collections.singletonList(groupModifyOp));
318 groupProvider.performGroupOperation(group.deviceId(), groupOps);
319 break;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800320
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700321 case GROUP_REMOVE_REQUESTED:
322 log.debug("GROUP_REMOVE_REQUESTED for Group {} on device {}",
323 group.id(), group.deviceId());
324 GroupOperation groupDeleteOp = GroupOperation.
325 createDeleteGroupOperation(group.id(),
326 group.type());
327 groupOps = new GroupOperations(
328 Collections.singletonList(groupDeleteOp));
329 groupProvider.performGroupOperation(group.deviceId(), groupOps);
330 break;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800331
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700332 case GROUP_ADDED:
333 case GROUP_UPDATED:
334 case GROUP_REMOVED:
335 case GROUP_ADD_FAILED:
336 case GROUP_UPDATE_FAILED:
337 case GROUP_REMOVE_FAILED:
338 post(event);
339 break;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800340
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700341 default:
342 break;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800343 }
344 }
345 }
346
347 private class InternalGroupProviderService
348 extends AbstractProviderService<GroupProvider>
349 implements GroupProviderService {
350
351 protected InternalGroupProviderService(GroupProvider provider) {
352 super(provider);
353 }
354
355 @Override
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700356 public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
sangho7ff01812015-02-09 16:21:53 -0800357 store.groupOperationFailed(deviceId, operation);
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800358 }
359
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800360 @Override
361 public void pushGroupMetrics(DeviceId deviceId,
362 Collection<Group> groupEntries) {
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700363 log.trace("Received group metrics from device {}", deviceId);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700364 checkValidity();
365 store.pushGroupMetrics(deviceId, groupEntries);
Srikanth Vavilapallied12ae52015-02-09 14:43:19 -0800366 }
367 }
368
369 private class InternalDeviceListener implements DeviceListener {
370
371 @Override
372 public void event(DeviceEvent event) {
373 switch (event.type()) {
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700374 case DEVICE_REMOVED:
375 case DEVICE_AVAILABILITY_CHANGED:
Charles Chan0c7c43b2016-01-14 17:39:20 -0800376 DeviceId deviceId = event.subject().id();
377 if (!deviceService.isAvailable(deviceId)) {
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700378 log.debug("Device {} became un available; clearing initial audit status",
379 event.type(), event.subject().id());
380 store.deviceInitialAuditCompleted(event.subject().id(), false);
Charles Chan0c7c43b2016-01-14 17:39:20 -0800381
382 if (purgeOnDisconnection) {
383 store.purgeGroupEntry(deviceId);
384 }
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700385 }
386 break;
Thomas Vachuska42e8cce2015-07-29 19:25:18 -0700387 default:
388 break;
Srikanth Vavilapalli45c27c82015-01-30 12:57:56 -0800389 }
390 }
391 }
392}