From dbee233e7ff18caf492f7ed4f14a1b6a700cadba Mon Sep 17 00:00:00 2001 From: sangho Date: Thu, 18 May 2017 09:59:16 +0900 Subject: [PATCH] [ONOS-6484] Implements skeleton to reimplement OpenStackPipeline in the application layer. Change-Id: I3a14685bd520293e167fab262e87ba82210e94ef --- .../openstacknetworking/api/Constants.java | 5 + .../api/OpenstackFlowRuleService.java | 36 +++ .../impl/OpenstackFlowRuleManager.java | 258 ++++++++++++++++++ 3 files changed, 299 insertions(+) create mode 100644 apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackFlowRuleService.java create mode 100644 apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFlowRuleManager.java diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java index a1d5ace124..f42923c0ad 100644 --- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java +++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java @@ -41,4 +41,9 @@ public final class Constants { public static final int PRIORITY_SWITCHING_RULE = 30000; public static final int PRIORITY_ACL_RULE = 30000; + public static final int SRC_VNI_TABLE = 0; + public static final int ACL_TABLE = 1; + public static final int JUMP_TABLE = 2; + public static final int ROUTING_TABLE = 3; + public static final int FORWARDING_TABLE = 4; } \ No newline at end of file diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackFlowRuleService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackFlowRuleService.java new file mode 100644 index 0000000000..01da935e43 --- /dev/null +++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackFlowRuleService.java @@ -0,0 +1,36 @@ +/* + * 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.openstacknetworking.api; + + +import org.onosproject.net.DeviceId; +import org.onosproject.net.flowobjective.ForwardingObjective; + +/** + * Service for setting flow rules. + * + */ +public interface OpenstackFlowRuleService { + + /** + * Sets flow rules. + * + * @param deviceId Device ID + * @param forwardingObjective flow rule container + * @param tableType table type for the flow rules + */ + void forward(DeviceId deviceId, ForwardingObjective forwardingObjective, int tableType); +} diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFlowRuleManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFlowRuleManager.java new file mode 100644 index 0000000000..6e8e7e0955 --- /dev/null +++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFlowRuleManager.java @@ -0,0 +1,258 @@ +/* + * 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.openstacknetworking.impl; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.Service; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.net.DeviceId; +import org.onosproject.net.flow.DefaultFlowRule; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.net.flow.FlowRuleOperations; +import org.onosproject.net.flow.FlowRuleOperationsContext; +import org.onosproject.net.flow.FlowRuleService; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.instructions.Instruction; +import org.onosproject.net.flowobjective.ForwardingObjective; +import org.onosproject.net.flowobjective.Objective; +import org.onosproject.openstacknetworking.api.Constants; +import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService; +import org.onosproject.openstacknode.OpenstackNode; +import org.onosproject.openstacknode.OpenstackNodeEvent; +import org.onosproject.openstacknode.OpenstackNodeListener; +import org.onosproject.openstacknode.OpenstackNodeService; +import org.slf4j.Logger; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static org.onlab.util.Tools.groupedThreads; +import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Sets flow rules directly using FlowRuleService. + */ +@Service +@Component(immediate = true) +public class OpenstackFlowRuleManager implements OpenstackFlowRuleService { + + private final Logger log = getLogger(getClass()); + + private static final int DROP_PRIORITY = 0; + private static final int HIGH_PRIORITY = 30000; + private static final int FLOW_RULE_TIME_OUT = 60; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected FlowRuleService flowRuleService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected OpenstackNodeService osNodeService; + + private final ExecutorService deviceEventExecutor = + Executors.newSingleThreadExecutor(groupedThreads("openstacknetworking", "device-event")); + private final InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener(); + + private ApplicationId appId; + + @Activate + protected void activate() { + appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID); + coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID); + osNodeService.addListener(internalNodeListener); + + log.info("Started"); + } + + @Deactivate + protected void deactivate() { + osNodeService.removeListener(internalNodeListener); + deviceEventExecutor.shutdown(); + + log.info("Stopped"); + } + + @Override + public void forward(DeviceId deviceId, ForwardingObjective forwardingObjective, int tableType) { + TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); + + forwardingObjective.treatment().allInstructions().stream() + .filter(i -> i.type() != Instruction.Type.NOACTION).forEach(treatment::add); + + FlowRule.Builder ruleBuilder = DefaultFlowRule.builder() + .forDevice(deviceId) + .withSelector(forwardingObjective.selector()) + .withTreatment(treatment.build()) + .withPriority(forwardingObjective.priority()) + .fromApp(forwardingObjective.appId()) + .forTable(tableType); + + if (forwardingObjective.permanent()) { + ruleBuilder.makePermanent(); + } else { + ruleBuilder.makeTemporary(FLOW_RULE_TIME_OUT); + } + + if (forwardingObjective.op().equals(Objective.Operation.ADD)) { + applyRules(true, ruleBuilder.build()); + } else { + applyRules(false, ruleBuilder.build()); + } + } + + private void applyRules(boolean install, FlowRule flowRule) { + FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder(); + + flowOpsBuilder = install ? flowOpsBuilder.add(flowRule) : flowOpsBuilder.remove(flowRule); + + flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() { + @Override + public void onSuccess(FlowRuleOperations ops) { + log.debug("Provisioned vni or forwarding table"); + } + + @Override + public void onError(FlowRuleOperations ops) { + log.debug("Failed to privision vni or forwarding table"); + } + })); + } + + private void initializePipeline(DeviceId deviceId) { + connectTables(deviceId, Constants.SRC_VNI_TABLE, Constants.ACL_TABLE); + connectTables(deviceId, Constants.ACL_TABLE, Constants.JUMP_TABLE); + setUpTableMissEntry(deviceId, Constants.ACL_TABLE); + setupJumpTable(deviceId); + } + + private void connectTables(DeviceId deviceId, int fromTable, int toTable) { + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); + + treatment.transition(toTable); + + FlowRule flowRule = DefaultFlowRule.builder() + .forDevice(deviceId) + .withSelector(selector.build()) + .withTreatment(treatment.build()) + .withPriority(DROP_PRIORITY) + .fromApp(appId) + .makePermanent() + .forTable(fromTable) + .build(); + + applyRules(true, flowRule); + } + + private void setUpTableMissEntry(DeviceId deviceId, int table) { + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); + + treatment.drop(); + + FlowRule flowRule = DefaultFlowRule.builder() + .forDevice(deviceId) + .withSelector(selector.build()) + .withTreatment(treatment.build()) + .withPriority(DROP_PRIORITY) + .fromApp(appId) + .makePermanent() + .forTable(table) + .build(); + + applyRules(true, flowRule); + } + + private void setupJumpTable(DeviceId deviceId) { + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); + + selector.matchEthDst(Constants.DEFAULT_GATEWAY_MAC); + treatment.transition(Constants.ROUTING_TABLE); + + FlowRule flowRule = DefaultFlowRule.builder() + .forDevice(deviceId) + .withSelector(selector.build()) + .withTreatment(treatment.build()) + .withPriority(HIGH_PRIORITY) + .fromApp(appId) + .makePermanent() + .forTable(Constants.JUMP_TABLE) + .build(); + + applyRules(true, flowRule); + + selector = DefaultTrafficSelector.builder(); + treatment = DefaultTrafficTreatment.builder(); + + treatment.transition(Constants.FORWARDING_TABLE); + + flowRule = DefaultFlowRule.builder() + .forDevice(deviceId) + .withSelector(selector.build()) + .withTreatment(treatment.build()) + .withPriority(DROP_PRIORITY) + .fromApp(appId) + .makePermanent() + .forTable(Constants.JUMP_TABLE) + .build(); + + applyRules(true, flowRule); + } + + private class InternalOpenstackNodeListener implements OpenstackNodeListener { + + @Override + public void event(OpenstackNodeEvent event) { + OpenstackNode osNode = event.subject(); + // TODO check leadership of the node and make only the leader process + + switch (event.type()) { + case COMPLETE: + deviceEventExecutor.execute(() -> { + log.info("COMPLETE node {} is detected", osNode.hostname()); + + }); + break; + case INCOMPLETE: + log.warn("{} is changed to INCOMPLETE state", osNode); + break; + case INIT: + case DEVICE_CREATED: + default: + break; + } + } + + private void processCompleteNode(OpenstackNode osNode) { + if (osNode.type().equals(OpenstackNodeService.NodeType.COMPUTE)) { + initializePipeline(osNode.intBridge()); + } + } + } +}