blob: 1ab518f42f49627a0d03a55db2fcc0fa5cc24266 [file] [log] [blame]
Esin Karaman971fb7f2017-12-28 13:44:52 +00001/*
2 * Copyright 2018-present Open Networking Foundation
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.drivers.bmv2;
19
20import com.google.common.cache.CacheBuilder;
21import com.google.common.cache.CacheLoader;
22import com.google.common.cache.LoadingCache;
23import com.google.common.collect.ImmutableList;
24import org.onosproject.core.GroupId;
25import org.onosproject.drivers.bmv2.api.Bmv2DeviceAgent;
26import org.onosproject.drivers.bmv2.api.Bmv2PreController;
27import org.onosproject.drivers.bmv2.api.runtime.Bmv2PreGroup;
28import org.onosproject.drivers.bmv2.api.runtime.Bmv2PreGroupHandle;
29import org.onosproject.drivers.bmv2.api.runtime.Bmv2RuntimeException;
30import org.onosproject.drivers.bmv2.impl.Bmv2PreGroupTranslatorImpl;
31import org.onosproject.drivers.bmv2.mirror.Bmv2PreGroupMirror;
32import org.onosproject.drivers.p4runtime.P4RuntimeGroupProgrammable;
33import org.onosproject.net.DeviceId;
34import org.onosproject.net.group.Group;
35import org.onosproject.net.group.GroupDescription;
36import org.onosproject.net.group.GroupOperation;
37import org.onosproject.net.group.GroupOperations;
38import org.slf4j.Logger;
39
40import java.util.Arrays;
41import java.util.Collection;
42import java.util.Collections;
43import java.util.concurrent.TimeUnit;
44import java.util.concurrent.locks.Lock;
45import java.util.concurrent.locks.ReentrantLock;
46import java.util.stream.Collectors;
47
48import static org.slf4j.LoggerFactory.getLogger;
49
50/**
51 * Implementation of the group programmable behaviour for BMv2.
52 */
53public class Bmv2GroupProgrammable extends P4RuntimeGroupProgrammable {
54
55 private static final Logger log = getLogger(Bmv2GroupProgrammable.class);
56
57 private static final int PRE_GROUP_LOCK_EXPIRE_TIME_IN_MIN = 10;
58
59 // Needed to synchronize operations over the same group.
60 private static final LoadingCache<Bmv2PreGroupHandle, Lock> PRE_GROUP_LOCKS = CacheBuilder.newBuilder()
61 .expireAfterAccess(PRE_GROUP_LOCK_EXPIRE_TIME_IN_MIN, TimeUnit.MINUTES)
62 .build(new CacheLoader<Bmv2PreGroupHandle, Lock>() {
63 @Override
64 public Lock load(Bmv2PreGroupHandle bmv2PreGroupHandle) {
65 return new ReentrantLock();
66 }
67 });
68
69 private Bmv2PreGroupMirror preGroupMirror;
70 private Bmv2PreController bmv2PreController;
71
72 @Override
73 protected boolean setupBehaviour() {
74 if (!super.setupBehaviour()) {
75 return false;
76 }
77
78 preGroupMirror = handler().get(Bmv2PreGroupMirror.class);
79 bmv2PreController = handler().get(Bmv2PreController.class);
80
81 return getBmv2DeviceAgent() != null
82 && preGroupMirror != null
83 && bmv2PreController != null;
84 }
85
86 @Override
87 public void performGroupOperation(DeviceId deviceId,
88 GroupOperations groupOps) {
89 if (!setupBehaviour()) {
90 return;
91 }
92 groupOps.operations().forEach(op -> processGroupOp(deviceId, op));
93 }
94
95
96 /**
97 * Fetches all groups of P4Runtime and BMv2 PRE in a device. Combines and returns them respectively.
98 *
99 * @return all the groups which are managed via both P4Runtime and BMv2 PRE
100 */
101 @Override
102 public Collection<Group> getGroups() {
103 //get groups managed via P4Runtime
104 Collection<Group> groups = getP4Groups();
105 //get groups managed via BMv2 Thrift
106 groups.addAll(getPreGroups());
107 return ImmutableList.copyOf(groups);
108 }
109
110 private Collection<Group> getP4Groups() {
111 return super.getGroups();
112 }
113
114 /**
115 * Returns BMv2 agent associated with a BMv2 device.
116 *
117 * @return BMv2 agent
118 */
119 private Bmv2DeviceAgent getBmv2DeviceAgent() {
120 return bmv2PreController.getPreClient(deviceId);
121 }
122
123 /**
124 * Retrieves groups of BMv2 PRE.
125 *
126 * @return collection of PRE groups
127 */
128 private Collection<Group> getPreGroups() {
129 if (!setupBehaviour()) {
130 return Collections.emptyList();
131 }
132 Bmv2DeviceAgent bmv2DeviceAgent = getBmv2DeviceAgent();
133
134 try {
135 return bmv2DeviceAgent.getPreGroups().stream()
136 .map(preGroup -> groupStore.getGroup(deviceId, GroupId.valueOf(preGroup.groupId())))
137 .collect(Collectors.toList());
138 } catch (Bmv2RuntimeException e) {
139 log.error("Exception while getting Bmv2 PRE groups of {}", deviceId, e);
140 return Collections.emptyList();
141 }
142 }
143
144 /**
145 * Checks whether specified group is a PRE group or not.
146 *
147 * @param group group
148 * @return Returns true iff this group is a PRE group; false otherwise.
149 */
150 private boolean isPreGroup(Group group) {
151 return group.type().equals(GroupDescription.Type.ALL);
152 }
153
154 /**
155 * Makes a decision between two methodologies over group type.
156 * A group of ALL type is evaluated by GroupProgrammable of BMv2;
157 * it is passed on to GroupProgrammable of P4Runtime otherwise.
158 *
159 * @param deviceId ID of the device on which the group is being accommodated.
160 * @param groupOp group operation
161 */
162 private void processGroupOp(DeviceId deviceId, GroupOperation groupOp) {
163 final Group group = groupStore.getGroup(deviceId, groupOp.groupId());
164
165 if (isPreGroup(group)) {
166 processPreGroupOp(deviceId, groupOp);
167 } else {
168 //means the group is managed via P4Runtime.
169 super.performGroupOperation(deviceId,
170 new GroupOperations(Arrays.asList(new GroupOperation[]{groupOp})));
171 }
172 }
173
174 private void processPreGroupOp(DeviceId deviceId, GroupOperation groupOp) {
175 if (!setupBehaviour()) {
176 return;
177 }
178
179 final Group group = groupStore.getGroup(deviceId, groupOp.groupId());
180
181 Bmv2PreGroup preGroup = Bmv2PreGroupTranslatorImpl.translate(group);
182
183 final Bmv2PreGroupHandle handle = Bmv2PreGroupHandle.of(deviceId, preGroup);
184
185 final Bmv2PreGroup groupOnDevice = preGroupMirror.get(handle);
186
187 PRE_GROUP_LOCKS.getUnchecked(handle).lock();
188 try {
189 switch (groupOp.opType()) {
190 case ADD:
191 onAdd(preGroup, handle);
192 break;
193 case MODIFY:
194 onModify(preGroup, groupOnDevice, handle);
195 break;
196 case DELETE:
197 onDelete(groupOnDevice, handle);
198 break;
199 default:
200 log.warn("PRE Group operation {} not supported", groupOp.opType());
201 }
202 } finally {
203 PRE_GROUP_LOCKS.getUnchecked(handle).unlock();
204 }
205 }
206
207 private void onAdd(Bmv2PreGroup preGroup, Bmv2PreGroupHandle handle) {
208 try {
209 writeGroup(preGroup, handle);
210 } catch (Bmv2RuntimeException e) {
211 log.error("Unable to create the PRE group with groupId={}. deviceId={}", preGroup.groupId(), deviceId, e);
212 }
213 }
214
215 private void onDelete(Bmv2PreGroup preGroupOnDevice, Bmv2PreGroupHandle handle) {
216 if (preGroupOnDevice == null) {
217 log.warn("Unable to delete the group. Nonexistent in the group mirror! deviceId={}", deviceId);
218 return;
219 }
220 try {
221 deleteGroup(preGroupOnDevice, handle);
222 } catch (Bmv2RuntimeException e) {
223 log.error("Unable to delete the group. deviceId={}", deviceId, e);
224 }
225 }
226
227 private void onModify(Bmv2PreGroup preGroup, Bmv2PreGroup preGroupOnDevice, Bmv2PreGroupHandle handle) {
228 if (preGroupOnDevice == null) {
229 log.warn("Unable to modify the group. Nonexistent in the group mirror! deviceId={}", deviceId);
230 return;
231 }
232 if (preGroup.equals(preGroupOnDevice)) {
233 return;
234 }
235 try {
236 deleteGroup(preGroupOnDevice, handle);
237 writeGroup(preGroup, handle);
238 } catch (Bmv2RuntimeException e) {
239 log.error("Unable to modify the group. deviceId={}, groupId={}", deviceId, preGroup.groupId(), e);
240 }
241 }
242
243 private void writeGroup(Bmv2PreGroup preGroup, Bmv2PreGroupHandle handle) throws Bmv2RuntimeException {
244 Bmv2DeviceAgent bmv2DeviceAgent = getBmv2DeviceAgent();
245 Bmv2PreGroup bmv2PreGroupCreated = bmv2DeviceAgent.writePreGroup(preGroup);
246 //put the created group into the mirror store
247 preGroupMirror.put(handle, bmv2PreGroupCreated);
248 }
249
250 private void deleteGroup(Bmv2PreGroup preGroupOnDevice, Bmv2PreGroupHandle handle) throws Bmv2RuntimeException {
251 Bmv2DeviceAgent bmv2DeviceAgent = getBmv2DeviceAgent();
252 bmv2DeviceAgent.deletePreGroup(preGroupOnDevice);
253 //remove the group from the mirror
254 preGroupMirror.remove(handle);
255 }
256
257}