blob: 60b0dbc3a15af95afb988a16cd8c253ff611999a [file] [log] [blame]
Andrea Campanella241896c2017-05-10 13:11:04 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Andrea Campanella241896c2017-05-10 13:11:04 -07003 *
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
Andrea Campanella0288c872017-08-07 18:32:51 +020017package org.onosproject.drivers.p4runtime;
Andrea Campanella241896c2017-05-10 13:11:04 -070018
Carmelo Cascone3977ea42019-02-28 13:43:42 -080019import org.onosproject.cluster.ClusterService;
Andrea Campanella241896c2017-05-10 13:11:04 -070020import org.onosproject.net.MastershipRole;
Carmelo Casconee5b28722018-06-22 17:28:28 +020021import org.onosproject.net.device.DeviceAgentListener;
Andrea Campanella241896c2017-05-10 13:11:04 -070022import org.onosproject.net.device.DeviceHandshaker;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080023import org.onosproject.net.pi.service.PiPipeconfWatchdogService;
Carmelo Cascone9e4972c2018-08-30 00:29:16 -070024import org.onosproject.net.provider.ProviderId;
Carmelo Cascone158b8c42018-07-04 19:42:37 +020025import org.onosproject.p4runtime.api.P4RuntimeClient;
Carmelo Cascone3977ea42019-02-28 13:43:42 -080026import org.onosproject.p4runtime.api.P4RuntimeClientKey;
Carmelo Cascone59f57de2017-07-11 19:55:09 -040027import org.onosproject.p4runtime.api.P4RuntimeController;
Andrea Campanella241896c2017-05-10 13:11:04 -070028
Carmelo Cascone3977ea42019-02-28 13:43:42 -080029import java.math.BigInteger;
Andrea Campanella241896c2017-05-10 13:11:04 -070030import java.util.concurrent.CompletableFuture;
31
Carmelo Cascone3977ea42019-02-28 13:43:42 -080032import static java.util.concurrent.CompletableFuture.completedFuture;
33
Andrea Campanella241896c2017-05-10 13:11:04 -070034/**
Carmelo Casconee3a7c742017-09-01 01:25:52 +020035 * Implementation of DeviceHandshaker for P4Runtime.
Andrea Campanella241896c2017-05-10 13:11:04 -070036 */
Carmelo Casconee3a7c742017-09-01 01:25:52 +020037public class P4RuntimeHandshaker extends AbstractP4RuntimeHandlerBehaviour implements DeviceHandshaker {
Andrea Campanella241896c2017-05-10 13:11:04 -070038
Carmelo Cascone3977ea42019-02-28 13:43:42 -080039 // This is needed to compute an election ID based on mastership term and
40 // preference. At the time of writing the practical maximum cluster size is
41 // 9. Since election IDs are 128bit numbers, we should'nt be too worried of
42 // being conservative when setting a static max size here. Making the
43 // cluster size dynamic would likely cause conflicts when generating
44 // election IDs (e.g. two nodes seeing different cluster sizes).
45 private static final int MAX_CLUSTER_SIZE = 20;
46
Andrea Campanella241896c2017-05-10 13:11:04 -070047 @Override
48 public CompletableFuture<Boolean> connect() {
Carmelo Casconee5b28722018-06-22 17:28:28 +020049 return CompletableFuture
Carmelo Cascone3977ea42019-02-28 13:43:42 -080050 .supplyAsync(this::createClient);
51 }
52
53 private boolean createClient() {
54 final P4RuntimeClientKey clientKey = clientKey();
55 if (clientKey == null) {
56 return false;
57 }
58 if (!handler().get(P4RuntimeController.class).createClient(clientKey)) {
59 log.debug("Unable to create client for {}", data().deviceId());
60 return false;
61 }
62 return true;
Andrea Campanella241896c2017-05-10 13:11:04 -070063 }
64
65 @Override
Carmelo Cascone9e4972c2018-08-30 00:29:16 -070066 public boolean isConnected() {
Carmelo Cascone3977ea42019-02-28 13:43:42 -080067 // This is based on the client key obtained from the current netcfg. If
68 // a client already exists for this device, but the netcfg with the
69 // server endpoints has changed, this will return false.
70 return getClientByKey() != null;
Carmelo Cascone9e4972c2018-08-30 00:29:16 -070071 }
72
73 @Override
Carmelo Cascone3977ea42019-02-28 13:43:42 -080074 public void disconnect() {
75 // This removes clients associated with this device ID, even if the
76 // netcfg has changed and so the client key for this device.
77 handler().get(P4RuntimeController.class).removeClient(data().deviceId());
78 }
79
80 @Override
81 public boolean isReachable() {
82 final P4RuntimeClient client = getClientByKey();
83 return client != null && client.isServerReachable();
84 }
85
86 @Override
87 public CompletableFuture<Boolean> probeReachability() {
88 final P4RuntimeClient client = getClientByKey();
Carmelo Cascone158b8c42018-07-04 19:42:37 +020089 if (client == null) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -080090 return completedFuture(false);
Carmelo Casconee5b28722018-06-22 17:28:28 +020091 }
Carmelo Cascone3977ea42019-02-28 13:43:42 -080092 return client.probeService();
Andrea Campanella241896c2017-05-10 13:11:04 -070093 }
94
95 @Override
Carmelo Cascone3977ea42019-02-28 13:43:42 -080096 public boolean isAvailable() {
97 // To be available, we require a session open (for packet in/out) and a
98 // pipeline config set.
99 final P4RuntimeClient client = getClientByKey();
100 if (client == null || !client.isServerReachable() || !client.isSessionOpen()) {
101 return false;
102 }
103 // Since we cannot probe the device, we rely on what's known by the
104 // pipeconf watchdog service.
105 return PiPipeconfWatchdogService.PipelineStatus.READY.equals(
106 handler().get(PiPipeconfWatchdogService.class)
107 .getStatus(data().deviceId()));
108 }
109
110 @Override
111 public CompletableFuture<Boolean> probeAvailability() {
112 // To be available, we require a session open (for packet in/out) and a
113 // pipeline config set.
114 final P4RuntimeClient client = getClientByKey();
115 if (client == null || !client.isServerReachable() || !client.isSessionOpen()) {
116 return completedFuture(false);
117 }
118 return client.isAnyPipelineConfigSet();
Andrea Campanella241896c2017-05-10 13:11:04 -0700119 }
120
121 @Override
Carmelo Casconee5b28722018-06-22 17:28:28 +0200122 public void roleChanged(MastershipRole newRole) {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800123 if (newRole.equals(MastershipRole.NONE)) {
124 final P4RuntimeClient client = getClientByKey();
125 if (client != null) {
126 log.info("Notified role NONE, closing session...");
127 client.closeSession();
128 }
129 } else {
130 throw new UnsupportedOperationException(
131 "Use preference-based way for setting MASTER or STANDBY roles");
132 }
133 }
134
135 @Override
136 public void roleChanged(int preference, long term) {
137 if (setupBehaviour()) {
138 final int clusterSize = handler().get(ClusterService.class)
139 .getNodes().size();
140 if (clusterSize > MAX_CLUSTER_SIZE) {
141 throw new IllegalStateException(
142 "Cluster too big! Maz size supported is " + MAX_CLUSTER_SIZE);
143 }
144 BigInteger electionId = BigInteger.valueOf(term)
145 .multiply(BigInteger.valueOf(MAX_CLUSTER_SIZE))
146 .subtract(BigInteger.valueOf(preference));
147 client.setMastership(preference == 0, electionId);
Yi Tseng3e7f1452017-10-20 10:31:53 -0700148 }
Andrea Campanella241896c2017-05-10 13:11:04 -0700149 }
Andrea Campanella1e573442018-05-17 17:07:13 +0200150
151 @Override
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700152 public MastershipRole getRole() {
Carmelo Cascone3977ea42019-02-28 13:43:42 -0800153 final P4RuntimeClient client = getClientByKey();
154 if (client == null || !client.isServerReachable() || !client.isSessionOpen()) {
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700155 return MastershipRole.NONE;
156 }
157 return client.isMaster() ? MastershipRole.MASTER : MastershipRole.STANDBY;
Andrea Campanella1e573442018-05-17 17:07:13 +0200158 }
159
160 @Override
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700161 public void addDeviceAgentListener(ProviderId providerId, DeviceAgentListener listener) {
162 // Don't use controller/deviceId class variables as they might be uninitialized.
163 handler().get(P4RuntimeController.class)
164 .addDeviceAgentListener(data().deviceId(), providerId, listener);
165 }
166
167 @Override
168 public void removeDeviceAgentListener(ProviderId providerId) {
Carmelo Cascone7044efd2018-07-06 13:01:36 +0200169 // Don't use controller/deviceId class variable as they might be uninitialized.
Carmelo Cascone158b8c42018-07-04 19:42:37 +0200170 handler().get(P4RuntimeController.class)
Carmelo Cascone9e4972c2018-08-30 00:29:16 -0700171 .removeDeviceAgentListener(data().deviceId(), providerId);
Andrea Campanella1e573442018-05-17 17:07:13 +0200172 }
Andrea Campanella241896c2017-05-10 13:11:04 -0700173}