| /* |
| * Copyright 2017-present Open Networking Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.onosproject.drivers.p4runtime; |
| |
| import org.onosproject.cluster.ClusterService; |
| import org.onosproject.grpc.utils.AbstractGrpcHandshaker; |
| import org.onosproject.net.MastershipRole; |
| import org.onosproject.net.device.DeviceAgentListener; |
| import org.onosproject.net.device.DeviceHandshaker; |
| import org.onosproject.net.pi.service.PiPipeconfWatchdogService; |
| import org.onosproject.net.provider.ProviderId; |
| import org.onosproject.p4runtime.api.P4RuntimeClient; |
| import org.onosproject.p4runtime.api.P4RuntimeController; |
| |
| import java.math.BigInteger; |
| import java.util.concurrent.CompletableFuture; |
| |
| import static java.util.concurrent.CompletableFuture.completedFuture; |
| import static org.onosproject.drivers.p4runtime.P4RuntimeDriverUtils.extractP4DeviceId; |
| |
| /** |
| * Implementation of DeviceHandshaker for P4Runtime. |
| */ |
| public class P4RuntimeHandshaker |
| extends AbstractGrpcHandshaker<P4RuntimeClient, P4RuntimeController> |
| implements DeviceHandshaker { |
| |
| // This is needed to compute an election ID based on mastership term and |
| // preference. At the time of writing the practical maximum cluster size is |
| // 9. Since election IDs are 128bit numbers, we should'nt be too worried of |
| // being conservative when setting a static max size here. Making the |
| // cluster size dynamic would likely cause conflicts when generating |
| // election IDs (e.g. two nodes seeing different cluster sizes). |
| private static final int MAX_CLUSTER_SIZE = 20; |
| |
| private Long p4DeviceId; |
| |
| public P4RuntimeHandshaker() { |
| super(P4RuntimeController.class); |
| } |
| |
| @Override |
| protected boolean setupBehaviour(String opName) { |
| if (!super.setupBehaviour(opName)) { |
| return false; |
| } |
| |
| p4DeviceId = extractP4DeviceId(mgmtUriFromNetcfg()); |
| if (p4DeviceId == null) { |
| log.warn("Unable to obtain the P4Runtime-internal device_id from " + |
| "config of {}, cannot perform {}", |
| deviceId, opName); |
| return false; |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean isAvailable() { |
| // To be available, we require a session open (for packet in/out) and a |
| // pipeline config set. |
| if (!setupBehaviour("isAvailable()") || |
| !client.isServerReachable() || |
| !client.isSessionOpen(p4DeviceId)) { |
| return false; |
| } |
| // Since we cannot probe the device, we rely on what's known by the |
| // pipeconf watchdog service. |
| return PiPipeconfWatchdogService.PipelineStatus.READY.equals( |
| handler().get(PiPipeconfWatchdogService.class) |
| .getStatus(data().deviceId())); |
| } |
| |
| @Override |
| public CompletableFuture<Boolean> probeAvailability() { |
| // To be available, we require a session open (for packet in/out) and a |
| // pipeline config set. |
| if (!setupBehaviour("probeAvailability()") || |
| !client.isServerReachable() || |
| !client.isSessionOpen(p4DeviceId)) { |
| return completedFuture(false); |
| } |
| return client.isAnyPipelineConfigSet(p4DeviceId); |
| } |
| |
| @Override |
| public void roleChanged(MastershipRole newRole) { |
| if (!setupBehaviour("roleChanged()")) { |
| return; |
| } |
| if (newRole.equals(MastershipRole.NONE)) { |
| log.info("Notified role NONE, closing session..."); |
| client.closeSession(p4DeviceId); |
| } else { |
| throw new UnsupportedOperationException( |
| "Use preference-based way for setting MASTER or STANDBY roles"); |
| } |
| } |
| |
| @Override |
| public void roleChanged(int preference, long term) { |
| if (!setupBehaviour("roleChanged()")) { |
| return; |
| } |
| final int clusterSize = handler().get(ClusterService.class) |
| .getNodes().size(); |
| if (clusterSize > MAX_CLUSTER_SIZE) { |
| throw new IllegalStateException( |
| "Cluster too big! Maz size supported is " + MAX_CLUSTER_SIZE); |
| } |
| final BigInteger electionId = BigInteger.valueOf(term) |
| .multiply(BigInteger.valueOf(MAX_CLUSTER_SIZE)) |
| .subtract(BigInteger.valueOf(preference)); |
| client.setMastership(p4DeviceId, preference == 0, electionId); |
| } |
| |
| @Override |
| public MastershipRole getRole() { |
| if (!setupBehaviour("getRole()") || |
| !client.isServerReachable() || |
| !client.isSessionOpen(p4DeviceId)) { |
| return MastershipRole.NONE; |
| } |
| return client.isMaster(p4DeviceId) |
| ? MastershipRole.MASTER : MastershipRole.STANDBY; |
| } |
| |
| @Override |
| public void addDeviceAgentListener(ProviderId providerId, DeviceAgentListener listener) { |
| // Don't use controller/deviceId class variables as they might be uninitialized. |
| handler().get(P4RuntimeController.class) |
| .addDeviceAgentListener(data().deviceId(), providerId, listener); |
| } |
| |
| @Override |
| public void removeDeviceAgentListener(ProviderId providerId) { |
| // Don't use controller/deviceId class variable as they might be uninitialized. |
| handler().get(P4RuntimeController.class) |
| .removeDeviceAgentListener(data().deviceId(), providerId); |
| } |
| } |