diff --git a/core/api/src/main/java/org/onosproject/net/pi/model/PiPipelineProgrammable.java b/core/api/src/main/java/org/onosproject/net/pi/model/PiPipelineProgrammable.java new file mode 100644 index 0000000000..28d4625b7a --- /dev/null +++ b/core/api/src/main/java/org/onosproject/net/pi/model/PiPipelineProgrammable.java @@ -0,0 +1,43 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * 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.net.pi.model; + +import org.onosproject.net.driver.HandlerBehaviour; + +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +/** + * Behavior to program the pipeline of a device. + */ +public interface PiPipelineProgrammable extends HandlerBehaviour { + /** + * Deploys the given pipeconf to the device. + * + * @param pipeconf pipeconf + * @return true if the operation was successful, false otherwise + */ + // TODO: return an explanation of why things went wrong, and the status of the device. + CompletableFuture deployPipeconf(PiPipeconf pipeconf); + + /** + * Returns the default pipeconf for ths device, to be used when any other pipeconf is not available. + * + * @return optional pipeconf + */ + Optional getDefaultPipeconf(); +} diff --git a/drivers/bmv2/BUCK b/drivers/bmv2/BUCK index ad83ec1406..b406b08cec 100644 --- a/drivers/bmv2/BUCK +++ b/drivers/bmv2/BUCK @@ -1,8 +1,32 @@ +GRPC_DEPS = [ + '//incubator/grpc-dependencies:grpc-core-repkg-1.3.0', + '//lib:grpc-protobuf-1.3.0', + '//lib:grpc-protobuf-lite-1.3.0', + '//lib:grpc-stub-1.3.0', + '//lib:grpc-netty-1.3.0', + '//lib:grpc-auth-1.3.0', + '//lib:google-instrumentation-0.3.0', + '//lib:protobuf-java-3.0.2', + # Lazily adding all netty-related packages. + # Some of them might not be necessary. + '//lib:netty', + '//lib:netty-buffer', + '//lib:netty-codec', + '//lib:netty-codec-http', + '//lib:netty-codec-http2', + '//lib:netty-common', + '//lib:netty-handler', + '//lib:netty-transport', + '//lib:netty-transport-native-epoll', + '//lib:netty-resolver', +] + COMPILE_DEPS = [ '//lib:CORE_DEPS', - '//protocols/grpc/api:onos-protocols-grpc-api', - '//lib:grpc-core-1.3.0', - '//lib:grpc-stub-1.3.0' + '//protocols/p4runtime/api:onos-protocols-p4runtime-api', + '//incubator/bmv2/model:onos-incubator-bmv2-model', + '//incubator/grpc-dependencies:grpc-core-repkg-1.3.0', + '//lib:grpc-netty-1.3.0', ] TEST_DEPS = [ @@ -12,11 +36,14 @@ TEST_DEPS = [ BUNDLES = [ ':onos-drivers-bmv2', - '//lib:grpc-core-1.3.0', - '//lib:grpc-stub-1.3.0', + '//protocols/p4runtime/proto:onos-protocols-p4runtime-proto', + '//protocols/p4runtime/api:onos-protocols-p4runtime-api', + '//protocols/p4runtime/ctl:onos-protocols-p4runtime-ctl', '//protocols/grpc/api:onos-protocols-grpc-api', '//protocols/grpc/ctl:onos-protocols-grpc-ctl', -] + '//protocols/grpc/proto:onos-protocols-grpc-proto', + '//incubator/bmv2/model:onos-incubator-bmv2-model', +] + GRPC_DEPS osgi_jar_with_tests( deps = COMPILE_DEPS, diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DefaultInterpreter.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DefaultInterpreter.java new file mode 100644 index 0000000000..59f0c8c605 --- /dev/null +++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DefaultInterpreter.java @@ -0,0 +1,121 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * 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.bmv2; + +import com.google.common.collect.ImmutableBiMap; +import org.onlab.util.ImmutableByteSequence; +import org.onosproject.net.PortNumber; +import org.onosproject.net.driver.AbstractHandlerBehaviour; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.criteria.Criterion; +import org.onosproject.net.flow.instructions.Instruction; +import org.onosproject.net.flow.instructions.Instructions; +import org.onosproject.net.pi.model.PiPipeconf; +import org.onosproject.net.pi.model.PiPipelineInterpreter; +import org.onosproject.net.pi.runtime.PiAction; +import org.onosproject.net.pi.runtime.PiActionId; +import org.onosproject.net.pi.runtime.PiActionParam; +import org.onosproject.net.pi.runtime.PiActionParamId; +import org.onosproject.net.pi.runtime.PiHeaderFieldId; +import org.onosproject.net.pi.runtime.PiTableAction; +import org.onosproject.net.pi.runtime.PiTableId; + +import java.util.Optional; + +import static org.onosproject.net.PortNumber.CONTROLLER; + +/** + * Interpreter implementation for the default pipeconf. + */ +public class Bmv2DefaultInterpreter extends AbstractHandlerBehaviour implements PiPipelineInterpreter { + private static final String TABLE0 = "table0"; + private static final String SEND_TO_CPU = "send_to_cpu_0"; + private static final String PORT = "port"; + private static final String DROP = "_drop_0"; + private static final String SET_EGRESS_PORT = "set_egress_port_0"; + + private static final PiHeaderFieldId IN_PORT_ID = PiHeaderFieldId.of("standard_metadata", "ingress_port"); + private static final PiHeaderFieldId ETH_DST_ID = PiHeaderFieldId.of("ethernet_t", "dstAddr"); + private static final PiHeaderFieldId ETH_SRC_ID = PiHeaderFieldId.of("ethernet_t", "srcAddr"); + private static final PiHeaderFieldId ETH_TYPE_ID = PiHeaderFieldId.of("ethernet_t", "etherType"); + + private static final ImmutableBiMap CRITERION_MAP = ImmutableBiMap.of( + Criterion.Type.IN_PORT, IN_PORT_ID, + Criterion.Type.ETH_DST, ETH_DST_ID, + Criterion.Type.ETH_SRC, ETH_SRC_ID, + Criterion.Type.ETH_TYPE, ETH_TYPE_ID); + + private static final ImmutableBiMap TABLE_MAP = ImmutableBiMap.of( + 0, PiTableId.of(TABLE0)); + + @Override + public PiTableAction mapTreatment(TrafficTreatment treatment, PiPipeconf pipeconf) throws PiInterpreterException { + + if (treatment.allInstructions().size() == 0) { + // No instructions means drop for us. + return actionWithName(DROP); + } else if (treatment.allInstructions().size() > 1) { + // Otherwise, we understand treatments with only 1 instruction. + throw new PiPipelineInterpreter.PiInterpreterException("Treatment has multiple instructions"); + } + + Instruction instruction = treatment.allInstructions().get(0); + + switch (instruction.type()) { + case OUTPUT: + Instructions.OutputInstruction outInstruction = (Instructions.OutputInstruction) instruction; + PortNumber port = outInstruction.port(); + if (!port.isLogical()) { + PiAction.builder() + .withId(PiActionId.of(SET_EGRESS_PORT)) + .withParameter(new PiActionParam(PiActionParamId.of(PORT), + ImmutableByteSequence.copyFrom(port.toLong()))) + .build(); + } else if (port.equals(CONTROLLER)) { + return actionWithName(SEND_TO_CPU); + } else { + throw new PiInterpreterException("Egress on logical port not supported: " + port); + } + case NOACTION: + return actionWithName(DROP); + default: + throw new PiInterpreterException("Instruction type not supported: " + instruction.type().name()); + } + } + + /** + * Returns an action instance with no runtime parameters. + */ + private PiAction actionWithName(String name) { + return PiAction.builder().withId(PiActionId.of(name)).build(); + } + + @Override + public Optional mapCriterionType(Criterion.Type type) { + return Optional.ofNullable(CRITERION_MAP.get(type)); + } + + @Override + public Optional mapPiHeaderFieldId(PiHeaderFieldId headerFieldId) { + return Optional.ofNullable(CRITERION_MAP.inverse().get(headerFieldId)); + } + + @Override + public Optional mapFlowRuleTableId(int flowRuleTableId) { + return Optional.ofNullable(TABLE_MAP.get(flowRuleTableId)); + } +} diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DefaultPipeconf.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DefaultPipeconf.java new file mode 100644 index 0000000000..ac2dbd7052 --- /dev/null +++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2DefaultPipeconf.java @@ -0,0 +1,103 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * 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.bmv2; + +import com.eclipsesource.json.Json; +import com.google.common.collect.ImmutableMap; +import org.onosproject.bmv2.model.Bmv2PipelineModelParser; +import org.onosproject.net.driver.Behaviour; +import org.onosproject.net.pi.model.PiPipeconf; +import org.onosproject.net.pi.model.PiPipeconfId; +import org.onosproject.net.pi.model.PiPipelineInterpreter; +import org.onosproject.net.pi.model.PiPipelineModel; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Collection; +import java.util.Map; +import java.util.Optional; + +/** + * Implementation of the default pipeconf for a BMv2 device. + */ +public final class Bmv2DefaultPipeconf implements PiPipeconf { + + private static final String PIPECONF_ID = "bmv2-default-pipeconf"; + private static final String JSON_PATH = "/default.json"; + private static final String P4INFO_PATH = "/default.p4info"; + + private final PiPipeconfId id; + private final PiPipelineModel pipelineModel; + private final InputStream jsonConfigStream = this.getClass().getResourceAsStream(JSON_PATH); + private final InputStream p4InfoStream = this.getClass().getResourceAsStream(P4INFO_PATH); + private final Map, Class> behaviours; + + Bmv2DefaultPipeconf() { + this.id = new PiPipeconfId(PIPECONF_ID); + try { + this.pipelineModel = Bmv2PipelineModelParser.parse( + Json.parse(new BufferedReader(new InputStreamReader(jsonConfigStream))).asObject()); + } catch (IOException e) { + throw new RuntimeException(e); + } + + this.behaviours = ImmutableMap.of( + PiPipelineInterpreter.class, Bmv2DefaultInterpreter.class + // TODO: reuse default single table pipeliner. + ); + } + + @Override + public PiPipeconfId id() { + return this.id; + } + + @Override + public PiPipelineModel pipelineModel() { + return pipelineModel; + } + + @Override + public Collection> behaviours() { + return behaviours.keySet(); + } + + @Override + public Optional> implementation(Class behaviour) { + return Optional.ofNullable(behaviours.get(behaviour)); + } + + @Override + public boolean hasBehaviour(Class behaviourClass) { + return behaviours.containsKey(behaviourClass); + } + + @Override + public Optional extension(ExtensionType type) { + + switch (type) { + case BMV2_JSON: + return Optional.of(jsonConfigStream); + case P4_INFO_TEXT: + return Optional.of(p4InfoStream); + default: + return Optional.empty(); + } + } +} diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2Handshaker.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2Handshaker.java index 8774c0991f..5d74947e79 100644 --- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2Handshaker.java +++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2Handshaker.java @@ -16,17 +16,14 @@ package org.onosproject.drivers.bmv2; -import org.onosproject.grpc.api.GrpcChannelId; -import org.onosproject.grpc.api.GrpcController; -import org.onosproject.grpc.api.GrpcServiceId; -import org.onosproject.grpc.api.GrpcStreamObserverId; +import io.grpc.ManagedChannelBuilder; +import io.grpc.netty.NettyChannelBuilder; import org.onosproject.net.DeviceId; import org.onosproject.net.MastershipRole; import org.onosproject.net.device.DeviceHandshaker; import org.onosproject.net.driver.AbstractHandlerBehaviour; import org.onosproject.net.driver.DriverData; -import org.onosproject.net.key.DeviceKeyId; -import org.onosproject.net.key.DeviceKeyService; +import org.onosproject.p4runtime.api.P4RuntimeController; import org.slf4j.Logger; import java.util.concurrent.CompletableFuture; @@ -34,63 +31,71 @@ import java.util.concurrent.CompletableFuture; import static org.slf4j.LoggerFactory.getLogger; /** - * Implementation of the DeviceHandshaker for the BMv2 softswitch. + * Implementation of DeviceHandshaker for BMv2. */ -//TODO consider abstract class with empty connect method and -//the implementation into a protected one for reusability. -//FIXME fill method bodies, used for testing. public class Bmv2Handshaker extends AbstractHandlerBehaviour implements DeviceHandshaker { private final Logger log = getLogger(getClass()); + // TODO: consider abstract class with empty connect method and implementation into a protected one for reusability. + @Override public CompletableFuture connect() { - GrpcController controller = handler().get(GrpcController.class); + return CompletableFuture.supplyAsync(this::doConnect); + } + + private boolean doConnect() { + + P4RuntimeController controller = handler().get(P4RuntimeController.class); + DeviceId deviceId = handler().data().deviceId(); - - CompletableFuture result = new CompletableFuture<>(); - DeviceKeyService deviceKeyService = handler().get(DeviceKeyService.class); + // DeviceKeyService deviceKeyService = handler().get(DeviceKeyService.class); DriverData data = data(); - //Retrieving information from the driver data. - log.info("protocol bmv2, ip {}, port {}, key {}", data.value("p4runtime_ip"), - data.value("p4runtime_port"), - deviceKeyService.getDeviceKey(DeviceKeyId.deviceKeyId(data.value("p4runtime_key"))) - .asUsernamePassword().username()); - log.info("protocol gnmi, ip {}, port {}, key {}", data.value("gnmi_ip"), data.value("gnmi_port"), - deviceKeyService.getDeviceKey(DeviceKeyId.deviceKeyId(data.value("gnmi_key"))) - .asUsernamePassword().username()); - result.complete(true); + String serverAddr = data.value("p4runtime_ip"); + int serverPort = Integer.valueOf(data.value("p4runtime_port")); + int p4DeviceId = Integer.valueOf(data.value("p4runtime_deviceId")); - //we know we need packet in so we register the observer. - GrpcChannelId channelId = GrpcChannelId.of(deviceId, "bmv2"); - GrpcServiceId serviceId = GrpcServiceId.of(channelId, "p4runtime"); - GrpcStreamObserverId observerId = GrpcStreamObserverId.of(serviceId, - Bmv2PacketProgrammable.class.getSimpleName()); - controller.addObserver(observerId, new Bmv2PacketInObserverHandler()); - return result; + ManagedChannelBuilder channelBuilder = NettyChannelBuilder + .forAddress(serverAddr, serverPort) + .usePlaintext(true); + + if (!controller.createClient(deviceId, p4DeviceId, channelBuilder)) { + log.warn("Unable to create P4runtime client for {}", deviceId); + return false; + } + + // TODO: gNMI handling + + return true; } @Override public CompletableFuture disconnect() { - CompletableFuture result = new CompletableFuture<>(); - result.complete(true); - return result; + return CompletableFuture.supplyAsync(() -> { + P4RuntimeController controller = handler().get(P4RuntimeController.class); + DeviceId deviceId = handler().data().deviceId(); + controller.removeClient(deviceId); + return true; + }); } @Override public CompletableFuture isReachable() { - CompletableFuture result = new CompletableFuture<>(); - result.complete(true); - return result; + return CompletableFuture.supplyAsync(() -> { + P4RuntimeController controller = handler().get(P4RuntimeController.class); + DeviceId deviceId = handler().data().deviceId(); + return controller.isReacheable(deviceId); + }); } @Override public CompletableFuture roleChanged(MastershipRole newRole) { CompletableFuture result = new CompletableFuture<>(); + log.warn("roleChanged not implemented"); result.complete(MastershipRole.MASTER); + // TODO. return result; } - } diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PacketInObserverHandler.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PacketInObserverHandler.java deleted file mode 100644 index 3b9e1451f1..0000000000 --- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PacketInObserverHandler.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2017-present Open Networking Laboratory - * - * 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.bmv2; - -import io.grpc.ManagedChannel; -import io.grpc.stub.StreamObserver; -import org.onosproject.grpc.api.GrpcObserverHandler; -import org.slf4j.Logger; - -import java.util.Optional; - -import static org.slf4j.LoggerFactory.getLogger; - -/** - * Sample Implementation of a PacketInObserverHandler. - * TODO refactor and actually use. - */ -public class Bmv2PacketInObserverHandler implements GrpcObserverHandler { - - private final Logger log = getLogger(getClass()); - - //private final AbstractStub asyncStub; - - //FIXME put at object due to p4Runtime compilation problems to be fixed. - private StreamObserver requestStreamObserver; - - @Override - public void bindObserver(ManagedChannel channel) { - - //asyncStub = ProtoGeneratedClass.newStub(channel); - - //reqeustStreamObserver = asyncStub.MethodName(new PacketInObserver()); - - } - - @Override - public Optional requestStreamObserver() { - return Optional.of(requestStreamObserver); - } - - @Override - public void removeObserver() { - //this should complete the two way streaming - requestStreamObserver.onCompleted(); - } - - private class PacketInObserver implements StreamObserver { - - @Override - public void onNext(Object o) { - log.info("onNext: {}", o.toString()); - - } - - @Override - public void onError(Throwable throwable) { - - } - - @Override - public void onCompleted() { - - } - } -} diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PacketProgrammable.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PacketProgrammable.java index 4b7606709b..7bf225871c 100644 --- a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PacketProgrammable.java +++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PacketProgrammable.java @@ -16,40 +16,32 @@ package org.onosproject.drivers.bmv2; -import io.grpc.stub.StreamObserver; -import org.onosproject.grpc.api.GrpcChannelId; -import org.onosproject.grpc.api.GrpcController; -import org.onosproject.grpc.api.GrpcObserverHandler; -import org.onosproject.grpc.api.GrpcServiceId; -import org.onosproject.grpc.api.GrpcStreamObserverId; -import org.onosproject.net.DeviceId; import org.onosproject.net.driver.AbstractHandlerBehaviour; -import org.onosproject.net.driver.DriverHandler; import org.onosproject.net.packet.OutboundPacket; import org.onosproject.net.packet.PacketProgrammable; -import java.util.Optional; - /** * Packet Programmable behaviour for BMv2 devices. */ public class Bmv2PacketProgrammable extends AbstractHandlerBehaviour implements PacketProgrammable { + @Override public void emit(OutboundPacket packet) { - DriverHandler handler = handler(); - GrpcController controller = handler.get(GrpcController.class); - DeviceId deviceId = handler.data().deviceId(); - GrpcChannelId channelId = GrpcChannelId.of(deviceId, "bmv2"); - GrpcServiceId serviceId = GrpcServiceId.of(channelId, "p4runtime"); - GrpcStreamObserverId observerId = GrpcStreamObserverId.of(serviceId, - this.getClass().getSimpleName()); - Optional manager = controller.getObserverManager(observerId); - if (!manager.isPresent()) { - //this is the first time the behaviour is called - controller.addObserver(observerId, new Bmv2PacketInObserverHandler()); - } - //other already registered the observer for us. - Optional observer = manager.get().requestStreamObserver(); - observer.ifPresent(objectStreamObserver -> objectStreamObserver.onNext(packet)); + // TODO: implement using P4runtime client. + // DriverHandler handler = handler(); + // GrpcController controller = handler.get(GrpcController.class); + // DeviceId deviceId = handler.data().deviceId(); + // GrpcChannelId channelId = GrpcChannelId.of(deviceId, "bmv2"); + // GrpcServiceId serviceId = GrpcServiceId.of(channelId, "p4runtime"); + // GrpcStreamObserverId observerId = GrpcStreamObserverId.of(serviceId, + // this.getClass().getSimpleName()); + // Optional manager = controller.getObserverManager(observerId); + // if (!manager.isPresent()) { + // //this is the first time the behaviour is called + // controller.addObserver(observerId, new Bmv2PacketInObserverHandler()); + // } + // //other already registered the observer for us. + // Optional observer = manager.get().requestStreamObserver(); + // observer.ifPresent(objectStreamObserver -> objectStreamObserver.onNext(packet)); } } diff --git a/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PipelineProgrammable.java b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PipelineProgrammable.java new file mode 100644 index 0000000000..735c142625 --- /dev/null +++ b/drivers/bmv2/src/main/java/org/onosproject/drivers/bmv2/Bmv2PipelineProgrammable.java @@ -0,0 +1,107 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * 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.bmv2; + +import org.onlab.util.SharedExecutors; +import org.onosproject.net.DeviceId; +import org.onosproject.net.driver.AbstractHandlerBehaviour; +import org.onosproject.net.pi.model.PiPipeconf; +import org.onosproject.net.pi.model.PiPipelineProgrammable; +import org.onosproject.p4runtime.api.P4RuntimeClient; +import org.onosproject.p4runtime.api.P4RuntimeController; +import org.slf4j.Logger; + +import java.io.InputStream; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.BMV2_JSON; +import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType.P4_INFO_TEXT; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Implementation of the PiPipelineProgrammable for BMv2. + */ +public class Bmv2PipelineProgrammable extends AbstractHandlerBehaviour implements PiPipelineProgrammable { + + private static final PiPipeconf DEFAULT_PIPECONF = new Bmv2DefaultPipeconf(); + + private final Logger log = getLogger(getClass()); + + @Override + public CompletableFuture deployPipeconf(PiPipeconf pipeconf) { + + CompletableFuture result = new CompletableFuture<>(); + + SharedExecutors.getPoolThreadExecutor().submit(() -> result.complete(doDeployConfig(pipeconf))); + + return result; + } + + private boolean doDeployConfig(PiPipeconf pipeconf) { + + P4RuntimeController controller = handler().get(P4RuntimeController.class); + + DeviceId deviceId = handler().data().deviceId(); + + if (!controller.hasClient(deviceId)) { + log.warn("Unable to find client for {}, aborting pipeconf deploy", deviceId); + return false; + + } + + P4RuntimeClient client = controller.getClient(deviceId); + + if (!pipeconf.extension(BMV2_JSON).isPresent()) { + log.warn("Missing BMv2 JSON config in pipeconf {}, aborting pipeconf deploy", pipeconf.id()); + return false; + } + + if (!pipeconf.extension(P4_INFO_TEXT).isPresent()) { + log.warn("Missing P4Info in pipeconf {}, aborting pipeconf deploy", pipeconf.id()); + return false; + } + + InputStream p4InfoStream = pipeconf.extension(P4_INFO_TEXT).get(); + InputStream jsonStream = pipeconf.extension(BMV2_JSON).get(); + + try { + if (!client.setPipelineConfig(p4InfoStream, jsonStream).get()) { + log.warn("Unable to deploy pipeconf {} to {}", pipeconf.id(), deviceId); + return false; + } + + // It would be more logical to have this performed at device handshake, but P4runtime would reject any + // command if a P4info has not been set first. + if (!client.initStreamChannel().get()) { + log.warn("Unable to init stream channel to {}.", deviceId); + return false; + } + + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + + return true; + } + + @Override + public Optional getDefaultPipeconf() { + return Optional.of(DEFAULT_PIPECONF); + } +} diff --git a/drivers/bmv2/src/main/resources/bmv2-drivers.xml b/drivers/bmv2/src/main/resources/bmv2-drivers.xml index bc343e02a5..0120974afb 100644 --- a/drivers/bmv2/src/main/resources/bmv2-drivers.xml +++ b/drivers/bmv2/src/main/resources/bmv2-drivers.xml @@ -20,6 +20,8 @@ impl="org.onosproject.drivers.bmv2.Bmv2Handshaker"/> + diff --git a/drivers/bmv2/src/main/resources/default.json b/drivers/bmv2/src/main/resources/default.json new file mode 100644 index 0000000000..3a46dcc5c7 --- /dev/null +++ b/drivers/bmv2/src/main/resources/default.json @@ -0,0 +1,2697 @@ +{ + "program" : "default.p4", + "__meta__" : { + "version" : [2, 7], + "compiler" : "https://github.com/p4lang/p4c" + }, + "header_types" : [ + { + "name" : "scalars_0", + "id" : 0, + "fields" : [ + ["tmp", 32, false], + ["tmp_0", 32, false] + ] + }, + { + "name" : "standard_metadata", + "id" : 1, + "fields" : [ + ["ingress_port", 9, false], + ["egress_spec", 9, false], + ["egress_port", 9, false], + ["clone_spec", 32, false], + ["instance_type", 32, false], + ["drop", 1, false], + ["recirculate_port", 16, false], + ["packet_length", 32, false], + ["enq_timestamp", 32, false], + ["enq_qdepth", 19, false], + ["deq_timedelta", 32, false], + ["deq_qdepth", 19, false], + ["ingress_global_timestamp", 48, false], + ["lf_field_list", 32, false], + ["mcast_grp", 16, false], + ["resubmit_flag", 1, false], + ["egress_rid", 16, false], + ["_padding", 5, false] + ] + }, + { + "name" : "ethernet_t", + "id" : 2, + "fields" : [ + ["dstAddr", 48, false], + ["srcAddr", 48, false], + ["etherType", 16, false] + ] + }, + { + "name" : "ipv4_t", + "id" : 3, + "fields" : [ + ["version", 4, false], + ["ihl", 4, false], + ["diffserv", 8, false], + ["totalLen", 16, false], + ["identification", 16, false], + ["flags", 3, false], + ["fragOffset", 13, false], + ["ttl", 8, false], + ["protocol", 8, false], + ["hdrChecksum", 16, false], + ["srcAddr", 32, false], + ["dstAddr", 32, false] + ] + }, + { + "name" : "tcp_t", + "id" : 4, + "fields" : [ + ["srcPort", 16, false], + ["dstPort", 16, false], + ["seqNo", 32, false], + ["ackNo", 32, false], + ["dataOffset", 4, false], + ["res", 3, false], + ["ecn", 3, false], + ["ctrl", 6, false], + ["window", 16, false], + ["checksum", 16, false], + ["urgentPtr", 16, false] + ] + }, + { + "name" : "udp_t", + "id" : 5, + "fields" : [ + ["srcPort", 16, false], + ["dstPort", 16, false], + ["length_", 16, false], + ["checksum", 16, false] + ] + }, + { + "name" : "ecmp_metadata_t", + "id" : 6, + "fields" : [ + ["groupId", 16, false], + ["selector", 16, false] + ] + }, + { + "name" : "wcmp_meta_t", + "id" : 7, + "fields" : [ + ["groupId", 16, false], + ["numBits", 8, false], + ["selector", 64, false] + ] + }, + { + "name" : "intrinsic_metadata_t", + "id" : 8, + "fields" : [ + ["ingress_global_timestamp", 32, false], + ["lf_field_list", 32, false], + ["mcast_grp", 16, false], + ["egress_rid", 16, false] + ] + } + ], + "headers" : [ + { + "name" : "standard_metadata_3", + "id" : 0, + "header_type" : "standard_metadata", + "metadata" : true, + "pi_omit" : true + }, + { + "name" : "standard_metadata_4", + "id" : 1, + "header_type" : "standard_metadata", + "metadata" : true, + "pi_omit" : true + }, + { + "name" : "standard_metadata_5", + "id" : 2, + "header_type" : "standard_metadata", + "metadata" : true, + "pi_omit" : true + }, + { + "name" : "scalars", + "id" : 3, + "header_type" : "scalars_0", + "metadata" : true, + "pi_omit" : true + }, + { + "name" : "standard_metadata", + "id" : 4, + "header_type" : "standard_metadata", + "metadata" : true, + "pi_omit" : true + }, + { + "name" : "ethernet", + "id" : 5, + "header_type" : "ethernet_t", + "metadata" : false, + "pi_omit" : true + }, + { + "name" : "ipv4", + "id" : 6, + "header_type" : "ipv4_t", + "metadata" : false, + "pi_omit" : true + }, + { + "name" : "tcp", + "id" : 7, + "header_type" : "tcp_t", + "metadata" : false, + "pi_omit" : true + }, + { + "name" : "udp", + "id" : 8, + "header_type" : "udp_t", + "metadata" : false, + "pi_omit" : true + }, + { + "name" : "ecmp_metadata", + "id" : 9, + "header_type" : "ecmp_metadata_t", + "metadata" : true, + "pi_omit" : true + }, + { + "name" : "wcmp_meta", + "id" : 10, + "header_type" : "wcmp_meta_t", + "metadata" : true, + "pi_omit" : true + }, + { + "name" : "intrinsic_metadata", + "id" : 11, + "header_type" : "intrinsic_metadata_t", + "metadata" : true, + "pi_omit" : true + } + ], + "header_stacks" : [], + "header_union_types" : [], + "header_unions" : [], + "header_union_stacks" : [], + "field_lists" : [], + "errors" : [ + ["NoError", 1], + ["PacketTooShort", 2], + ["NoMatch", 3], + ["StackOutOfBounds", 4], + ["HeaderTooShort", 5], + ["ParserTimeout", 6] + ], + "enums" : [], + "parsers" : [ + { + "name" : "parser", + "id" : 0, + "init_state" : "start", + "parse_states" : [ + { + "name" : "parse_ipv4", + "id" : 0, + "parser_ops" : [ + { + "parameters" : [ + { + "type" : "regular", + "value" : "ipv4" + } + ], + "op" : "extract" + } + ], + "transitions" : [ + { + "value" : "0x06", + "mask" : null, + "next_state" : "parse_tcp" + }, + { + "value" : "0x11", + "mask" : null, + "next_state" : "parse_udp" + }, + { + "value" : "default", + "mask" : null, + "next_state" : null + } + ], + "transition_key" : [ + { + "type" : "field", + "value" : ["ipv4", "protocol"] + } + ] + }, + { + "name" : "parse_tcp", + "id" : 1, + "parser_ops" : [ + { + "parameters" : [ + { + "type" : "regular", + "value" : "tcp" + } + ], + "op" : "extract" + } + ], + "transitions" : [ + { + "value" : "default", + "mask" : null, + "next_state" : null + } + ], + "transition_key" : [] + }, + { + "name" : "parse_udp", + "id" : 2, + "parser_ops" : [ + { + "parameters" : [ + { + "type" : "regular", + "value" : "udp" + } + ], + "op" : "extract" + } + ], + "transitions" : [ + { + "value" : "default", + "mask" : null, + "next_state" : null + } + ], + "transition_key" : [] + }, + { + "name" : "start", + "id" : 3, + "parser_ops" : [ + { + "parameters" : [ + { + "type" : "regular", + "value" : "ethernet" + } + ], + "op" : "extract" + } + ], + "transitions" : [ + { + "value" : "0x0800", + "mask" : null, + "next_state" : "parse_ipv4" + }, + { + "value" : "default", + "mask" : null, + "next_state" : null + } + ], + "transition_key" : [ + { + "type" : "field", + "value" : ["ethernet", "etherType"] + } + ] + } + ] + } + ], + "deparsers" : [ + { + "name" : "deparser", + "id" : 0, + "source_info" : { + "filename" : "include/parsers.p4", + "line" : 43, + "column" : 8, + "source_fragment" : "DeparserImpl" + }, + "order" : ["ethernet", "ipv4", "udp", "tcp"] + } + ], + "meter_arrays" : [], + "counter_arrays" : [ + { + "name" : "table0_counter", + "id" : 0, + "is_direct" : true, + "binding" : "table0" + }, + { + "name" : "port_counters_control.egress_port_counter", + "id" : 1, + "source_info" : { + "filename" : "include/port_counters.p4", + "line" : 6, + "column" : 38, + "source_fragment" : "egress_port_counter" + }, + "size" : 254, + "is_direct" : false + }, + { + "name" : "port_counters_control.ingress_port_counter", + "id" : 2, + "source_info" : { + "filename" : "include/port_counters.p4", + "line" : 7, + "column" : 38, + "source_fragment" : "ingress_port_counter" + }, + "size" : 254, + "is_direct" : false + } + ], + "register_arrays" : [], + "calculations" : [], + "learn_lists" : [], + "actions" : [ + { + "name" : "set_egress_port", + "id" : 0, + "runtime_data" : [ + { + "name" : "port", + "bitwidth" : 9 + } + ], + "primitives" : [ + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_3", "ingress_port"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "ingress_port"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_3", "egress_spec"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "egress_spec"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_3", "egress_port"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "egress_port"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_3", "clone_spec"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "clone_spec"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_3", "instance_type"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "instance_type"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_3", "drop"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "drop"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_3", "recirculate_port"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "recirculate_port"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_3", "packet_length"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "packet_length"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_3", "enq_timestamp"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "enq_timestamp"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_3", "enq_qdepth"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "enq_qdepth"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_3", "deq_timedelta"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "deq_timedelta"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_3", "deq_qdepth"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "deq_qdepth"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_3", "ingress_global_timestamp"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "ingress_global_timestamp"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_3", "lf_field_list"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "lf_field_list"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_3", "mcast_grp"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "mcast_grp"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_3", "resubmit_flag"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "resubmit_flag"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_3", "egress_rid"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "egress_rid"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_3", "egress_spec"] + }, + { + "type" : "runtime_data", + "value" : 0 + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 9, + "column" : 4, + "source_fragment" : "standard_metadata.egress_spec = port" + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "ingress_port"] + }, + { + "type" : "field", + "value" : ["standard_metadata_3", "ingress_port"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "egress_spec"] + }, + { + "type" : "field", + "value" : ["standard_metadata_3", "egress_spec"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "egress_port"] + }, + { + "type" : "field", + "value" : ["standard_metadata_3", "egress_port"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "clone_spec"] + }, + { + "type" : "field", + "value" : ["standard_metadata_3", "clone_spec"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "instance_type"] + }, + { + "type" : "field", + "value" : ["standard_metadata_3", "instance_type"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "drop"] + }, + { + "type" : "field", + "value" : ["standard_metadata_3", "drop"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "recirculate_port"] + }, + { + "type" : "field", + "value" : ["standard_metadata_3", "recirculate_port"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "packet_length"] + }, + { + "type" : "field", + "value" : ["standard_metadata_3", "packet_length"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "enq_timestamp"] + }, + { + "type" : "field", + "value" : ["standard_metadata_3", "enq_timestamp"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "enq_qdepth"] + }, + { + "type" : "field", + "value" : ["standard_metadata_3", "enq_qdepth"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "deq_timedelta"] + }, + { + "type" : "field", + "value" : ["standard_metadata_3", "deq_timedelta"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "deq_qdepth"] + }, + { + "type" : "field", + "value" : ["standard_metadata_3", "deq_qdepth"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "ingress_global_timestamp"] + }, + { + "type" : "field", + "value" : ["standard_metadata_3", "ingress_global_timestamp"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "lf_field_list"] + }, + { + "type" : "field", + "value" : ["standard_metadata_3", "lf_field_list"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "mcast_grp"] + }, + { + "type" : "field", + "value" : ["standard_metadata_3", "mcast_grp"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "resubmit_flag"] + }, + { + "type" : "field", + "value" : ["standard_metadata_3", "resubmit_flag"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "egress_rid"] + }, + { + "type" : "field", + "value" : ["standard_metadata_3", "egress_rid"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 8, + "column" : 49, + "source_fragment" : "standard_metadata, Port port) { ..." + } + } + ] + }, + { + "name" : "send_to_cpu", + "id" : 1, + "runtime_data" : [], + "primitives" : [ + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_4", "ingress_port"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "ingress_port"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_4", "egress_spec"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "egress_spec"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_4", "egress_port"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "egress_port"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_4", "clone_spec"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "clone_spec"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_4", "instance_type"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "instance_type"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_4", "drop"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "drop"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_4", "recirculate_port"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "recirculate_port"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_4", "packet_length"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "packet_length"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_4", "enq_timestamp"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "enq_timestamp"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_4", "enq_qdepth"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "enq_qdepth"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_4", "deq_timedelta"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "deq_timedelta"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_4", "deq_qdepth"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "deq_qdepth"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_4", "ingress_global_timestamp"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "ingress_global_timestamp"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_4", "lf_field_list"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "lf_field_list"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_4", "mcast_grp"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "mcast_grp"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_4", "resubmit_flag"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "resubmit_flag"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_4", "egress_rid"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "egress_rid"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_4", "egress_spec"] + }, + { + "type" : "hexstr", + "value" : "0x00ff" + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 5, + "column" : 4, + "source_fragment" : "standard_metadata.egress_spec = 9w255" + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "ingress_port"] + }, + { + "type" : "field", + "value" : ["standard_metadata_4", "ingress_port"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "egress_spec"] + }, + { + "type" : "field", + "value" : ["standard_metadata_4", "egress_spec"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "egress_port"] + }, + { + "type" : "field", + "value" : ["standard_metadata_4", "egress_port"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "clone_spec"] + }, + { + "type" : "field", + "value" : ["standard_metadata_4", "clone_spec"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "instance_type"] + }, + { + "type" : "field", + "value" : ["standard_metadata_4", "instance_type"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "drop"] + }, + { + "type" : "field", + "value" : ["standard_metadata_4", "drop"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "recirculate_port"] + }, + { + "type" : "field", + "value" : ["standard_metadata_4", "recirculate_port"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "packet_length"] + }, + { + "type" : "field", + "value" : ["standard_metadata_4", "packet_length"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "enq_timestamp"] + }, + { + "type" : "field", + "value" : ["standard_metadata_4", "enq_timestamp"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "enq_qdepth"] + }, + { + "type" : "field", + "value" : ["standard_metadata_4", "enq_qdepth"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "deq_timedelta"] + }, + { + "type" : "field", + "value" : ["standard_metadata_4", "deq_timedelta"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "deq_qdepth"] + }, + { + "type" : "field", + "value" : ["standard_metadata_4", "deq_qdepth"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "ingress_global_timestamp"] + }, + { + "type" : "field", + "value" : ["standard_metadata_4", "ingress_global_timestamp"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "lf_field_list"] + }, + { + "type" : "field", + "value" : ["standard_metadata_4", "lf_field_list"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "mcast_grp"] + }, + { + "type" : "field", + "value" : ["standard_metadata_4", "mcast_grp"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "resubmit_flag"] + }, + { + "type" : "field", + "value" : ["standard_metadata_4", "resubmit_flag"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "egress_rid"] + }, + { + "type" : "field", + "value" : ["standard_metadata_4", "egress_rid"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 4, + "column" : 45, + "source_fragment" : "standard_metadata) { ..." + } + } + ] + }, + { + "name" : "drop", + "id" : 2, + "runtime_data" : [], + "primitives" : [ + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_5", "ingress_port"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "ingress_port"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_5", "egress_spec"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "egress_spec"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_5", "egress_port"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "egress_port"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_5", "clone_spec"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "clone_spec"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_5", "instance_type"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "instance_type"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_5", "drop"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "drop"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_5", "recirculate_port"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "recirculate_port"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_5", "packet_length"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "packet_length"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_5", "enq_timestamp"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "enq_timestamp"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_5", "enq_qdepth"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "enq_qdepth"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_5", "deq_timedelta"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "deq_timedelta"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_5", "deq_qdepth"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "deq_qdepth"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_5", "ingress_global_timestamp"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "ingress_global_timestamp"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_5", "lf_field_list"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "lf_field_list"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_5", "mcast_grp"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "mcast_grp"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_5", "resubmit_flag"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "resubmit_flag"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_5", "egress_rid"] + }, + { + "type" : "field", + "value" : ["standard_metadata", "egress_rid"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata_5", "egress_spec"] + }, + { + "type" : "hexstr", + "value" : "0x01ff" + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 13, + "column" : 4, + "source_fragment" : "standard_metadata.egress_spec = 9w511" + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "ingress_port"] + }, + { + "type" : "field", + "value" : ["standard_metadata_5", "ingress_port"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "egress_spec"] + }, + { + "type" : "field", + "value" : ["standard_metadata_5", "egress_spec"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "egress_port"] + }, + { + "type" : "field", + "value" : ["standard_metadata_5", "egress_port"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "clone_spec"] + }, + { + "type" : "field", + "value" : ["standard_metadata_5", "clone_spec"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "instance_type"] + }, + { + "type" : "field", + "value" : ["standard_metadata_5", "instance_type"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "drop"] + }, + { + "type" : "field", + "value" : ["standard_metadata_5", "drop"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "recirculate_port"] + }, + { + "type" : "field", + "value" : ["standard_metadata_5", "recirculate_port"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "packet_length"] + }, + { + "type" : "field", + "value" : ["standard_metadata_5", "packet_length"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "enq_timestamp"] + }, + { + "type" : "field", + "value" : ["standard_metadata_5", "enq_timestamp"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "enq_qdepth"] + }, + { + "type" : "field", + "value" : ["standard_metadata_5", "enq_qdepth"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "deq_timedelta"] + }, + { + "type" : "field", + "value" : ["standard_metadata_5", "deq_timedelta"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "deq_qdepth"] + }, + { + "type" : "field", + "value" : ["standard_metadata_5", "deq_qdepth"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "ingress_global_timestamp"] + }, + { + "type" : "field", + "value" : ["standard_metadata_5", "ingress_global_timestamp"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "lf_field_list"] + }, + { + "type" : "field", + "value" : ["standard_metadata_5", "lf_field_list"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "mcast_grp"] + }, + { + "type" : "field", + "value" : ["standard_metadata_5", "mcast_grp"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "resubmit_flag"] + }, + { + "type" : "field", + "value" : ["standard_metadata_5", "resubmit_flag"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["standard_metadata", "egress_rid"] + }, + { + "type" : "field", + "value" : ["standard_metadata_5", "egress_rid"] + } + ], + "source_info" : { + "filename" : "include/actions.p4", + "line" : 12, + "column" : 38, + "source_fragment" : "standard_metadata) { ..." + } + } + ] + }, + { + "name" : "NoAction", + "id" : 3, + "runtime_data" : [], + "primitives" : [] + }, + { + "name" : "act", + "id" : 4, + "runtime_data" : [], + "primitives" : [ + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["scalars", "tmp"] + }, + { + "type" : "expression", + "value" : { + "type" : "expression", + "value" : { + "op" : "&", + "left" : { + "type" : "field", + "value" : ["standard_metadata", "ingress_port"] + }, + "right" : { + "type" : "hexstr", + "value" : "0xffffffff" + } + } + } + } + ] + }, + { + "op" : "count", + "parameters" : [ + { + "type" : "counter_array", + "value" : "port_counters_control.ingress_port_counter" + }, + { + "type" : "field", + "value" : ["scalars", "tmp"] + } + ], + "source_info" : { + "filename" : "include/port_counters.p4", + "line" : 11, + "column" : 12, + "source_fragment" : "ingress_port_counter.count((bit<32>)standard_metadata.ingress_port)" + } + }, + { + "op" : "assign", + "parameters" : [ + { + "type" : "field", + "value" : ["scalars", "tmp_0"] + }, + { + "type" : "expression", + "value" : { + "type" : "expression", + "value" : { + "op" : "&", + "left" : { + "type" : "field", + "value" : ["standard_metadata", "egress_spec"] + }, + "right" : { + "type" : "hexstr", + "value" : "0xffffffff" + } + } + } + } + ] + }, + { + "op" : "count", + "parameters" : [ + { + "type" : "counter_array", + "value" : "port_counters_control.egress_port_counter" + }, + { + "type" : "field", + "value" : ["scalars", "tmp_0"] + } + ], + "source_info" : { + "filename" : "include/port_counters.p4", + "line" : 12, + "column" : 12, + "source_fragment" : "egress_port_counter.count((bit<32>)standard_metadata.egress_spec)" + } + } + ] + } + ], + "pipelines" : [ + { + "name" : "ingress", + "id" : 0, + "source_info" : { + "filename" : "default.p4", + "line" : 10, + "column" : 8, + "source_fragment" : "ingress" + }, + "init_table" : "table0", + "tables" : [ + { + "name" : "table0", + "id" : 0, + "source_info" : { + "filename" : "default.p4", + "line" : 13, + "column" : 10, + "source_fragment" : "table0" + }, + "key" : [ + { + "match_type" : "ternary", + "target" : ["standard_metadata", "ingress_port"], + "mask" : null + }, + { + "match_type" : "ternary", + "target" : ["ethernet", "dstAddr"], + "mask" : null + }, + { + "match_type" : "ternary", + "target" : ["ethernet", "srcAddr"], + "mask" : null + }, + { + "match_type" : "ternary", + "target" : ["ethernet", "etherType"], + "mask" : null + } + ], + "match_type" : "ternary", + "type" : "simple", + "max_size" : 1024, + "support_timeout" : false, + "direct_meters" : null, + "action_ids" : [0, 1, 2, 3], + "actions" : ["set_egress_port", "send_to_cpu", "drop", "NoAction"], + "base_default_next" : "node_3", + "next_tables" : { + "set_egress_port" : "node_3", + "send_to_cpu" : "node_3", + "drop" : "node_3", + "NoAction" : "node_3" + }, + "default_entry" : { + "action_id" : 3, + "action_const" : false, + "action_data" : [], + "action_entry_const" : false + } + }, + { + "name" : "tbl_act", + "id" : 1, + "key" : [], + "match_type" : "exact", + "type" : "simple", + "max_size" : 1024, + "with_counters" : false, + "support_timeout" : false, + "direct_meters" : null, + "action_ids" : [4], + "actions" : ["act"], + "base_default_next" : null, + "next_tables" : { + "act" : null + }, + "default_entry" : { + "action_id" : 4, + "action_const" : true, + "action_data" : [], + "action_entry_const" : true + } + } + ], + "action_profiles" : [], + "conditionals" : [ + { + "name" : "node_3", + "id" : 0, + "source_info" : { + "filename" : "include/port_counters.p4", + "line" : 10, + "column" : 12, + "source_fragment" : "standard_metadata.egress_spec < 254" + }, + "expression" : { + "type" : "expression", + "value" : { + "op" : "<", + "left" : { + "type" : "field", + "value" : ["standard_metadata", "egress_spec"] + }, + "right" : { + "type" : "hexstr", + "value" : "0x00fe" + } + } + }, + "false_next" : null, + "true_next" : "tbl_act" + } + ] + }, + { + "name" : "egress", + "id" : 1, + "source_info" : { + "filename" : "default.p4", + "line" : 36, + "column" : 8, + "source_fragment" : "egress" + }, + "init_table" : null, + "tables" : [], + "action_profiles" : [], + "conditionals" : [] + } + ], + "checksums" : [], + "force_arith" : [], + "extern_instances" : [], + "field_aliases" : [ + [ + "queueing_metadata.enq_timestamp", + ["standard_metadata", "enq_timestamp"] + ], + [ + "queueing_metadata.enq_qdepth", + ["standard_metadata", "enq_qdepth"] + ], + [ + "queueing_metadata.deq_timedelta", + ["standard_metadata", "deq_timedelta"] + ], + [ + "queueing_metadata.deq_qdepth", + ["standard_metadata", "deq_qdepth"] + ], + [ + "intrinsic_metadata.ingress_global_timestamp", + ["standard_metadata", "ingress_global_timestamp"] + ], + [ + "intrinsic_metadata.lf_field_list", + ["standard_metadata", "lf_field_list"] + ], + [ + "intrinsic_metadata.mcast_grp", + ["standard_metadata", "mcast_grp"] + ], + [ + "intrinsic_metadata.resubmit_flag", + ["standard_metadata", "resubmit_flag"] + ], + [ + "intrinsic_metadata.egress_rid", + ["standard_metadata", "egress_rid"] + ] + ] +} \ No newline at end of file diff --git a/drivers/bmv2/src/main/resources/default.p4info b/drivers/bmv2/src/main/resources/default.p4info new file mode 100644 index 0000000000..bd3649dfa6 --- /dev/null +++ b/drivers/bmv2/src/main/resources/default.p4info @@ -0,0 +1,113 @@ +tables { + preamble { + id: 33617813 + name: "table0" + alias: "table0" + } + match_fields { + id: 1 + name: "standard_metadata.ingress_port" + bitwidth: 9 + match_type: TERNARY + } + match_fields { + id: 2 + name: "hdr.ethernet.dstAddr" + bitwidth: 48 + match_type: TERNARY + } + match_fields { + id: 3 + name: "hdr.ethernet.srcAddr" + bitwidth: 48 + match_type: TERNARY + } + match_fields { + id: 4 + name: "hdr.ethernet.etherType" + bitwidth: 16 + match_type: TERNARY + } + action_refs { + id: 16794308 + } + action_refs { + id: 16829080 + } + action_refs { + id: 16793508 + } + action_refs { + id: 16800567 + annotations: "@defaultonly()" + } + direct_resource_ids: 301990488 + size: 1024 + with_entry_timeout: true +} +actions { + preamble { + id: 16794308 + name: "set_egress_port" + alias: "set_egress_port" + } + params { + id: 1 + name: "port" + bitwidth: 9 + } +} +actions { + preamble { + id: 16829080 + name: "send_to_cpu" + alias: "send_to_cpu" + } +} +actions { + preamble { + id: 16793508 + name: "drop" + alias: "drop" + } +} +actions { + preamble { + id: 16800567 + name: "NoAction" + alias: "NoAction" + } +} +counters { + preamble { + id: 302025528 + name: "port_counters_control.egress_port_counter" + alias: "egress_port_counter" + } + spec { + unit: PACKETS + } + size: 254 +} +counters { + preamble { + id: 301999025 + name: "port_counters_control.ingress_port_counter" + alias: "ingress_port_counter" + } + spec { + unit: PACKETS + } + size: 254 +} +direct_counters { + preamble { + id: 301990488 + name: "table0_counter" + alias: "table0_counter" + } + spec { + unit: PACKETS + } + direct_table_id: 33617813 +} diff --git a/protocols/grpc/api/src/main/java/org/onosproject/grpc/api/GrpcController.java b/protocols/grpc/api/src/main/java/org/onosproject/grpc/api/GrpcController.java index f1e279a255..629aad1088 100644 --- a/protocols/grpc/api/src/main/java/org/onosproject/grpc/api/GrpcController.java +++ b/protocols/grpc/api/src/main/java/org/onosproject/grpc/api/GrpcController.java @@ -57,7 +57,7 @@ public interface GrpcController { Optional getObserverManager(GrpcStreamObserverId observerId); /** - * Tries to connect to a specific gRPC device, if the connection is successful + * Tries to connect to a specific gRPC server, if the connection is successful * returns the ManagedChannel. This method blocks until the channel is setup or a timeout expires. * By default the timeout is 20 seconds. If the timeout expires and thus the channel can't be set up * a IOException is thrown. @@ -83,6 +83,15 @@ public interface GrpcController { */ Map getChannels(); + /** + * Returns true if the channel associated with the given identifier is open, i.e. the server is able to successfully + * responds to RPCs. + * + * @param channelId channel identifier + * @return true if channel is open, false otherwise. + */ + boolean isChannelOpen(GrpcChannelId channelId); + /** * Returns all ManagedChannels associated to the given device identifier. * diff --git a/protocols/grpc/ctl/BUCK b/protocols/grpc/ctl/BUCK index 764d980027..18e041110d 100644 --- a/protocols/grpc/ctl/BUCK +++ b/protocols/grpc/ctl/BUCK @@ -1,11 +1,13 @@ COMPILE_DEPS = [ '//lib:CORE_DEPS', '//protocols/grpc/api:onos-protocols-grpc-api', + '//protocols/grpc/proto:onos-protocols-grpc-proto', '//lib:grpc-core-1.3.0', '//lib:grpc-protobuf-1.3.0', '//lib:grpc-stub-1.3.0', '//lib:grpc-netty-1.3.0', '//lib:grpc-auth-1.3.0', + '//lib:protobuf-java-3.0.2', ] TEST_DEPS = [ diff --git a/protocols/grpc/ctl/src/main/java/org/onosproject/grpc/ctl/GrpcControllerImpl.java b/protocols/grpc/ctl/src/main/java/org/onosproject/grpc/ctl/GrpcControllerImpl.java index 4ce097dd91..152106a998 100644 --- a/protocols/grpc/ctl/src/main/java/org/onosproject/grpc/ctl/GrpcControllerImpl.java +++ b/protocols/grpc/ctl/src/main/java/org/onosproject/grpc/ctl/GrpcControllerImpl.java @@ -19,6 +19,8 @@ package org.onosproject.grpc.ctl; import com.google.common.collect.ImmutableSet; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; @@ -27,16 +29,20 @@ import org.onosproject.grpc.api.GrpcChannelId; import org.onosproject.grpc.api.GrpcController; import org.onosproject.grpc.api.GrpcObserverHandler; import org.onosproject.grpc.api.GrpcStreamObserverId; +import org.onosproject.grpc.ctl.dummy.Dummy; +import org.onosproject.grpc.ctl.dummy.DummyServiceGrpc; import org.onosproject.net.DeviceId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; /** * Default implementation of the GrpcController. @@ -45,6 +51,8 @@ import java.util.concurrent.ConcurrentHashMap; @Service public class GrpcControllerImpl implements GrpcController { + private static final int CONNECTION_TIMEOUT_SECONDS = 20; + public static final Logger log = LoggerFactory .getLogger(GrpcControllerImpl.class); @@ -87,18 +95,63 @@ public class GrpcControllerImpl implements GrpcController { } @Override - public ManagedChannel connectChannel(GrpcChannelId channelId, ManagedChannelBuilder channelBuilder) { + public ManagedChannel connectChannel(GrpcChannelId channelId, ManagedChannelBuilder channelBuilder) + throws IOException { ManagedChannel channel = channelBuilder.build(); - channel.getState(true); + // Forced connection not yet implemented. Use workaround... + // channel.getState(true); + doDummyMessage(channel); + channelBuilders.put(channelId, channelBuilder); channels.put(channelId, channel); return channel; } + private void doDummyMessage(ManagedChannel channel) throws IOException { + DummyServiceGrpc.DummyServiceBlockingStub dummyStub = DummyServiceGrpc.newBlockingStub(channel) + .withDeadlineAfter(CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS); + try { + dummyStub.sayHello(Dummy.DummyMessageThatNoOneWouldReallyUse.getDefaultInstance()); + } catch (StatusRuntimeException e) { + if (e.getStatus() != Status.UNIMPLEMENTED) { + // UNIMPLEMENTED means that server received our message but doesn't know how to handle it. + // Hence, channel is open. + throw new IOException(e); + } + } + } + + @Override + public boolean isChannelOpen(GrpcChannelId channelId) { + if (!channels.containsKey(channelId)) { + log.warn("Can't check if channel open for unknown channel id {}", channelId); + return false; + } + + try { + doDummyMessage(channels.get(channelId)); + return true; + } catch (IOException e) { + return false; + } + } + @Override public void disconnectChannel(GrpcChannelId channelId) { - channels.get(channelId).shutdown(); + if (!channels.containsKey(channelId)) { + // Nothing to do. + return; + } + ManagedChannel channel = channels.get(channelId); + + try { + channel.shutdown().awaitTermination(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + log.warn("Channel {} didn't shut down in time."); + channel.shutdownNow(); + } + channels.remove(channelId); channelBuilders.remove(channelId); } diff --git a/protocols/grpc/proto/BUCK b/protocols/grpc/proto/BUCK new file mode 100644 index 0000000000..f354b399fd --- /dev/null +++ b/protocols/grpc/proto/BUCK @@ -0,0 +1,28 @@ +include_defs( + '//bucklets/grpc.bucklet' +) + +PROTOC_VER = '3.0.2' +GRPC_VER = '1.3.0' + + +COMPILE_DEPS = [ + '//lib:CORE_DEPS', + '//incubator/grpc-dependencies:grpc-core-repkg-' + GRPC_VER, + '//lib:grpc-stub-' + GRPC_VER, + '//lib:grpc-protobuf-' + GRPC_VER, + '//lib:protobuf-java-' + PROTOC_VER, +] + + +grpc_jar( + proto_match_patterns = ["*.proto"], + proto_paths = ["$ONOS_ROOT/protocols/grpc/proto/"], + protoc_version = PROTOC_VER, + plugin_version = GRPC_VER, + deps = COMPILE_DEPS, +) + +project_config( + src_target = ':onos-protocols-grpc-proto' +) \ No newline at end of file diff --git a/protocols/grpc/proto/dummy.proto b/protocols/grpc/proto/dummy.proto new file mode 100644 index 0000000000..d02b5d815f --- /dev/null +++ b/protocols/grpc/proto/dummy.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +option java_package = "org.onosproject.grpc.ctl.dummy"; + +package dummy; + +service DummyService { + rpc SayHello (DummyMessageThatNoOneWouldReallyUse) returns (DummyMessageThatNoOneWouldReallyUse) {} +} + +message DummyMessageThatNoOneWouldReallyUse { +} \ No newline at end of file diff --git a/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeController.java b/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeController.java index 3b8074f71d..ecdcb31c61 100644 --- a/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeController.java +++ b/protocols/p4runtime/api/src/main/java/org/onosproject/p4runtime/api/P4RuntimeController.java @@ -65,4 +65,14 @@ public interface P4RuntimeController extends ListenerService streamRequestObserver; + private Context.CancellableContext streamContext; P4RuntimeClientImpl(DeviceId deviceId, int p4DeviceId, ManagedChannel channel, P4RuntimeControllerImpl controller, @@ -74,8 +76,7 @@ public class P4RuntimeClientImpl implements P4RuntimeClient { this.executorService = executorService; this.blockingStub = P4RuntimeGrpc.newBlockingStub(channel) .withDeadlineAfter(DEADLINE_SECONDS, TimeUnit.SECONDS); - this.asyncStub = P4RuntimeGrpc.newStub(channel) - .withDeadlineAfter(DEADLINE_SECONDS, TimeUnit.SECONDS); + this.asyncStub = P4RuntimeGrpc.newStub(channel); } @Override @@ -85,21 +86,44 @@ public class P4RuntimeClientImpl implements P4RuntimeClient { private boolean doInitStreamChannel() { if (this.streamRequestObserver == null) { - this.streamRequestObserver = this.asyncStub.streamChannel(new StreamChannelResponseObserver()); + + streamContext = Context.current().withCancellation(); + streamContext.run( + () -> streamRequestObserver = asyncStub.streamChannel(new StreamChannelResponseObserver())); + // To listen for packets and other events, we need to start the RPC. - // Here we do it by sending an empty packet out. - try { - this.streamRequestObserver.onNext(StreamMessageRequest.newBuilder() - .setPacket(PacketOut.getDefaultInstance()) - .build()); - } catch (StatusRuntimeException e) { - log.warn("Unable to initialize stream channel for {}: {}", deviceId, e); + // Here we do it by sending a master arbitration update. + if (!doArbitrationUpdate()) { + log.warn("Unable to initialize stream channel for {}", deviceId); return false; } } return true; } + private boolean doArbitrationUpdate() { + + if (streamRequestObserver == null) { + log.error("Null request stream observer for {}", deviceId); + return false; + } + + try { + StreamMessageRequest initRequest = StreamMessageRequest + .newBuilder() + .setArbitration(MasterArbitrationUpdate + .newBuilder() + .setDeviceId(p4DeviceId) + .build()) + .build(); + streamRequestObserver.onNext(initRequest); + return true; + } catch (StatusRuntimeException e) { + log.warn("Arbitration update failed for {}: {}", deviceId, e); + return false; + } + } + @Override public CompletableFuture setPipelineConfig(InputStream p4info, InputStream targetConfig) { return CompletableFuture.supplyAsync(() -> doSetPipelineConfig(p4info, targetConfig), executorService); @@ -168,12 +192,15 @@ public class P4RuntimeClientImpl implements P4RuntimeClient { @Override public void shutdown() { - if (this.streamRequestObserver != null) { - this.streamRequestObserver.onError(new StatusRuntimeException(Status.CANCELLED)); - this.streamRequestObserver.onCompleted(); + log.info("Shutting down client for {}...", deviceId); + + if (streamRequestObserver != null) { + streamRequestObserver.onCompleted(); + streamContext.cancel(null); + streamContext = null; } - this.executorService.shutdownNow(); + this.executorService.shutdown(); try { executorService.awaitTermination(5, TimeUnit.SECONDS); } catch (InterruptedException e) { @@ -216,12 +243,16 @@ public class P4RuntimeClientImpl implements P4RuntimeClient { @Override public void onError(Throwable throwable) { - log.warn("Error on stream channel for {}: {}", deviceId, throwable); + log.warn("Error on stream channel for {}: {}", deviceId, Status.fromThrowable(throwable)); + // FIXME: we might want to recreate the channel. + // In general, we want to be robust against any transient error and, if the channel is open, make sure the + // stream channel is always on. } @Override public void onCompleted() { - // TODO: declare the device as disconnected? + log.warn("Stream channel for {} has completed", deviceId); + // FIXME: same concern as before. } } diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeControllerImpl.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeControllerImpl.java index cd4151c980..06376cae60 100644 --- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeControllerImpl.java +++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeControllerImpl.java @@ -39,8 +39,8 @@ import org.slf4j.Logger; import java.io.IOException; import java.util.Map; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import static com.google.common.base.Preconditions.checkNotNull; import static java.lang.String.format; @@ -62,10 +62,11 @@ public class P4RuntimeControllerImpl private final Logger log = getLogger(getClass()); private final NameResolverProvider nameResolverProvider = new DnsNameResolverProvider(); - private final Map clients = Maps.newConcurrentMap(); - private final Map channelIds = Maps.newConcurrentMap(); + private final Map clients = Maps.newHashMap(); + private final Map channelIds = Maps.newHashMap(); + // TODO: should use a cache to delete unused locks. - private final Map deviceLocks = Maps.newConcurrentMap(); + private final Map deviceLocks = Maps.newConcurrentMap(); @Activate public void activate() { @@ -85,8 +86,8 @@ public class P4RuntimeControllerImpl checkNotNull(deviceId); checkNotNull(channelBuilder); - deviceLocks.putIfAbsent(deviceId, new ReentrantLock()); - deviceLocks.get(deviceId).lock(); + deviceLocks.putIfAbsent(deviceId, new ReentrantReadWriteLock()); + deviceLocks.get(deviceId).writeLock().lock(); log.info("Creating client for {} (with internal device id {})...", deviceId, p4DeviceId); @@ -97,11 +98,12 @@ public class P4RuntimeControllerImpl return doCreateClient(deviceId, p4DeviceId, channelBuilder); } } finally { - deviceLocks.get(deviceId).unlock(); + deviceLocks.get(deviceId).writeLock().unlock(); } } private boolean doCreateClient(DeviceId deviceId, int p4DeviceId, ManagedChannelBuilder channelBuilder) { + GrpcChannelId channelId = GrpcChannelId.of(deviceId, "p4runtime"); // Channel defaults. @@ -127,43 +129,62 @@ public class P4RuntimeControllerImpl @Override public P4RuntimeClient getClient(DeviceId deviceId) { - deviceLocks.putIfAbsent(deviceId, new ReentrantLock()); - deviceLocks.get(deviceId).lock(); + deviceLocks.putIfAbsent(deviceId, new ReentrantReadWriteLock()); + deviceLocks.get(deviceId).readLock().lock(); try { return clients.get(deviceId); } finally { - deviceLocks.get(deviceId).unlock(); + deviceLocks.get(deviceId).readLock().unlock(); } } @Override public void removeClient(DeviceId deviceId) { - deviceLocks.putIfAbsent(deviceId, new ReentrantLock()); - deviceLocks.get(deviceId).lock(); + deviceLocks.putIfAbsent(deviceId, new ReentrantReadWriteLock()); + deviceLocks.get(deviceId).writeLock().lock(); try { if (clients.containsKey(deviceId)) { clients.get(deviceId).shutdown(); grpcController.disconnectChannel(channelIds.get(deviceId)); clients.remove(deviceId); + channelIds.remove(deviceId); } } finally { - deviceLocks.get(deviceId).unlock(); + deviceLocks.get(deviceId).writeLock().unlock(); } } @Override public boolean hasClient(DeviceId deviceId) { - deviceLocks.putIfAbsent(deviceId, new ReentrantLock()); - deviceLocks.get(deviceId).lock(); + deviceLocks.putIfAbsent(deviceId, new ReentrantReadWriteLock()); + deviceLocks.get(deviceId).readLock().lock(); try { return clients.containsKey(deviceId); } finally { - deviceLocks.get(deviceId).unlock(); + deviceLocks.get(deviceId).readLock().unlock(); + } + } + + @Override + public boolean isReacheable(DeviceId deviceId) { + + deviceLocks.putIfAbsent(deviceId, new ReentrantReadWriteLock()); + deviceLocks.get(deviceId).readLock().lock(); + + try { + if (!clients.containsKey(deviceId)) { + log.warn("No client for {}, can't check for reachability", deviceId); + return false; + } + + return grpcController.isChannelOpen(channelIds.get(deviceId)); + } finally { + deviceLocks.get(deviceId).readLock().unlock(); } } diff --git a/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java b/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java index 1b3c459660..ad48e1eccb 100644 --- a/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java +++ b/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java @@ -60,7 +60,9 @@ import org.onosproject.net.driver.DefaultDriverHandler; import org.onosproject.net.driver.Driver; import org.onosproject.net.driver.DriverData; import org.onosproject.net.driver.DriverService; +import org.onosproject.net.pi.model.PiPipeconf; import org.onosproject.net.pi.model.PiPipeconfId; +import org.onosproject.net.pi.model.PiPipelineProgrammable; import org.onosproject.net.pi.runtime.PiPipeconfConfig; import org.onosproject.net.pi.runtime.PiPipeconfService; import org.onosproject.net.provider.AbstractProvider; @@ -72,7 +74,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentMap; @@ -139,12 +140,12 @@ public class GeneralDeviceProvider extends AbstractProvider protected ScheduledExecutorService connectionExecutor = newScheduledThreadPool(CORE_POOL_SIZE, - groupedThreads("onos/generaldeviceprovider-device", - "connection-executor-%d", log)); + groupedThreads("onos/generaldeviceprovider-device", + "connection-executor-%d", log)); protected ScheduledExecutorService portStatsExecutor = newScheduledThreadPool(CORE_POOL_SIZE, - groupedThreads("onos/generaldeviceprovider-port-stats", - "port-stats-executor-%d", log)); + groupedThreads("onos/generaldeviceprovider-port-stats", + "port-stats-executor-%d", log)); protected DeviceProviderService providerService; private InternalDeviceListener deviceListener = new InternalDeviceListener(); @@ -239,7 +240,7 @@ public class GeneralDeviceProvider extends AbstractProvider modified.thenAcceptAsync(result -> { if (!result) { log.warn("Your device {} port {} status can't be changed to {}", - deviceId, portNumber, enable); + deviceId, portNumber, enable); } }); @@ -251,13 +252,13 @@ public class GeneralDeviceProvider extends AbstractProvider private DeviceHandshaker getHandshaker(DeviceId deviceId) { Driver driver = getDriver(deviceId); return getBehaviour(driver, DeviceHandshaker.class, - new DefaultDriverData(driver, deviceId)); + new DefaultDriverData(driver, deviceId)); } private PortAdmin getPortAdmin(DeviceId deviceId) { Driver driver = getDriver(deviceId); return getBehaviour(driver, PortAdmin.class, - new DefaultDriverData(driver, deviceId)); + new DefaultDriverData(driver, deviceId)); } @@ -267,7 +268,7 @@ public class GeneralDeviceProvider extends AbstractProvider driver = driverService.getDriver(deviceId); } catch (ItemNotFoundException e) { log.debug("Falling back to configuration to fetch driver " + - "for device {}", deviceId); + "for device {}", deviceId); driver = driverService.getDriver( cfgService.getConfig(deviceId, BasicDeviceConfig.class).driver()); } @@ -298,7 +299,7 @@ public class GeneralDeviceProvider extends AbstractProvider if (providerConfig == null || basicDeviceConfig == null) { log.error("Configuration is NULL: basic config {}, general provider " + - "config {}", basicDeviceConfig, providerConfig); + "config {}", basicDeviceConfig, providerConfig); } else { log.info("Connecting to device {} with driver {}", deviceId, basicDeviceConfig.driver()); @@ -310,7 +311,7 @@ public class GeneralDeviceProvider extends AbstractProvider if (handshaker == null) { log.error("Device {}, with driver {} does not support DeviceHandshaker " + - "behaviour, {}", deviceId, driver.name(), driver.behaviours()); + "behaviour, {}", deviceId, driver.name(), driver.behaviours()); return; } //Storing deviceKeyId and all other config values @@ -333,13 +334,13 @@ public class GeneralDeviceProvider extends AbstractProvider ChassisId cid = new ChassisId(); SparseAnnotations annotations = DefaultAnnotations.builder() .set(AnnotationKeys.PROTOCOL, - providerConfig.protocolsInfo().keySet().toString()) + providerConfig.protocolsInfo().keySet().toString()) .build(); DeviceDescription description = new DefaultDeviceDescription(deviceId.uri(), Device.Type.SWITCH, - driver.manufacturer(), driver.hwVersion(), - driver.swVersion(), UNKNOWN, - cid, false, annotations); + driver.manufacturer(), driver.hwVersion(), + driver.swVersion(), UNKNOWN, + cid, false, annotations); //Empty list of ports List ports = new ArrayList<>(); @@ -354,27 +355,15 @@ public class GeneralDeviceProvider extends AbstractProvider ports = deviceDiscovery.discoverPortDetails(); } - Optional pipeconfId = piPipeconfService.ofDevice(deviceId); - //Apply the Pipeline configuration and then connect the device - if (pipeconfId.isPresent()) { - DeviceDescription finalDescription = description; - List finalPorts = ports; - piPipeconfService.bindToDevice(pipeconfId.get(), deviceId).whenComplete((success, ex) -> { - if (success) { - advertiseDevice(deviceId, finalDescription, finalPorts); - } else { - log.error("Can't merge driver {} with pipeconf {} for device {}, " + - "not reporting it to the device manager", - driver.name(), pipeconfId.get(), deviceId); - } - }).exceptionally(ex -> { - throw new IllegalStateException(ex); - }); - } else { - //No other operation is needed, advertise the device to the core. - advertiseDevice(deviceId, description, ports); + if (!handlePipeconf(deviceId, driver, driverData)) { + // Something went wrong during handling of pipeconf. + // We already logged the error. + handshaker.disconnect(); + return; } + advertiseDevice(deviceId, description, ports); + } else { log.warn("Can't connect to device {}", deviceId); } @@ -382,6 +371,66 @@ public class GeneralDeviceProvider extends AbstractProvider } } + /** + * Handles the case of a device that is pipeline programmable. Returns true if the operation wa successful and the + * device can be registered to the core, false otherwise. + */ + private boolean handlePipeconf(DeviceId deviceId, Driver driver, DriverData driverData) { + + PiPipelineProgrammable pipelineProg = getBehaviour(driver, PiPipelineProgrammable.class, + driverData); + + if (pipelineProg == null) { + // Device is not pipeline programmable. + return true; + } + + PiPipeconfId pipeconfId = piPipeconfService.ofDevice(deviceId).orElseGet(() -> { + // No pipeconf has been associated with this device. + // Check if device driver provides a default one. + if (pipelineProg.getDefaultPipeconf().isPresent()) { + PiPipeconf defaultPipeconf = pipelineProg.getDefaultPipeconf().get(); + log.info("Using default pipeconf {} for {}", defaultPipeconf.id(), deviceId); + // Register default one if it is not. + // TODO: this should be performed at driver loading. + if (!piPipeconfService.getPipeconf(defaultPipeconf.id()).isPresent()) { + piPipeconfService.register(defaultPipeconf); + } + return defaultPipeconf.id(); + } else { + return null; + } + }); + + if (pipeconfId == null) { + log.warn("Device {} is pipeline programmable but no pipeconf can be associated to it.", deviceId); + return true; + } + + + PiPipeconf pipeconf = piPipeconfService.getPipeconf(pipeconfId).orElseThrow( + () -> new IllegalStateException("Pipeconf is not registered: " + pipeconfId) + ); + + + try { + if (!pipelineProg.deployPipeconf(pipeconf).get()) { + log.error("Unable to deploy pipeconf {} to {}, aborting device discovery", pipeconfId, deviceId); + return false; + } + + if (!piPipeconfService.bindToDevice(pipeconfId, deviceId).get()) { + log.error("Unable to merge driver {} for device {} with pipeconf {}, aborting device discovery", + driver.name(), deviceId, pipeconfId); + return false; + } + } catch (InterruptedException | ExecutionException e) { + throw new IllegalStateException(e); + } + + return true; + } + private void advertiseDevice(DeviceId deviceId, DeviceDescription description, List ports) { providerService.deviceConnected(deviceId, description); providerService.updatePorts(deviceId, ports); @@ -454,7 +503,7 @@ public class GeneralDeviceProvider extends AbstractProvider //If we want to connect a p4runtime device with no pipeline if (event.config().isPresent() && Collections.disjoint(ImmutableSet.copyOf(event.config().get().node().fieldNames()), - PIPELINE_CONFIGURABLE_PROTOCOLS)) { + PIPELINE_CONFIGURABLE_PROTOCOLS)) { pipelineConfigured.add(deviceId); } deviceConfigured.add(deviceId); @@ -526,15 +575,15 @@ public class GeneralDeviceProvider extends AbstractProvider // be available we check and base it on the streaming API (e.g. gNMI) if (deviceService.getDevice(event.subject().id()). is(PortStatisticsDiscovery.class)) { - portStatsExecutor.scheduleAtFixedRate(exceptionSafe(() -> - updatePortStatistics(event.subject().id())), + portStatsExecutor.scheduleAtFixedRate( + exceptionSafe(() -> updatePortStatistics(event.subject().id())), 0, PORT_STATS_PERIOD_SECONDS, TimeUnit.SECONDS); updatePortStatistics(event.subject().id()); } } else if (type.equals(Type.DEVICE_REMOVED)) { connectionExecutor.submit(exceptionSafe(() -> - disconnectDevice(event.subject().id()))); + disconnectDevice(event.subject().id()))); } } diff --git a/tools/dev/mininet/bmv2.py b/tools/dev/mininet/bmv2.py index e1d51a5a3c..a0f92d2646 100644 --- a/tools/dev/mininet/bmv2.py +++ b/tools/dev/mininet/bmv2.py @@ -79,9 +79,13 @@ class ONOSBmv2Switch(Switch): "p4runtime": { "ip": srcIP, "port": self.grpcPort, + "deviceId": self.deviceId, "deviceKeyId": "p4runtime:%s" % onosDeviceId } }, + "piPipeconf": { + "piPipeconfId": "" + }, "basic": { "driver": "bmv2" }