blob: e689298d5f18fe8df30670a31f45eafe2ad2b0c0 [file] [log] [blame]
/*
* 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);
}
}