blob: cdcfb0cc6aca417291ade78e9b46fb7f6e19b834 [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.ctl;
19
20import org.apache.thrift.TException;
21import org.onosproject.bmv2.thriftapi.SimplePreLAG;
22import org.onosproject.drivers.bmv2.api.Bmv2DeviceAgent;
23import org.onosproject.drivers.bmv2.api.runtime.Bmv2PreGroup;
24import org.onosproject.drivers.bmv2.api.runtime.Bmv2PreNode;
25import org.onosproject.drivers.bmv2.api.runtime.Bmv2RuntimeException;
26import org.onosproject.drivers.bmv2.impl.Bmv2PreGroupTranslatorImpl;
27import org.onosproject.net.DeviceId;
28import org.slf4j.Logger;
29import org.slf4j.LoggerFactory;
30
31import java.io.IOException;
32import java.util.List;
33
34import static com.google.common.base.Preconditions.checkNotNull;
35import static org.onosproject.drivers.bmv2.ctl.Bmv2TExceptionParser.parseTException;
36
37/**
38 * Implementation of a Thrift client to control a BMv2 device.
39 */
40final class Bmv2DeviceThriftClient implements Bmv2DeviceAgent {
41
42 // FIXME: make context_id arbitrary for each call
43 // See: https://github.com/p4lang/behavioral-model/blob/master/modules/bm_sim/include/bm_sim/context.h
44 private static final int CONTEXT_ID = 0;
45 private static final String DEFAULT_LAG_MAP = "";
46 private final Logger log = LoggerFactory.getLogger(this.getClass());
47 private final SimplePreLAG.Iface simplePreLagClient;
48 private final DeviceId deviceId;
49
50 // ban constructor
51 protected Bmv2DeviceThriftClient(DeviceId deviceId, SimplePreLAG.Iface simplePreLagClient) {
52 this.deviceId = deviceId;
53 this.simplePreLagClient = simplePreLagClient;
54 }
55
56 @Override
57 public DeviceId deviceId() {
58 return deviceId;
59 }
60
61 @Override
62 public Bmv2PreGroup writePreGroup(Bmv2PreGroup preGroup) throws Bmv2RuntimeException {
63 log.debug("Creating a multicast group... > deviceId={}, {}", deviceId, preGroup);
64
65 GroupRollbackMachine groupRollbackMachine = new GroupRollbackMachine(preGroup);
66 try {
67 //first create mc group
68 preGroup.setNativeGroupHandle(createMcGroup(preGroup.groupId()));
69 groupRollbackMachine.setState(GroupOperationState.GROUP_CREATED);
70 //create mc nodes
71 createMcNodesOfGroup(preGroup);
72 groupRollbackMachine.setState(GroupOperationState.NODES_CREATED);
73 //associate nodes with group
74 associateMcNodesOfGroup(preGroup);
75 groupRollbackMachine.setState(GroupOperationState.NODES_ASSOCIATED);
76
77 log.debug("Multicast group created successfully. deviceId={}, {}", deviceId, preGroup);
78
79 return preGroup;
80 } finally {
81 groupRollbackMachine.rollbackIfNecessary();
82 }
83 }
84
85 @Override
86 public void deletePreGroup(Bmv2PreGroup preGroup) throws Bmv2RuntimeException {
87 log.debug("Deleting a multicast group... > deviceId={}, {}", deviceId, preGroup);
88 //disassociate mc nodes from group
89 disassociateMcNodesOfGroup(preGroup);
90 //delete mc nodes
91 deleteMcNodesOfGroup(preGroup);
92 //delete group
93 deleteMcGroup(preGroup);
94
95 log.debug("Multicast group deleted. deviceId={}, {}", deviceId, preGroup);
96 }
97
98 @Override
99 public List<Bmv2PreGroup> getPreGroups() throws Bmv2RuntimeException {
100 try {
101 String entries = simplePreLagClient.bm_mc_get_entries(CONTEXT_ID);
102 return Bmv2PreGroupTranslatorImpl.translate(entries);
103
104 } catch (TException | IOException e) {
105 log.debug("Exception while getting multicast groups. deviceId={}", deviceId, e);
106
107 if (e instanceof TException) {
108 throw parseTException((TException) e);
109 } else {
110 throw new Bmv2RuntimeException(e);
111 }
112 }
113 }
114
115 /**
116 * Creates multicast nodes one by one.
117 * Node handles obtained as the results of node creation operations are stored
118 * in given Bmv2PreGroup object.
119 *
120 * @param preGroup Bmv2PreGroup object
121 * @throws Bmv2RuntimeException
122 */
123 private void createMcNodesOfGroup(Bmv2PreGroup preGroup) throws Bmv2RuntimeException {
124 for (Bmv2PreNode node : preGroup.nodes().nodes()) {
125 node.setL1Handle(createMcNode(node));
126 }
127 }
128
129 /**
130 * Associates multicast nodes with a group one by one.
131 *
132 * @param preGroup Bmv2PreGroup object
133 * @throws Bmv2RuntimeException
134 */
135 private void associateMcNodesOfGroup(Bmv2PreGroup preGroup) throws Bmv2RuntimeException {
136 int nativeGroupHandle = preGroup.nativeGroupHandle();
137 for (Bmv2PreNode node : preGroup.nodes().nodes()) {
138 associateMcNode(nativeGroupHandle, node);
139 }
140 }
141
142 /**
143 * Deletes multicast nodes one by one.
144 *
145 * @param preGroup Bmv2PreGroup object
146 * @throws Bmv2RuntimeException
147 */
148 private void deleteMcNodesOfGroup(Bmv2PreGroup preGroup) throws Bmv2RuntimeException {
149 for (Bmv2PreNode node : preGroup.nodes().nodes()) {
150 destroyMcNode(node);
151 }
152 }
153
154 /**
155 * Disassociates multicast nodes from a group one by one.
156 *
157 * @param preGroup Bmv2PreGroup object
158 * @throws Bmv2RuntimeException
159 */
160 private void disassociateMcNodesOfGroup(Bmv2PreGroup preGroup) throws Bmv2RuntimeException {
161 int nativeGroupHandle = preGroup.nativeGroupHandle();
162 for (Bmv2PreNode node : preGroup.nodes().nodes()) {
163 disassociateMcNode(nativeGroupHandle, node);
164 }
165 }
166
167
168 /**
169 * Creates a multicast group with specified group Id.
170 *
171 * @param groupId identifier of a group
172 * @return group handle (BMv2 specific identifier associated with the group)
173 * @throws Bmv2RuntimeException
174 */
175 private int createMcGroup(int groupId) throws Bmv2RuntimeException {
176 log.debug("Creating the multicast group... > deviceId={}, groupId={}", deviceId, groupId);
177 try {
178 return simplePreLagClient.bm_mc_mgrp_create(CONTEXT_ID, groupId);
179 } catch (TException e) {
180 log.debug("Exception during creating multicast group. deviceId={}, groupId={}", deviceId, groupId);
181 throw parseTException(e);
182 }
183 }
184
185 /**
186 * Deletes a multicast group from a BMv2 device.
187 *
188 * @param preGroup
189 * @throws Bmv2RuntimeException
190 */
191 private void deleteMcGroup(Bmv2PreGroup preGroup) throws Bmv2RuntimeException {
192 log.debug("Destroying the multicast group... > deviceId={}, groupId={}, groupHandle={}",
193 deviceId, preGroup.groupId(), preGroup.nativeGroupHandle());
194 try {
195 simplePreLagClient.bm_mc_mgrp_destroy(CONTEXT_ID, preGroup.nativeGroupHandle());
196 } catch (TException e) {
197 log.debug("Exception during destroying multicast group. deviceId={}, groupId={}, groupHandle={}",
198 deviceId, preGroup.groupId(), preGroup.nativeGroupHandle());
199 throw parseTException(e);
200 }
201 }
202
203 /**
204 * Creates a multicast node on the BMv2 device.
205 *
206 * @param node Bmv2PreNode
207 * @return L1 handle
208 * @throws Bmv2RuntimeException
209 */
210 private int createMcNode(Bmv2PreNode node) throws Bmv2RuntimeException {
211 log.debug("Creating the multicast node... > deviceId={}, {}", deviceId, node);
212 try {
213 return simplePreLagClient.bm_mc_node_create(CONTEXT_ID, node.rid(), node.portMap(), DEFAULT_LAG_MAP);
214 } catch (TException e) {
215 log.debug("Exception during creating multicast node: {}", node);
216 throw parseTException(e);
217 }
218 }
219
220 /**
221 * Associates a multicast node with a group.
222 *
223 * @param groupHandle handle of the group that the node will be associated with
224 * @param node Bmv2PreNode
225 * @throws Bmv2RuntimeException
226 */
227 private void associateMcNode(int groupHandle, Bmv2PreNode node) throws Bmv2RuntimeException {
228 log.debug("Associating the multicast node with the group... > deviceId={}, groupHandle:{}, node:{}",
229 deviceId, groupHandle, node);
230 try {
231 simplePreLagClient.bm_mc_node_associate(CONTEXT_ID, groupHandle, node.l1Handle());
232 } catch (TException e) {
233 log.debug("Exception during associating multicast node with group. deviceId={} groupHandle:{}, node:{}",
234 deviceId, groupHandle, node);
235 throw parseTException(e);
236 }
237 }
238
239 /**
240 * Disassociates a multicast node from a group.
241 *
242 * @param groupHandle handle of the group that the node will be disassociated from
243 * @param node Bmv2PreNode
244 * @throws Bmv2RuntimeException
245 */
246 private void disassociateMcNode(int groupHandle, Bmv2PreNode node) throws Bmv2RuntimeException {
247 log.debug("Disassociating the multicast node from the group... > deviceId={}, groupHandle:{}, node:{}",
248 deviceId, groupHandle, node);
249 try {
250 simplePreLagClient.bm_mc_node_dissociate(CONTEXT_ID, groupHandle, node.l1Handle());
251 } catch (TException e) {
252 log.debug("Failed to disassociate multicast node from group. deviceId={} groupHandle:{}, node:{}",
253 deviceId, groupHandle, node);
254 throw parseTException(e);
255 }
256 }
257
258 /**
259 * Destroys the multicast node in a BMv2 device.
260 *
261 * @param node PRE node which is about to be destroyed
262 * @throws Bmv2RuntimeException
263 */
264 private void destroyMcNode(Bmv2PreNode node) throws Bmv2RuntimeException {
265 log.debug("Destroying the multicast node... > deviceId={}, node:{}", deviceId, node);
266 try {
267 simplePreLagClient.bm_mc_node_destroy(CONTEXT_ID, node.l1Handle());
268 } catch (TException e) {
269 log.debug("Exception during destroying multicast node. deviceId={}, node:{}", deviceId, node);
270 throw parseTException(e);
271 }
272 }
273
274
275 /**
276 * Defines identifiers of main group operation steps.
277 */
278 private enum GroupOperationState {
279 IDLE, // nothing has been done
280 GROUP_CREATED, //the last successful step is group creation
281 NODES_CREATED, //the last successful step is node creation
282 NODES_ASSOCIATED //the last successful step is node association.
283 }
284
285 /**
286 * Implementation of a simple state machine to keep track of complex (non-atomic) operations on groups and
287 * to execute essential rollback steps accordingly.
288 * For example, creating a multicast group is composed of multiple steps:
289 * 1- Group creation
290 * 2- Node creation
291 * 3- Node association
292 * Each step associates with a GroupOperationState to keep track of group creation operation.
293 * A rollback flow is executed with respect to the current state.
294 */
295 private class GroupRollbackMachine {
296 Bmv2PreGroup preGroup;
297 //indicates the last successful step
298 GroupOperationState state = GroupOperationState.IDLE;
299
300 private GroupRollbackMachine() {
301 //hidden constructor
302 }
303
304 public GroupRollbackMachine(Bmv2PreGroup preGroup) {
305 this.preGroup = preGroup;
306 }
307
308 GroupOperationState state() {
309 return state;
310 }
311
312 void setState(GroupOperationState state) {
313 this.state = checkNotNull(state);
314 }
315
316 /**
317 * Checks the state and executes necessary rollback flow if necessary.
318 */
319 void rollbackIfNecessary() {
320 switch (state) {
321 case GROUP_CREATED:
322 //means node creation failed. Delete already created nodes and the group
323 onGroupCreated();
324 break;
325 case NODES_CREATED:
326 //means node association failed.
327 //Disassociate already associated nodes then delete nodes and the group.
328 onNodesCreated();
329 break;
330 default:
331 //do nothing in IDLE and NODES_ASSOCIATED states. They do not signify a failure.
332 break;
333 }
334 }
335
336 /**
337 * Executes necessary steps in case the last successful step is group creation.
338 * This means one of the node creation operations has been failed and all previous steps should rollback.
339 */
340 private void onGroupCreated() {
341 log.warn("One of the steps of mc group creation operation has been failed." +
342 "Rolling back in state {}...> deviceId={}, groupId={}",
343 state, deviceId, preGroup.groupId());
344 deleteNodes(preGroup);
345 deleteGroup(preGroup);
346 }
347
348 /**
349 * Executes necessary steps in case the last successful step is node creation.
350 * This means one of the node association operations has been failed and all previous steps should rollback.
351 */
352 private void onNodesCreated() {
353 log.warn("One of the steps of mc group creation operation has been failed." +
354 "Rolling back in state {}...> deviceId={}, groupId={}",
355 state, deviceId, preGroup.groupId());
356 disassociateNodes(preGroup);
357 deleteNodes(preGroup);
358 deleteGroup(preGroup);
359 }
360
361 /**
362 * Deletes a group in the scope of rollback operation.
363 */
364 private void deleteGroup(Bmv2PreGroup preGroup) {
365 try {
366 deleteMcGroup(preGroup);
367 } catch (Bmv2RuntimeException e) {
368 log.error("Unable to destroy multicast group in the scope of rollback operation." +
369 "deviceId={}, groupId={}", deviceId, preGroup.groupId());
370 }
371 }
372
373
374 /**
375 * Disassociates all nodes from their group in the scope of rollback operation.
376 */
377 private void disassociateNodes(Bmv2PreGroup preGroup) {
378 preGroup.nodes().nodes().forEach(node -> {
379 try {
380 disassociateMcNode(preGroup.nativeGroupHandle(), node);
381 } catch (Bmv2RuntimeException e) {
382 log.error("Unable to disassociate the node in the scope of rollback operation." +
383 "deviceId={}, groupHandle={}, l1Handle={}",
384 deviceId, preGroup.nativeGroupHandle(), node.l1Handle(), e);
385 }
386 });
387 }
388
389 /**
390 * Deletes all nodes of a group in the scope of rollback operation.
391 */
392 private void deleteNodes(Bmv2PreGroup preGroup) {
393 //filter created nodes and destroy them
394 preGroup.nodes().nodes().stream()
395 .filter(node -> node.l1Handle() != null)
396 .forEach(node -> {
397 try {
398 destroyMcNode(node);
399 } catch (Bmv2RuntimeException e) {
400 log.error("Unable to destroy the node in the scope of rollback operation." +
401 "deviceId={}, l1Handle={}", deviceId, node.l1Handle(), e);
402 }
403 });
404 }
405 }
406}