Hyunsun Moon

Refactored to handle service instance by service type

- Added service instance handler
- Implemented dummy, vsg, and olt agent instance handler

Change-Id: Id3edd5eecb1caadf0f835cb10a952100e18b283b
...@@ -11,6 +11,7 @@ COMPILE_DEPS = [ ...@@ -11,6 +11,7 @@ COMPILE_DEPS = [
11 '//core/store/serializers:onos-core-serializers', 11 '//core/store/serializers:onos-core-serializers',
12 '//apps/dhcp/api:onos-apps-dhcp-api', 12 '//apps/dhcp/api:onos-apps-dhcp-api',
13 '//apps/xosclient:onos-apps-xosclient', 13 '//apps/xosclient:onos-apps-xosclient',
14 + '//apps/cordconfig:onos-apps-cordconfig',
14 '//protocols/ovsdb/api:onos-protocols-ovsdb-api', 15 '//protocols/ovsdb/api:onos-protocols-ovsdb-api',
15 '//protocols/ovsdb/rfc:onos-protocols-ovsdb-rfc', 16 '//protocols/ovsdb/rfc:onos-protocols-ovsdb-rfc',
16 ] 17 ]
...@@ -35,5 +36,5 @@ onos_app ( ...@@ -35,5 +36,5 @@ onos_app (
35 included_bundles = BUNDLES, 36 included_bundles = BUNDLES,
36 excluded_bundles = EXCLUDED_BUNDLES, 37 excluded_bundles = EXCLUDED_BUNDLES,
37 description = 'APIs for interacting with the CORD VTN application.', 38 description = 'APIs for interacting with the CORD VTN application.',
38 - required_apps = [ 'org.onosproject.xosclient', 'org.onosproject.dhcp', 'org.onosproject.ovsdb' ], 39 + required_apps = [ 'org.onosproject.cord-config', 'org.onosproject.xosclient', 'org.onosproject.dhcp', 'org.onosproject.ovsdb' ],
39 ) 40 )
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
18 category="Traffic Steering" url="http://onosproject.org" title="CORD Virtual Tenant Network" 18 category="Traffic Steering" url="http://onosproject.org" title="CORD Virtual Tenant Network"
19 featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features" 19 featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features"
20 features="${project.artifactId}" 20 features="${project.artifactId}"
21 - apps="org.onosproject.ovsdb-base,org.onosproject.dhcp,org.onosproject.xosclient"> 21 + apps="org.onosproject.ovsdb-base,org.onosproject.dhcp,org.onosproject.xosclient,org.onosproject.cord-config">
22 <description>${project.description}</description> 22 <description>${project.description}</description>
23 <artifact>mvn:${project.groupId}/onos-app-cordvtn/${project.version}</artifact> 23 <artifact>mvn:${project.groupId}/onos-app-cordvtn/${project.version}</artifact>
24 </app> 24 </app>
......
...@@ -108,6 +108,11 @@ ...@@ -108,6 +108,11 @@
108 <version>${project.version}</version> 108 <version>${project.version}</version>
109 </dependency> 109 </dependency>
110 <dependency> 110 <dependency>
111 + <groupId>org.onosproject</groupId>
112 + <artifactId>onos-cord-config</artifactId>
113 + <version>${project.version}</version>
114 + </dependency>
115 + <dependency>
111 <groupId>com.jcraft</groupId> 116 <groupId>com.jcraft</groupId>
112 <artifactId>jsch</artifactId> 117 <artifactId>jsch</artifactId>
113 <version>0.1.53</version> 118 <version>0.1.53</version>
......
...@@ -20,6 +20,7 @@ import com.google.common.collect.Maps; ...@@ -20,6 +20,7 @@ import com.google.common.collect.Maps;
20 import com.google.common.collect.Sets; 20 import com.google.common.collect.Sets;
21 import org.onlab.packet.Ip4Address; 21 import org.onlab.packet.Ip4Address;
22 import org.onlab.packet.IpAddress; 22 import org.onlab.packet.IpAddress;
23 +import org.onlab.packet.IpPrefix;
23 import org.onlab.packet.MacAddress; 24 import org.onlab.packet.MacAddress;
24 import org.onlab.packet.TpPort; 25 import org.onlab.packet.TpPort;
25 import org.onosproject.core.ApplicationId; 26 import org.onosproject.core.ApplicationId;
...@@ -46,6 +47,7 @@ public class CordVtnConfig extends Config<ApplicationId> { ...@@ -46,6 +47,7 @@ public class CordVtnConfig extends Config<ApplicationId> {
46 public static final String GATEWAY_IP = "gatewayIp"; 47 public static final String GATEWAY_IP = "gatewayIp";
47 public static final String GATEWAY_MAC = "gatewayMac"; 48 public static final String GATEWAY_MAC = "gatewayMac";
48 public static final String LOCAL_MANAGEMENT_IP = "localManagementIp"; 49 public static final String LOCAL_MANAGEMENT_IP = "localManagementIp";
50 + public static final String MANAGEMENT_IP = "managementIpRange";
49 public static final String OVSDB_PORT = "ovsdbPort"; 51 public static final String OVSDB_PORT = "ovsdbPort";
50 52
51 public static final String CORDVTN_NODES = "nodes"; 53 public static final String CORDVTN_NODES = "nodes";
...@@ -187,6 +189,25 @@ public class CordVtnConfig extends Config<ApplicationId> { ...@@ -187,6 +189,25 @@ public class CordVtnConfig extends Config<ApplicationId> {
187 } 189 }
188 190
189 /** 191 /**
192 + * Returns management IP address range.
193 + *
194 + * @return management network ip prefix, or null
195 + */
196 + public IpPrefix managementIpRange() {
197 + JsonNode jsonNode = object.get(MANAGEMENT_IP);
198 + if (jsonNode == null) {
199 + return null;
200 + }
201 +
202 + try {
203 + return IpPrefix.valueOf(jsonNode.asText());
204 + } catch (IllegalArgumentException e) {
205 + log.error("{}:{} wrong address format", MANAGEMENT_IP, jsonNode);
206 + return null;
207 + }
208 + }
209 +
210 + /**
190 * Returns XOS access information. 211 * Returns XOS access information.
191 * 212 *
192 * @return XOS access, or null 213 * @return XOS access, or null
......
...@@ -15,14 +15,8 @@ ...@@ -15,14 +15,8 @@
15 */ 15 */
16 package org.onosproject.cordvtn.api; 16 package org.onosproject.cordvtn.api;
17 17
18 -import org.onlab.packet.IpAddress;
19 -import org.onlab.packet.MacAddress;
20 -import org.onosproject.net.ConnectPoint;
21 -import org.onosproject.net.HostId;
22 import org.onosproject.xosclient.api.VtnServiceId; 18 import org.onosproject.xosclient.api.VtnServiceId;
23 19
24 -import java.util.Map;
25 -
26 /** 20 /**
27 * Service for provisioning overlay virtual networks on compute nodes. 21 * Service for provisioning overlay virtual networks on compute nodes.
28 */ 22 */
...@@ -31,21 +25,6 @@ public interface CordVtnService { ...@@ -31,21 +25,6 @@ public interface CordVtnService {
31 String CORDVTN_APP_ID = "org.onosproject.cordvtn"; 25 String CORDVTN_APP_ID = "org.onosproject.cordvtn";
32 26
33 /** 27 /**
34 - * Adds a new VM on a given node and connect point.
35 - *
36 - * @param node cordvtn node
37 - * @param connectPoint connect point
38 - */
39 - void addServiceVm(CordVtnNode node, ConnectPoint connectPoint);
40 -
41 - /**
42 - * Removes a VM from a given node and connect point.
43 - *
44 - * @param connectPoint connect point
45 - */
46 - void removeServiceVm(ConnectPoint connectPoint);
47 -
48 - /**
49 * Creates dependencies for a given tenant service. 28 * Creates dependencies for a given tenant service.
50 * 29 *
51 * @param tServiceId id of the service which has a dependency 30 * @param tServiceId id of the service which has a dependency
...@@ -62,14 +41,4 @@ public interface CordVtnService { ...@@ -62,14 +41,4 @@ public interface CordVtnService {
62 * @param pServiceId id of the service which provide dependency 41 * @param pServiceId id of the service which provide dependency
63 */ 42 */
64 void removeServiceDependency(VtnServiceId tServiceId, VtnServiceId pServiceId); 43 void removeServiceDependency(VtnServiceId tServiceId, VtnServiceId pServiceId);
65 -
66 - /**
67 - * Updates virtual service gateways.
68 - *
69 - * @param vSgHost host id of vSG host
70 - * @param serviceVlan service vlan id
71 - * @param vSgs map of ip and mac address of vSGs running in this vSG host
72 - */
73 - void updateVirtualSubscriberGateways(HostId vSgHost, String serviceVlan,
74 - Map<IpAddress, MacAddress> vSgs);
75 } 44 }
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.cordvtn.api;
17 +
18 +import com.google.common.base.Strings;
19 +import org.onlab.packet.Ip4Address;
20 +import org.onlab.packet.MacAddress;
21 +import org.onosproject.net.DeviceId;
22 +import org.onosproject.net.Host;
23 +import org.onosproject.net.PortNumber;
24 +import org.onosproject.xosclient.api.VtnPortId;
25 +import org.onosproject.xosclient.api.VtnService;
26 +import org.onosproject.xosclient.api.VtnServiceId;
27 +
28 +import static com.google.common.base.Preconditions.checkArgument;
29 +import static com.google.common.base.Preconditions.checkNotNull;
30 +
31 +/**
32 + * Provides methods to help to handle network service instance.
33 + */
34 +public final class Instance {
35 +
36 + public static final String SERVICE_ID = "serviceId";
37 + public static final String SERVICE_TYPE = "serviceType";
38 + public static final String PORT_ID = "vtnPortId";
39 + public static final String CREATE_TIME = "createTime";
40 + public static final String NESTED_INSTANCE = "nestedInstance";
41 + public static final String TRUE = "true";
42 +
43 + private final Host host;
44 +
45 + /**
46 + * Default constructor.
47 + *
48 + * @param instance host object of this instance
49 + */
50 + private Instance(Host instance) {
51 + this.host = instance;
52 + }
53 +
54 + /**
55 + * Returns host object of this instance.
56 + *
57 + * @return host
58 + */
59 + public Host host() {
60 + return this.host;
61 + }
62 +
63 + /**
64 + * Returns new instance.
65 + *
66 + * @param host host object of this instance
67 + * @return instance
68 + */
69 + public static Instance of(Host host) {
70 + checkNotNull(host);
71 + checkArgument(!Strings.isNullOrEmpty(host.annotations().value(SERVICE_ID)));
72 + checkArgument(!Strings.isNullOrEmpty(host.annotations().value(SERVICE_TYPE)));
73 + checkArgument(!Strings.isNullOrEmpty(host.annotations().value(PORT_ID)));
74 + checkArgument(!Strings.isNullOrEmpty(host.annotations().value(CREATE_TIME)));
75 +
76 + return new Instance(host);
77 + }
78 +
79 + /**
80 + * Returns service ID of a given host.
81 + *
82 + * @return vtn service id
83 + */
84 + public VtnServiceId serviceId() {
85 + String serviceId = host.annotations().value(SERVICE_ID);
86 + return VtnServiceId.of(serviceId);
87 + }
88 +
89 + /**
90 + * Returns service type of a given host.
91 + *
92 + * @return vtn service type
93 + */
94 + public VtnService.ServiceType serviceType() {
95 + String serviceType = host.annotations().value(SERVICE_TYPE);
96 + return VtnService.ServiceType.valueOf(serviceType);
97 + }
98 +
99 + /**
100 + * Returns port ID of a given host.
101 + *
102 + * @return vtn port id
103 + */
104 + public VtnPortId portId() {
105 + String portId = host.annotations().value(PORT_ID);
106 + return VtnPortId.of(portId);
107 + }
108 +
109 + /**
110 + * Returns if the instance is nested container or not.
111 + *
112 + * @return true if it's nested container; false otherwise
113 + */
114 + public boolean isNestedInstance() {
115 + return host.annotations().value(NESTED_INSTANCE) != null;
116 + }
117 +
118 + /**
119 + * Returns MAC address of this instance.
120 + *
121 + * @return mac address
122 + */
123 + public MacAddress mac() {
124 + return host.mac();
125 + }
126 +
127 + /**
128 + * Returns IP address of this instance.
129 + *
130 + * @return ip address
131 + */
132 + public Ip4Address ipAddress() {
133 + // assume all instance has only one IP address, and only IP4 is supported now
134 + return host.ipAddresses().stream().findFirst().get().getIp4Address();
135 + }
136 +
137 + /**
138 + * Returns device ID of this host.
139 + *
140 + * @return device id
141 + */
142 + public DeviceId deviceId() {
143 + return host.location().deviceId();
144 + }
145 +
146 + /**
147 + * Returns the port number where this host is.
148 + *
149 + * @return port number
150 + */
151 + public PortNumber portNumber() {
152 + return host.location().port();
153 + }
154 +
155 + /**
156 + * Returns annotation value with a given key.
157 + *
158 + * @param annotationKey annotation key
159 + * @return annotation value
160 + */
161 + public String getAnnotation(String annotationKey) {
162 + return host.annotations().value(annotationKey);
163 + }
164 +
165 + @Override
166 + public String toString() {
167 + return host.toString();
168 + }
169 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.cordvtn.api;
17 +
18 +/**
19 + * Handles service instance detection and removal.
20 + */
21 +public interface InstanceHandler {
22 +
23 + /**
24 + * Handles newly detected instance.
25 + *
26 + * @param instance instance
27 + */
28 + void instanceDetected(Instance instance);
29 +
30 + /**
31 + * Handles removed instance.
32 + *
33 + * @param instance instance
34 + */
35 + void instanceRemoved(Instance instance);
36 +}
...@@ -18,7 +18,7 @@ package org.onosproject.cordvtn.cli; ...@@ -18,7 +18,7 @@ package org.onosproject.cordvtn.cli;
18 18
19 import org.apache.karaf.shell.commands.Command; 19 import org.apache.karaf.shell.commands.Command;
20 import org.onosproject.cli.AbstractShellCommand; 20 import org.onosproject.cli.AbstractShellCommand;
21 -import org.onosproject.cordvtn.impl.CordVtnNodeManager; 21 +import org.onosproject.cordvtn.impl.CordVtnPipeline;
22 22
23 /** 23 /**
24 * Deletes nodes from the service. 24 * Deletes nodes from the service.
...@@ -29,8 +29,8 @@ public class CordVtnFlushRules extends AbstractShellCommand { ...@@ -29,8 +29,8 @@ public class CordVtnFlushRules extends AbstractShellCommand {
29 29
30 @Override 30 @Override
31 protected void execute() { 31 protected void execute() {
32 - CordVtnNodeManager nodeManager = AbstractShellCommand.get(CordVtnNodeManager.class); 32 + CordVtnPipeline pipeline = AbstractShellCommand.get(CordVtnPipeline.class);
33 - nodeManager.flushRules(); 33 + pipeline.flushRules();
34 print("Successfully flushed"); 34 print("Successfully flushed");
35 } 35 }
36 } 36 }
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
15 */ 15 */
16 package org.onosproject.cordvtn.impl; 16 package org.onosproject.cordvtn.impl;
17 17
18 -import com.google.common.base.Strings;
19 import com.google.common.collect.Lists; 18 import com.google.common.collect.Lists;
20 import com.google.common.collect.Maps; 19 import com.google.common.collect.Maps;
21 import com.google.common.collect.Sets; 20 import com.google.common.collect.Sets;
...@@ -27,210 +26,77 @@ import org.apache.felix.scr.annotations.ReferenceCardinality; ...@@ -27,210 +26,77 @@ import org.apache.felix.scr.annotations.ReferenceCardinality;
27 import org.apache.felix.scr.annotations.Service; 26 import org.apache.felix.scr.annotations.Service;
28 import org.onlab.packet.Ethernet; 27 import org.onlab.packet.Ethernet;
29 import org.onlab.packet.Ip4Address; 28 import org.onlab.packet.Ip4Address;
30 -import org.onlab.packet.IpAddress; 29 +import org.onlab.packet.Ip4Prefix;
31 -import org.onlab.packet.MacAddress;
32 -import org.onlab.packet.VlanId;
33 -import org.onosproject.cordvtn.api.CordVtnConfig;
34 import org.onosproject.cordvtn.api.CordVtnNode; 30 import org.onosproject.cordvtn.api.CordVtnNode;
35 import org.onosproject.cordvtn.api.CordVtnService; 31 import org.onosproject.cordvtn.api.CordVtnService;
36 -import org.onosproject.core.ApplicationId; 32 +import org.onosproject.cordvtn.api.Instance;
37 -import org.onosproject.core.CoreService; 33 +import org.onosproject.core.DefaultGroupId;
38 -import org.onosproject.dhcp.DhcpService; 34 +import org.onosproject.core.GroupId;
39 -import org.onosproject.mastership.MastershipService; 35 +import org.onosproject.net.DeviceId;
40 -import org.onosproject.net.ConnectPoint;
41 -import org.onosproject.net.DefaultAnnotations;
42 import org.onosproject.net.Host; 36 import org.onosproject.net.Host;
43 -import org.onosproject.net.HostId; 37 +import org.onosproject.net.PortNumber;
44 -import org.onosproject.net.HostLocation; 38 +import org.onosproject.net.flow.DefaultFlowRule;
45 -import org.onosproject.net.Port; 39 +import org.onosproject.net.flow.DefaultTrafficSelector;
46 -import org.onosproject.net.config.ConfigFactory; 40 +import org.onosproject.net.flow.DefaultTrafficTreatment;
47 -import org.onosproject.net.config.NetworkConfigEvent; 41 +import org.onosproject.net.flow.FlowRule;
48 -import org.onosproject.net.config.NetworkConfigListener; 42 +import org.onosproject.net.flow.TrafficSelector;
49 -import org.onosproject.net.config.NetworkConfigRegistry; 43 +import org.onosproject.net.flow.TrafficTreatment;
50 -import org.onosproject.net.config.basics.SubjectFactories; 44 +import org.onosproject.net.flow.instructions.ExtensionTreatment;
51 -import org.onosproject.net.device.DeviceService; 45 +import org.onosproject.net.group.DefaultGroupDescription;
52 -import org.onosproject.net.flow.FlowRuleService; 46 +import org.onosproject.net.group.DefaultGroupKey;
47 +import org.onosproject.net.group.Group;
48 +import org.onosproject.net.group.GroupBucket;
49 +import org.onosproject.net.group.GroupBuckets;
50 +import org.onosproject.net.group.GroupDescription;
51 +import org.onosproject.net.group.GroupKey;
53 import org.onosproject.net.group.GroupService; 52 import org.onosproject.net.group.GroupService;
54 -import org.onosproject.net.host.DefaultHostDescription;
55 -import org.onosproject.net.host.HostDescription;
56 import org.onosproject.net.host.HostEvent; 53 import org.onosproject.net.host.HostEvent;
57 import org.onosproject.net.host.HostListener; 54 import org.onosproject.net.host.HostListener;
58 -import org.onosproject.net.host.HostProvider;
59 -import org.onosproject.net.host.HostProviderRegistry;
60 -import org.onosproject.net.host.HostProviderService;
61 -import org.onosproject.net.host.HostService;
62 -import org.onosproject.net.packet.PacketContext;
63 -import org.onosproject.net.packet.PacketProcessor;
64 -import org.onosproject.net.packet.PacketService;
65 -import org.onosproject.net.provider.AbstractProvider;
66 -import org.onosproject.net.provider.ProviderId;
67 -import org.onosproject.xosclient.api.OpenStackAccess;
68 -import org.onosproject.xosclient.api.VtnPort;
69 -import org.onosproject.xosclient.api.VtnPortApi;
70 -import org.onosproject.xosclient.api.VtnPortId;
71 import org.onosproject.xosclient.api.VtnService; 55 import org.onosproject.xosclient.api.VtnService;
72 -import org.onosproject.xosclient.api.VtnServiceApi;
73 import org.onosproject.xosclient.api.VtnServiceId; 56 import org.onosproject.xosclient.api.VtnServiceId;
74 -import org.onosproject.xosclient.api.XosAccess;
75 -import org.onosproject.xosclient.api.XosClientService;
76 import org.slf4j.Logger; 57 import org.slf4j.Logger;
77 58
78 import java.util.List; 59 import java.util.List;
79 import java.util.Map; 60 import java.util.Map;
80 import java.util.Objects; 61 import java.util.Objects;
81 import java.util.Set; 62 import java.util.Set;
82 -import java.util.concurrent.ExecutorService;
83 import java.util.stream.Collectors; 63 import java.util.stream.Collectors;
84 -import java.util.stream.StreamSupport;
85 64
86 -import static com.google.common.base.Preconditions.checkNotNull;
87 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; 65 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
88 import static org.onlab.util.Tools.groupedThreads; 66 import static org.onlab.util.Tools.groupedThreads;
67 +import static org.onosproject.cordvtn.impl.CordVtnPipeline.*;
68 +import static org.onosproject.net.group.DefaultGroupBucket.createSelectGroupBucket;
89 import static org.slf4j.LoggerFactory.getLogger; 69 import static org.slf4j.LoggerFactory.getLogger;
90 70
91 /** 71 /**
92 - * Provisions virtual tenant networks with service chaining capability 72 + * Provisions service dependency capabilities between network services.
93 - * in OpenStack environment.
94 */ 73 */
95 @Component(immediate = true) 74 @Component(immediate = true)
96 @Service 75 @Service
97 -public class CordVtn extends AbstractProvider implements CordVtnService, HostProvider { 76 +public class CordVtn extends CordVtnInstanceHandler implements CordVtnService {
98 77
99 protected final Logger log = getLogger(getClass()); 78 protected final Logger log = getLogger(getClass());
100 79
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 - protected CoreService coreService;
103 -
104 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 - protected NetworkConfigRegistry configRegistry;
106 -
107 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 - protected HostProviderRegistry hostProviderRegistry;
109 -
110 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 - protected DeviceService deviceService;
112 -
113 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 - protected HostService hostService;
115 -
116 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 - protected FlowRuleService flowRuleService;
118 -
119 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
120 - protected PacketService packetService;
121 -
122 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 - protected MastershipService mastershipService;
124 -
125 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 protected GroupService groupService; 81 protected GroupService groupService;
127 82
128 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129 - protected DhcpService dhcpService;
130 -
131 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
132 - protected XosClientService xosClient;
133 -
134 - private final ConfigFactory configFactory =
135 - new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, CordVtnConfig.class, "cordvtn") {
136 - @Override
137 - public CordVtnConfig createConfig() {
138 - return new CordVtnConfig();
139 - }
140 - };
141 -
142 - private static final String XOS_ACCESS_ERROR = "XOS access is not configured";
143 - private static final String OPENSTACK_ACCESS_ERROR = "OpenStack access is not configured";
144 -
145 - private static final String DEFAULT_TUNNEL = "vxlan";
146 - private static final String SERVICE_ID = "serviceId";
147 - private static final String PORT_ID = "vtnPortId";
148 - private static final String DATA_PLANE_IP = "dataPlaneIp";
149 - private static final String DATA_PLANE_INTF = "dataPlaneIntf";
150 - private static final String S_TAG = "stag";
151 - private static final String VSG_HOST_ID = "vsgHostId";
152 - private static final String CREATE_TIME = "createTime";
153 -
154 - private static final Ip4Address DEFAULT_DNS = Ip4Address.valueOf("8.8.8.8");
155 -
156 - private final ExecutorService eventExecutor =
157 - newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn", "event-handler"));
158 -
159 - private final PacketProcessor packetProcessor = new InternalPacketProcessor();
160 - private final HostListener hostListener = new InternalHostListener();
161 - private final NetworkConfigListener configListener = new InternalConfigListener();
162 -
163 - private ApplicationId appId;
164 - private HostProviderService hostProvider;
165 - private CordVtnRuleInstaller ruleInstaller;
166 - private CordVtnArpProxy arpProxy;
167 -
168 - private volatile XosAccess xosAccess = null;
169 - private volatile OpenStackAccess osAccess = null;
170 - private volatile MacAddress privateGatewayMac = MacAddress.NONE;
171 -
172 - /**
173 - * Creates an cordvtn host location provider.
174 - */
175 - public CordVtn() {
176 - super(new ProviderId("host", CORDVTN_APP_ID));
177 - }
178 -
179 @Activate 83 @Activate
180 protected void activate() { 84 protected void activate() {
181 - appId = coreService.registerApplication(CordVtnService.CORDVTN_APP_ID); 85 + eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn", "event-handler"));
182 - ruleInstaller = new CordVtnRuleInstaller(appId, flowRuleService, 86 + hostListener = new InternalHostListener();
183 - deviceService, 87 + super.activate();
184 - groupService,
185 - hostService,
186 - configRegistry,
187 - DEFAULT_TUNNEL);
188 -
189 - arpProxy = new CordVtnArpProxy(appId, packetService, hostService);
190 - packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
191 - arpProxy.requestPacket();
192 -
193 - hostService.addListener(hostListener);
194 - hostProvider = hostProviderRegistry.register(this);
195 -
196 - configRegistry.registerConfigFactory(configFactory);
197 - configRegistry.addListener(configListener);
198 -
199 - log.info("Started");
200 } 88 }
201 89
202 @Deactivate 90 @Deactivate
203 protected void deactivate() { 91 protected void deactivate() {
204 - hostProviderRegistry.unregister(this); 92 + super.deactivate();
205 - hostService.removeListener(hostListener);
206 -
207 - packetService.removeProcessor(packetProcessor);
208 -
209 - configRegistry.unregisterConfigFactory(configFactory);
210 - configRegistry.removeListener(configListener);
211 -
212 - eventExecutor.shutdown();
213 - log.info("Stopped");
214 - }
215 -
216 - @Override
217 - public void triggerProbe(Host host) {
218 - /*
219 - * Note: In CORD deployment, we assume that all hosts are configured.
220 - * Therefore no probe is required.
221 - */
222 } 93 }
223 94
224 @Override 95 @Override
225 public void createServiceDependency(VtnServiceId tServiceId, VtnServiceId pServiceId, 96 public void createServiceDependency(VtnServiceId tServiceId, VtnServiceId pServiceId,
226 boolean isBidirectional) { 97 boolean isBidirectional) {
227 - checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR); 98 + VtnService tService = getVtnService(tServiceId);
228 - checkNotNull(xosAccess, XOS_ACCESS_ERROR); 99 + VtnService pService = getVtnService(pServiceId);
229 -
230 - // TODO remove openstack access when XOS provides all information
231 - VtnServiceApi serviceApi = xosClient.getClient(xosAccess).vtnService();
232 - VtnService tService = serviceApi.service(tServiceId, osAccess);
233 - VtnService pService = serviceApi.service(pServiceId, osAccess);
234 100
235 if (tService == null || pService == null) { 101 if (tService == null || pService == null) {
236 log.error("Failed to create dependency between {} and {}", 102 log.error("Failed to create dependency between {} and {}",
...@@ -239,18 +105,13 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro ...@@ -239,18 +105,13 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
239 } 105 }
240 106
241 log.info("Created dependency between {} and {}", tService.name(), pService.name()); 107 log.info("Created dependency between {} and {}", tService.name(), pService.name());
242 - ruleInstaller.populateServiceDependencyRules(tService, pService, isBidirectional, true); 108 + serviceDependencyRules(tService, pService, isBidirectional, true);
243 } 109 }
244 110
245 @Override 111 @Override
246 public void removeServiceDependency(VtnServiceId tServiceId, VtnServiceId pServiceId) { 112 public void removeServiceDependency(VtnServiceId tServiceId, VtnServiceId pServiceId) {
247 - checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR); 113 + VtnService tService = getVtnService(tServiceId);
248 - checkNotNull(xosAccess, XOS_ACCESS_ERROR); 114 + VtnService pService = getVtnService(pServiceId);
249 -
250 - // TODO remove openstack access when XOS provides all information
251 - VtnServiceApi serviceApi = xosClient.getClient(xosAccess).vtnService();
252 - VtnService tService = serviceApi.service(tServiceId, osAccess);
253 - VtnService pService = serviceApi.service(pServiceId, osAccess);
254 115
255 if (tService == null || pService == null) { 116 if (tService == null || pService == null) {
256 log.error("Failed to remove dependency between {} and {}", 117 log.error("Failed to remove dependency between {} and {}",
...@@ -259,427 +120,267 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro ...@@ -259,427 +120,267 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
259 } 120 }
260 121
261 log.info("Removed dependency between {} and {}", tService.name(), pService.name()); 122 log.info("Removed dependency between {} and {}", tService.name(), pService.name());
262 - ruleInstaller.populateServiceDependencyRules(tService, pService, true, false); 123 + serviceDependencyRules(tService, pService, true, false);
263 } 124 }
264 125
265 @Override 126 @Override
266 - public void addServiceVm(CordVtnNode node, ConnectPoint connectPoint) { 127 + public void instanceDetected(Instance instance) {
267 - checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR); 128 + VtnService service = getVtnService(instance.serviceId());
268 - checkNotNull(xosAccess, XOS_ACCESS_ERROR); 129 + if (service == null) {
269 -
270 - Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
271 - String portName = port.annotations().value("portName");
272 -
273 - // TODO remove openstack access when XOS provides all information
274 - VtnPortApi portApi = xosClient.getClient(xosAccess).vtnPort();
275 - VtnPort vtnPort = portApi.vtnPort(portName, osAccess);
276 - if (vtnPort == null) {
277 - log.warn("Failed to get port information of {}", portName);
278 return; 130 return;
279 } 131 }
280 132
281 - // Added CREATE_TIME intentionally to trigger HOST_UPDATED event for the 133 + // TODO get bidirectional information from XOS once XOS supports
282 - // existing instances. 134 + service.tenantServices().stream().forEach(
283 - DefaultAnnotations.Builder annotations = DefaultAnnotations.builder() 135 + tServiceId -> createServiceDependency(tServiceId, service.id(), true));
284 - .set(SERVICE_ID, vtnPort.serviceId().id()) 136 + service.providerServices().stream().forEach(
285 - .set(PORT_ID, vtnPort.id().id()) 137 + pServiceId -> createServiceDependency(service.id(), pServiceId, true));
286 - .set(DATA_PLANE_IP, node.dpIp().ip().toString())
287 - .set(DATA_PLANE_INTF, node.dpIntf())
288 - .set(CREATE_TIME, String.valueOf(System.currentTimeMillis()));
289 -
290 - // TODO address service specific task in a separate package
291 - String serviceVlan = getServiceVlan(vtnPort);
292 - if (!Strings.isNullOrEmpty(serviceVlan)) {
293 - annotations.set(S_TAG, serviceVlan);
294 - }
295 -
296 - HostDescription hostDesc = new DefaultHostDescription(
297 - vtnPort.mac(),
298 - VlanId.NONE,
299 - new HostLocation(connectPoint, System.currentTimeMillis()),
300 - Sets.newHashSet(vtnPort.ip()),
301 - annotations.build());
302 138
303 - HostId hostId = HostId.hostId(vtnPort.mac()); 139 + updateProviderServiceInstances(service);
304 - hostProvider.hostDetected(hostId, hostDesc, false);
305 } 140 }
306 141
307 @Override 142 @Override
308 - public void removeServiceVm(ConnectPoint connectPoint) { 143 + public void instanceRemoved(Instance instance) {
309 - hostService.getConnectedHosts(connectPoint) 144 + VtnService service = getVtnService(instance.serviceId());
310 - .stream() 145 + if (service == null) {
311 - .forEach(host -> hostProvider.hostVanished(host.id()));
312 - }
313 -
314 - @Override
315 - // TODO address service specific task in a separate package
316 - public void updateVirtualSubscriberGateways(HostId vSgHostId, String serviceVlan,
317 - Map<IpAddress, MacAddress> vSgs) {
318 - Host vSgHost = hostService.getHost(vSgHostId);
319 - if (vSgHost == null || !vSgHost.annotations().value(S_TAG).equals(serviceVlan)) {
320 - log.debug("Invalid vSG updates for {}", serviceVlan);
321 return; 146 return;
322 } 147 }
323 148
324 - log.info("Updates vSGs in {} with {}", vSgHost.id(), vSgs.toString()); 149 + if (!service.providerServices().isEmpty()) {
325 - vSgs.entrySet().stream() 150 + removeInstanceFromTenantService(instance, service);
326 - .filter(entry -> hostService.getHostsByMac(entry.getValue()).isEmpty())
327 - .forEach(entry -> addVirtualSubscriberGateway(
328 - vSgHost,
329 - entry.getKey(),
330 - entry.getValue(),
331 - serviceVlan));
332 -
333 - hostService.getConnectedHosts(vSgHost.location()).stream()
334 - .filter(host -> !host.mac().equals(vSgHost.mac()))
335 - .filter(host -> !vSgs.values().contains(host.mac()))
336 - .forEach(host -> {
337 - log.info("Removed vSG {}", host.toString());
338 - hostProvider.hostVanished(host.id());
339 - });
340 - }
341 -
342 - /**
343 - * Adds virtual subscriber gateway to the system.
344 - *
345 - * @param vSgHost host virtual machine of this vSG
346 - * @param vSgIp vSG ip address
347 - * @param vSgMac vSG mac address
348 - * @param serviceVlan service vlan
349 - */
350 - // TODO address service specific task in a separate package
351 - private void addVirtualSubscriberGateway(Host vSgHost, IpAddress vSgIp, MacAddress vSgMac,
352 - String serviceVlan) {
353 - log.info("vSG with IP({}) MAC({}) added", vSgIp.toString(), vSgMac.toString());
354 -
355 - HostId hostId = HostId.hostId(vSgMac);
356 - DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
357 - .set(S_TAG, serviceVlan)
358 - .set(VSG_HOST_ID, vSgHost.id().toString())
359 - .set(CREATE_TIME, String.valueOf(System.currentTimeMillis()));
360 -
361 - HostDescription hostDesc = new DefaultHostDescription(
362 - vSgMac,
363 - VlanId.NONE,
364 - vSgHost.location(),
365 - Sets.newHashSet(vSgIp),
366 - annotations.build());
367 -
368 - hostProvider.hostDetected(hostId, hostDesc, false);
369 - }
370 -
371 - /**
372 - * Returns public ip addresses of vSGs running inside a give vSG host.
373 - *
374 - * @param vSgHost vSG host
375 - * @return map of ip and mac address, or empty map
376 - */
377 - // TODO address service specific task in a separate package
378 - private Map<IpAddress, MacAddress> getSubscriberGateways(Host vSgHost) {
379 - checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
380 - checkNotNull(xosAccess, XOS_ACCESS_ERROR);
381 -
382 - String vtnPortId = vSgHost.annotations().value(PORT_ID);
383 - String sTag = vSgHost.annotations().value(S_TAG);
384 -
385 - if (Strings.isNullOrEmpty(vtnPortId) || Strings.isNullOrEmpty(sTag)) {
386 - log.warn("PORT_ID and S_TAG is not set, ignore {}", vSgHost);
387 - return Maps.newHashMap();
388 } 151 }
389 - 152 + if (!service.tenantServices().isEmpty()) {
390 - // TODO remove openstack access when XOS provides all information 153 + updateProviderServiceInstances(service);
391 - VtnPortApi portApi = xosClient.getClient(xosAccess).vtnPort();
392 - VtnPort vtnPort = portApi.vtnPort(VtnPortId.of(vtnPortId), osAccess);
393 - if (vtnPort == null) {
394 - log.warn("Failed to get port information of {}", vSgHost);
395 - return Maps.newHashMap();
396 } 154 }
397 -
398 - if (!sTag.equals(getServiceVlan(vtnPort))) {
399 - log.error("Host({}) s-tag does not match with VTN port s-tag", vSgHost);
400 - return Maps.newHashMap();
401 - }
402 - return vtnPort.addressPairs();
403 } 155 }
404 156
405 - /** 157 + private void updateProviderServiceInstances(VtnService service) {
406 - * Returns s-tag from a given VTN port. 158 + GroupKey groupKey = getGroupKey(service.id());
407 - *
408 - * @param vtnPort vtn port
409 - * @return s-tag string
410 - */
411 - // TODO address service specific task in a separate package
412 - private String getServiceVlan(VtnPort vtnPort) {
413 - checkNotNull(vtnPort);
414 -
415 - String portName = vtnPort.name();
416 - if (portName != null && portName.startsWith(S_TAG)) {
417 - return portName.split("-")[1];
418 - } else {
419 - return null;
420 - }
421 - }
422 159
423 - /** 160 + Set<DeviceId> devices = nodeManager.completeNodes().stream()
424 - * Returns instances with a given network service. 161 + .map(CordVtnNode::intBrId)
425 - *
426 - * @param serviceId service id
427 - * @return set of hosts
428 - */
429 - private Set<Host> getInstances(VtnServiceId serviceId) {
430 - return StreamSupport.stream(hostService.getHosts().spliterator(), false)
431 - .filter(host -> Objects.equals(
432 - serviceId.id(),
433 - host.annotations().value(SERVICE_ID)))
434 .collect(Collectors.toSet()); 162 .collect(Collectors.toSet());
435 - }
436 163
437 - /** 164 + for (DeviceId deviceId : devices) {
438 - * Registers static DHCP lease for a given host. 165 + Group group = groupService.getGroup(deviceId, groupKey);
439 - * 166 + if (group == null) {
440 - * @param host host 167 + log.trace("No group exists for service {} in {}", service.id(), deviceId);
441 - * @param service cord service 168 + continue;
442 - */ 169 + }
443 - private void registerDhcpLease(Host host, VtnService service) {
444 - List<Ip4Address> options = Lists.newArrayList();
445 - options.add(Ip4Address.makeMaskPrefix(service.subnet().prefixLength()));
446 - options.add(service.serviceIp().getIp4Address());
447 - options.add(service.serviceIp().getIp4Address());
448 - options.add(DEFAULT_DNS);
449 -
450 - log.debug("Set static DHCP mapping for {}", host.mac());
451 - dhcpService.setStaticMapping(host.mac(),
452 - host.ipAddresses().stream().findFirst().get().getIp4Address(),
453 - true,
454 - options);
455 - }
456 170
457 - /** 171 + List<GroupBucket> oldBuckets = group.buckets().buckets();
458 - * Handles VM detected situation. 172 + List<GroupBucket> newBuckets = getServiceGroupBuckets(
459 - * 173 + deviceId, service.vni(), getInstances(service.id())).buckets();
460 - * @param host host
461 - */
462 - private void serviceVmAdded(Host host) {
463 - checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
464 - checkNotNull(xosAccess, XOS_ACCESS_ERROR);
465 -
466 - // TODO address service specific task in a separate package
467 - String serviceVlan = host.annotations().value(S_TAG);
468 - if (serviceVlan != null) {
469 - virtualSubscriberGatewayAdded(host, serviceVlan);
470 - }
471 174
472 - String serviceId = host.annotations().value(SERVICE_ID); 175 + if (oldBuckets.equals(newBuckets)) {
473 - if (Strings.isNullOrEmpty(serviceId)) { 176 + continue;
474 - // ignore this host, it is not a service instance 177 + }
475 - return;
476 - }
477 178
478 - log.info("Instance is detected {}", host); 179 + List<GroupBucket> bucketsToRemove = Lists.newArrayList(oldBuckets);
180 + bucketsToRemove.removeAll(newBuckets);
181 + if (!bucketsToRemove.isEmpty()) {
182 + groupService.removeBucketsFromGroup(
183 + deviceId,
184 + groupKey,
185 + new GroupBuckets(bucketsToRemove),
186 + groupKey, appId);
187 + }
479 188
480 - // TODO remove openstack access when XOS provides all information 189 + List<GroupBucket> bucketsToAdd = Lists.newArrayList(newBuckets);
481 - VtnServiceApi serviceApi = xosClient.getClient(xosAccess).vtnService(); 190 + bucketsToAdd.removeAll(oldBuckets);
482 - VtnService service = serviceApi.service(VtnServiceId.of(serviceId), osAccess); 191 + if (!bucketsToAdd.isEmpty()) {
483 - if (service == null) { 192 + groupService.addBucketsToGroup(
484 - log.warn("Failed to get VtnService for {}", serviceId); 193 + deviceId,
485 - return; 194 + groupKey,
195 + new GroupBuckets(bucketsToAdd),
196 + groupKey, appId);
197 + }
486 } 198 }
199 + }
487 200
488 - switch (service.networkType()) { 201 + private void removeInstanceFromTenantService(Instance instance, VtnService service) {
489 - case MANAGEMENT: 202 + service.providerServices().stream().forEach(pServiceId -> {
490 - ruleInstaller.populateManagementNetworkRules(host, service); 203 + Map<DeviceId, Set<PortNumber>> inPorts = Maps.newHashMap();
491 - break; 204 + Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
492 - case PRIVATE:
493 - arpProxy.addGateway(service.serviceIp(), privateGatewayMac);
494 - case PUBLIC:
495 - default:
496 - // TODO get bidirectional information from XOS once XOS supports
497 - service.tenantServices().stream().forEach(
498 - tServiceId -> createServiceDependency(tServiceId, service.id(), true));
499 - service.providerServices().stream().forEach(
500 - pServiceId -> createServiceDependency(service.id(), pServiceId, true));
501 -
502 - ruleInstaller.updateProviderServiceGroup(service);
503 - // sends gratuitous ARP here for the case of adding existing VMs
504 - // when ONOS or cordvtn app is restarted
505 - arpProxy.sendGratuitousArpForGateway(service.serviceIp(), Sets.newHashSet(host));
506 - break;
507 - }
508 205
509 - registerDhcpLease(host, service); 206 + inPorts.put(instance.deviceId(), Sets.newHashSet(instance.portNumber()));
510 - ruleInstaller.populateBasicConnectionRules(host, service, true); 207 + outGroups.put(instance.deviceId(), getGroupId(pServiceId, instance.deviceId()));
511 - }
512 208
513 - /** 209 + inServiceRule(inPorts, outGroups, false);
514 - * Handles VM removed situation. 210 + });
515 - * 211 + }
516 - * @param host host
517 - */
518 - private void serviceVmRemoved(Host host) {
519 - checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
520 - checkNotNull(xosAccess, XOS_ACCESS_ERROR);
521 -
522 - // TODO address service specific task in a separate package
523 - if (host.annotations().value(S_TAG) != null) {
524 - virtualSubscriberGatewayRemoved(host);
525 - }
526 212
527 - String serviceId = host.annotations().value(SERVICE_ID); 213 + private void serviceDependencyRules(VtnService tService, VtnService pService,
528 - if (Strings.isNullOrEmpty(serviceId)) { 214 + boolean isBidirectional, boolean install) {
529 - // ignore this host, it is not a service instance 215 + Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
530 - return; 216 + Map<DeviceId, Set<PortNumber>> inPorts = Maps.newHashMap();
531 - } 217 +
218 + nodeManager.completeNodes().stream().forEach(node -> {
219 + DeviceId deviceId = node.intBrId();
220 + GroupId groupId = createServiceGroup(deviceId, pService);
221 + outGroups.put(deviceId, groupId);
222 +
223 + Set<PortNumber> tServiceInstances = getInstances(tService.id())
224 + .stream()
225 + .filter(instance -> instance.deviceId().equals(deviceId))
226 + .map(Instance::portNumber)
227 + .collect(Collectors.toSet());
228 + inPorts.put(deviceId, tServiceInstances);
229 + });
532 230
533 - log.info("Instance is vanished {}", host); 231 + Ip4Prefix srcRange = tService.subnet().getIp4Prefix();
232 + Ip4Prefix dstRange = pService.subnet().getIp4Prefix();
534 233
535 - // TODO remove openstack access when XOS provides all information 234 + indirectAccessRule(srcRange, pService.serviceIp().getIp4Address(), outGroups, install);
536 - VtnServiceApi vtnServiceApi = xosClient.getClient(xosAccess).vtnService(); 235 + directAccessRule(srcRange, dstRange, install);
537 - VtnService service = vtnServiceApi.service(VtnServiceId.of(serviceId), osAccess); 236 + if (isBidirectional) {
538 - if (service == null) { 237 + directAccessRule(dstRange, srcRange, install);
539 - log.warn("Failed to get VtnService for {}", serviceId);
540 - return;
541 } 238 }
239 + inServiceRule(inPorts, outGroups, install);
240 + }
542 241
543 - // TODO need to consider the case that the service is removed also 242 + private void indirectAccessRule(Ip4Prefix srcRange, Ip4Address serviceIp,
544 - switch (service.networkType()) { 243 + Map<DeviceId, GroupId> outGroups, boolean install) {
545 - case MANAGEMENT: 244 + TrafficSelector selector = DefaultTrafficSelector.builder()
546 - break; 245 + .matchEthType(Ethernet.TYPE_IPV4)
547 - case PRIVATE: 246 + .matchIPSrc(srcRange)
548 - if (getInstances(VtnServiceId.of(serviceId)).isEmpty()) { 247 + .matchIPDst(serviceIp.toIpPrefix())
549 - arpProxy.removeGateway(service.serviceIp()); 248 + .build();
550 - } 249 +
551 - case PUBLIC: 250 + for (Map.Entry<DeviceId, GroupId> outGroup : outGroups.entrySet()) {
552 - default: 251 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
553 - if (!service.tenantServices().isEmpty()) { 252 + .group(outGroup.getValue())
554 - ruleInstaller.updateProviderServiceGroup(service); 253 + .build();
555 - } 254 +
556 - if (!service.providerServices().isEmpty()) { 255 + FlowRule flowRule = DefaultFlowRule.builder()
557 - ruleInstaller.updateTenantServiceVm(host, service); 256 + .fromApp(appId)
558 - } 257 + .withSelector(selector)
559 - break; 258 + .withTreatment(treatment)
259 + .withPriority(PRIORITY_HIGH)
260 + .forDevice(outGroup.getKey())
261 + .forTable(TABLE_ACCESS_TYPE)
262 + .makePermanent()
263 + .build();
264 +
265 + pipeline.processFlowRule(install, flowRule);
560 } 266 }
267 + }
561 268
562 - dhcpService.removeStaticMapping(host.mac()); 269 + private void directAccessRule(Ip4Prefix srcRange, Ip4Prefix dstRange, boolean install) {
563 - ruleInstaller.populateBasicConnectionRules(host, service, false); 270 + TrafficSelector selector = DefaultTrafficSelector.builder()
271 + .matchEthType(Ethernet.TYPE_IPV4)
272 + .matchIPSrc(srcRange)
273 + .matchIPDst(dstRange)
274 + .build();
275 +
276 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
277 + .transition(TABLE_DST_IP)
278 + .build();
279 +
280 + nodeManager.completeNodes().stream().forEach(node -> {
281 + DeviceId deviceId = node.intBrId();
282 + FlowRule flowRuleDirect = DefaultFlowRule.builder()
283 + .fromApp(appId)
284 + .withSelector(selector)
285 + .withTreatment(treatment)
286 + .withPriority(PRIORITY_DEFAULT)
287 + .forDevice(deviceId)
288 + .forTable(TABLE_ACCESS_TYPE)
289 + .makePermanent()
290 + .build();
291 +
292 + pipeline.processFlowRule(install, flowRuleDirect);
293 + });
564 } 294 }
565 295
296 + private void inServiceRule(Map<DeviceId, Set<PortNumber>> inPorts,
297 + Map<DeviceId, GroupId> outGroups, boolean install) {
298 + for (Map.Entry<DeviceId, Set<PortNumber>> entry : inPorts.entrySet()) {
299 + Set<PortNumber> ports = entry.getValue();
300 + DeviceId deviceId = entry.getKey();
566 301
567 - /** 302 + GroupId groupId = outGroups.get(deviceId);
568 - * Handles virtual subscriber gateway VM or container. 303 + if (groupId == null) {
569 - * 304 + continue;
570 - * @param host new host with stag, it can be vsg VM or vsg
571 - * @param serviceVlan service vlan
572 - */
573 - // TODO address service specific task in a separate package
574 - private void virtualSubscriberGatewayAdded(Host host, String serviceVlan) {
575 - Map<IpAddress, MacAddress> vSgs;
576 - Host vSgHost;
577 -
578 - String vSgHostId = host.annotations().value(VSG_HOST_ID);
579 - if (vSgHostId == null) {
580 - log.info("vSG VM detected {}", host.id());
581 -
582 - vSgHost = host;
583 - vSgs = getSubscriberGateways(vSgHost);
584 - vSgs.entrySet().stream().forEach(entry -> addVirtualSubscriberGateway(
585 - vSgHost,
586 - entry.getKey(),
587 - entry.getValue(),
588 - serviceVlan));
589 - } else {
590 - vSgHost = hostService.getHost(HostId.hostId(vSgHostId));
591 - if (vSgHost == null) {
592 - return;
593 } 305 }
594 306
595 - log.info("vSG detected {}", host.id()); 307 + ports.stream().forEach(port -> {
596 - vSgs = getSubscriberGateways(vSgHost); 308 + TrafficSelector selector = DefaultTrafficSelector.builder()
309 + .matchInPort(port)
310 + .build();
311 +
312 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
313 + .group(groupId)
314 + .build();
315 +
316 + FlowRule flowRule = DefaultFlowRule.builder()
317 + .fromApp(appId)
318 + .withSelector(selector)
319 + .withTreatment(treatment)
320 + .withPriority(PRIORITY_DEFAULT)
321 + .forDevice(deviceId)
322 + .forTable(TABLE_IN_SERVICE)
323 + .makePermanent()
324 + .build();
325 +
326 + pipeline.processFlowRule(install, flowRule);
327 + });
597 } 328 }
598 -
599 - ruleInstaller.populateSubscriberGatewayRules(vSgHost, vSgs.keySet());
600 } 329 }
601 330
602 - /** 331 + private GroupId getGroupId(VtnServiceId serviceId, DeviceId deviceId) {
603 - * Handles virtual subscriber gateway removed. 332 + return new DefaultGroupId(Objects.hash(serviceId, deviceId));
604 - *
605 - * @param vSg vsg host to remove
606 - */
607 - // TODO address service specific task in a separate package
608 - private void virtualSubscriberGatewayRemoved(Host vSg) {
609 - String vSgHostId = vSg.annotations().value(VSG_HOST_ID);
610 - if (vSgHostId == null) {
611 - return;
612 - }
613 -
614 - Host vSgHost = hostService.getHost(HostId.hostId(vSgHostId));
615 - if (vSgHost == null) {
616 - return;
617 - }
618 -
619 - log.info("vSG removed {}", vSg.id());
620 - Map<IpAddress, MacAddress> vSgs = getSubscriberGateways(vSgHost);
621 - ruleInstaller.populateSubscriberGatewayRules(vSgHost, vSgs.keySet());
622 } 333 }
623 334
624 - /** 335 + private GroupKey getGroupKey(VtnServiceId serviceId) {
625 - * Sets service network gateway MAC address and sends out gratuitous ARP to all 336 + return new DefaultGroupKey(serviceId.id().getBytes());
626 - * VMs to update the gateway MAC address.
627 - *
628 - * @param newMac mac address to update
629 - */
630 - private void setPrivateGatewayMac(MacAddress newMac) {
631 - checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
632 - checkNotNull(xosAccess, XOS_ACCESS_ERROR);
633 -
634 - if (newMac == null || newMac.equals(privateGatewayMac)) {
635 - // no updates, do nothing
636 - return;
637 - }
638 -
639 - privateGatewayMac = newMac;
640 - log.debug("Set service gateway MAC address to {}", privateGatewayMac.toString());
641 -
642 - VtnServiceApi vtnServiceApi = xosClient.getClient(xosAccess).vtnService();
643 - vtnServiceApi.services().stream().forEach(serviceId -> {
644 - VtnService service = vtnServiceApi.service(serviceId, osAccess);
645 - if (service != null) {
646 - arpProxy.addGateway(service.serviceIp(), privateGatewayMac);
647 - arpProxy.sendGratuitousArpForGateway(service.serviceIp(), getInstances(serviceId));
648 - }
649 - });
650 } 337 }
651 338
652 - /** 339 + private GroupId createServiceGroup(DeviceId deviceId, VtnService service) {
653 - * Sets public gateway MAC address. 340 + GroupKey groupKey = getGroupKey(service.id());
654 - * 341 + Group group = groupService.getGroup(deviceId, groupKey);
655 - * @param publicGateways gateway ip and mac address pairs 342 + GroupId groupId = getGroupId(service.id(), deviceId);
656 - */
657 - private void setPublicGatewayMac(Map<IpAddress, MacAddress> publicGateways) {
658 - publicGateways.entrySet()
659 - .stream()
660 - .forEach(entry -> {
661 - arpProxy.addGateway(entry.getKey(), entry.getValue());
662 - log.debug("Added public gateway IP {}, MAC {}",
663 - entry.getKey().toString(), entry.getValue().toString());
664 - });
665 - // TODO notice gateway MAC change to VMs holds this gateway IP
666 - }
667 343
668 - /** 344 + if (group != null) {
669 - * Updates configurations. 345 + log.debug("Group {} is already exist in {}", service.id(), deviceId);
670 - */ 346 + return groupId;
671 - private void readConfiguration() {
672 - CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
673 - if (config == null) {
674 - log.debug("No configuration found");
675 - return;
676 } 347 }
677 348
678 - xosAccess = config.xosAccess(); 349 + GroupBuckets buckets = getServiceGroupBuckets(
679 - osAccess = config.openstackAccess(); 350 + deviceId, service.vni(), getInstances(service.id()));
351 + GroupDescription groupDescription = new DefaultGroupDescription(
352 + deviceId,
353 + GroupDescription.Type.SELECT,
354 + buckets,
355 + groupKey,
356 + groupId.id(),
357 + appId);
358 +
359 + groupService.addGroup(groupDescription);
360 + return groupId;
361 + }
680 362
681 - setPrivateGatewayMac(config.privateGatewayMac()); 363 + private GroupBuckets getServiceGroupBuckets(DeviceId deviceId, long tunnelId,
682 - setPublicGatewayMac(config.publicGateways()); 364 + Set<Instance> instances) {
365 + List<GroupBucket> buckets = Lists.newArrayList();
366 + instances.stream().forEach(instance -> {
367 + Ip4Address tunnelIp = nodeManager.dpIp(instance.deviceId()).getIp4Address();
368 + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
369 +
370 + if (deviceId.equals(instance.deviceId())) {
371 + tBuilder.setEthDst(instance.mac())
372 + .setOutput(instance.portNumber());
373 + } else {
374 + ExtensionTreatment tunnelDst =
375 + pipeline.tunnelDstTreatment(deviceId, tunnelIp);
376 + tBuilder.setEthDst(instance.mac())
377 + .extension(tunnelDst, deviceId)
378 + .setTunnelId(tunnelId)
379 + .setOutput(nodeManager.tunnelPort(instance.deviceId()));
380 + }
381 + buckets.add(createSelectGroupBucket(tBuilder.build()));
382 + });
383 + return new GroupBuckets(buckets);
683 } 384 }
684 385
685 private class InternalHostListener implements HostListener { 386 private class InternalHostListener implements HostListener {
...@@ -692,50 +393,14 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro ...@@ -692,50 +393,14 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
692 return; 393 return;
693 } 394 }
694 395
396 + Instance instance = Instance.of(host);
695 switch (event.type()) { 397 switch (event.type()) {
696 case HOST_UPDATED: 398 case HOST_UPDATED:
697 case HOST_ADDED: 399 case HOST_ADDED:
698 - eventExecutor.execute(() -> serviceVmAdded(host)); 400 + eventExecutor.execute(() -> instanceDetected(instance));
699 break; 401 break;
700 case HOST_REMOVED: 402 case HOST_REMOVED:
701 - eventExecutor.execute(() -> serviceVmRemoved(host)); 403 + eventExecutor.execute(() -> instanceRemoved(instance));
702 - break;
703 - default:
704 - break;
705 - }
706 - }
707 - }
708 -
709 - private class InternalPacketProcessor implements PacketProcessor {
710 -
711 - @Override
712 - public void process(PacketContext context) {
713 - if (context.isHandled()) {
714 - return;
715 - }
716 -
717 - Ethernet ethPacket = context.inPacket().parsed();
718 - if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
719 - return;
720 - }
721 -
722 - arpProxy.processArpPacket(context, ethPacket);
723 - }
724 - }
725 -
726 - private class InternalConfigListener implements NetworkConfigListener {
727 -
728 - @Override
729 - public void event(NetworkConfigEvent event) {
730 - if (!event.configClass().equals(CordVtnConfig.class)) {
731 - return;
732 - }
733 -
734 - switch (event.type()) {
735 - case CONFIG_ADDED:
736 - case CONFIG_UPDATED:
737 - log.info("Network configuration changed");
738 - eventExecutor.execute(CordVtn.this::readConfiguration);
739 break; 404 break;
740 default: 405 default:
741 break; 406 break;
......
...@@ -22,6 +22,7 @@ import org.onlab.packet.Ethernet; ...@@ -22,6 +22,7 @@ import org.onlab.packet.Ethernet;
22 import org.onlab.packet.Ip4Address; 22 import org.onlab.packet.Ip4Address;
23 import org.onlab.packet.IpAddress; 23 import org.onlab.packet.IpAddress;
24 import org.onlab.packet.MacAddress; 24 import org.onlab.packet.MacAddress;
25 +import org.onosproject.cordvtn.api.Instance;
25 import org.onosproject.core.ApplicationId; 26 import org.onosproject.core.ApplicationId;
26 import org.onosproject.net.Host; 27 import org.onosproject.net.Host;
27 import org.onosproject.net.flow.DefaultTrafficSelector; 28 import org.onosproject.net.flow.DefaultTrafficSelector;
...@@ -166,9 +167,9 @@ public class CordVtnArpProxy { ...@@ -166,9 +167,9 @@ public class CordVtnArpProxy {
166 * Emits gratuitous ARP when a gateway mac address has been changed. 167 * Emits gratuitous ARP when a gateway mac address has been changed.
167 * 168 *
168 * @param gatewayIp gateway ip address to update MAC 169 * @param gatewayIp gateway ip address to update MAC
169 - * @param hosts set of hosts to send gratuitous ARP packet 170 + * @param instances set of instances to send gratuitous ARP packet
170 */ 171 */
171 - public void sendGratuitousArpForGateway(IpAddress gatewayIp, Set<Host> hosts) { 172 + public void sendGratuitousArpForGateway(IpAddress gatewayIp, Set<Instance> instances) {
172 MacAddress gatewayMac = gateways.get(gatewayIp.getIp4Address()); 173 MacAddress gatewayMac = gateways.get(gatewayIp.getIp4Address());
173 if (gatewayMac == null) { 174 if (gatewayMac == null) {
174 log.debug("Gateway {} is not registered to ARP proxy", gatewayIp.toString()); 175 log.debug("Gateway {} is not registered to ARP proxy", gatewayIp.toString());
...@@ -176,13 +177,13 @@ public class CordVtnArpProxy { ...@@ -176,13 +177,13 @@ public class CordVtnArpProxy {
176 } 177 }
177 178
178 Ethernet ethArp = buildGratuitousArp(gatewayIp.getIp4Address(), gatewayMac); 179 Ethernet ethArp = buildGratuitousArp(gatewayIp.getIp4Address(), gatewayMac);
179 - hosts.stream().forEach(host -> { 180 + instances.stream().forEach(instance -> {
180 TrafficTreatment treatment = DefaultTrafficTreatment.builder() 181 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
181 - .setOutput(host.location().port()) 182 + .setOutput(instance.portNumber())
182 .build(); 183 .build();
183 184
184 packetService.emit(new DefaultOutboundPacket( 185 packetService.emit(new DefaultOutboundPacket(
185 - host.location().deviceId(), 186 + instance.deviceId(),
186 treatment, 187 treatment,
187 ByteBuffer.wrap(ethArp.serialize()))); 188 ByteBuffer.wrap(ethArp.serialize())));
188 }); 189 });
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.cordvtn.impl;
17 +
18 +import org.apache.felix.scr.annotations.Component;
19 +import org.apache.felix.scr.annotations.Reference;
20 +import org.apache.felix.scr.annotations.ReferenceCardinality;
21 +import org.onlab.packet.Ethernet;
22 +import org.onlab.packet.Ip4Address;
23 +import org.onlab.packet.Ip4Prefix;
24 +import org.onosproject.cordvtn.api.CordVtnConfig;
25 +import org.onosproject.cordvtn.api.CordVtnNode;
26 +import org.onosproject.cordvtn.api.CordVtnService;
27 +import org.onosproject.cordvtn.api.Instance;
28 +import org.onosproject.cordvtn.api.InstanceHandler;
29 +import org.onosproject.core.ApplicationId;
30 +import org.onosproject.core.CoreService;
31 +import org.onosproject.mastership.MastershipService;
32 +import org.onosproject.net.Host;
33 +import org.onosproject.net.PortNumber;
34 +import org.onosproject.net.config.NetworkConfigEvent;
35 +import org.onosproject.net.config.NetworkConfigListener;
36 +import org.onosproject.net.config.NetworkConfigRegistry;
37 +import org.onosproject.net.flow.DefaultFlowRule;
38 +import org.onosproject.net.flow.DefaultTrafficSelector;
39 +import org.onosproject.net.flow.DefaultTrafficTreatment;
40 +import org.onosproject.net.flow.FlowRule;
41 +import org.onosproject.net.flow.TrafficSelector;
42 +import org.onosproject.net.flow.TrafficTreatment;
43 +import org.onosproject.net.flow.instructions.ExtensionTreatment;
44 +import org.onosproject.net.host.HostEvent;
45 +import org.onosproject.net.host.HostListener;
46 +import org.onosproject.net.host.HostService;
47 +import org.onosproject.xosclient.api.OpenStackAccess;
48 +import org.onosproject.xosclient.api.VtnService;
49 +import org.onosproject.xosclient.api.VtnServiceApi;
50 +import org.onosproject.xosclient.api.VtnServiceId;
51 +import org.onosproject.xosclient.api.XosAccess;
52 +import org.onosproject.xosclient.api.XosClientService;
53 +import org.slf4j.Logger;
54 +
55 +import java.util.Objects;
56 +import java.util.Set;
57 +import java.util.concurrent.ExecutorService;
58 +import java.util.stream.Collectors;
59 +import java.util.stream.StreamSupport;
60 +
61 +import static com.google.common.base.Preconditions.checkNotNull;
62 +import static org.onosproject.cordvtn.impl.CordVtnPipeline.*;
63 +import static org.onosproject.xosclient.api.VtnService.NetworkType.MANAGEMENT;
64 +import static org.slf4j.LoggerFactory.getLogger;
65 +
66 +/**
67 + * Provides default virtual network connectivity for service instances.
68 + */
69 +@Component(immediate = true)
70 +public abstract class CordVtnInstanceHandler implements InstanceHandler {
71 +
72 + protected final Logger log = getLogger(getClass());
73 +
74 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 + protected CoreService coreService;
76 +
77 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 + protected MastershipService mastershipService;
79 +
80 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 + protected NetworkConfigRegistry configRegistry;
82 +
83 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 + protected HostService hostService;
85 +
86 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 + protected XosClientService xosClient;
88 +
89 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
90 + protected CordVtnNodeManager nodeManager;
91 +
92 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
93 + protected CordVtnPipeline pipeline;
94 +
95 + protected static final String OPENSTACK_ACCESS_ERROR = "OpenStack access is not configured";
96 + protected static final String XOS_ACCESS_ERROR = "XOS access is not configured";
97 +
98 + protected XosAccess xosAccess = null;
99 + protected OpenStackAccess osAccess = null;
100 + protected ApplicationId appId;
101 + protected VtnService.ServiceType serviceType;
102 + protected ExecutorService eventExecutor;
103 +
104 + protected HostListener hostListener = new InternalHostListener();
105 + protected NetworkConfigListener configListener = new InternalConfigListener();
106 +
107 + protected void activate() {
108 + // sub class should set service type and event executor in its activate method
109 + appId = coreService.registerApplication(CordVtnService.CORDVTN_APP_ID);
110 +
111 + hostService.addListener(hostListener);
112 + configRegistry.addListener(configListener);
113 +
114 + log.info("Started");
115 + }
116 +
117 + protected void deactivate() {
118 + hostService.removeListener(hostListener);
119 + configRegistry.removeListener(configListener);
120 + eventExecutor.shutdown();
121 +
122 + log.info("Stopped");
123 + }
124 +
125 + @Override
126 + public void instanceDetected(Instance instance) {
127 + log.info("Instance is detected {}", instance);
128 +
129 + VtnService service = getVtnService(instance.serviceId());
130 + if (service == null) {
131 + log.warn("Failed to get VtnService for {}", instance);
132 + return;
133 + }
134 +
135 + if (service.networkType().equals(MANAGEMENT)) {
136 + managementNetworkRules(instance, service, true);
137 + }
138 +
139 + defaultConnectionRules(instance, service, true);
140 + }
141 +
142 + @Override
143 + public void instanceRemoved(Instance instance) {
144 + log.info("Instance is removed {}", instance);
145 +
146 + VtnService service = getVtnService(instance.serviceId());
147 + if (service == null) {
148 + log.warn("Failed to get VtnService for {}", instance);
149 + return;
150 + }
151 +
152 + if (service.networkType().equals(MANAGEMENT)) {
153 + managementNetworkRules(instance, service, false);
154 + }
155 +
156 + // TODO check if any stale management network rules are
157 + defaultConnectionRules(instance, service, false);
158 + }
159 +
160 + protected VtnService getVtnService(VtnServiceId serviceId) {
161 + checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
162 + checkNotNull(xosAccess, XOS_ACCESS_ERROR);
163 +
164 + // TODO remove openstack access when XOS provides all information
165 + VtnServiceApi serviceApi = xosClient.getClient(xosAccess).vtnService();
166 + VtnService service = serviceApi.service(serviceId, osAccess);
167 + if (service == null) {
168 + log.warn("Failed to get VtnService for {}", serviceId);
169 + }
170 + return service;
171 + }
172 +
173 + protected Set<Instance> getInstances(VtnServiceId serviceId) {
174 + return StreamSupport.stream(hostService.getHosts().spliterator(), false)
175 + .filter(host -> Objects.equals(
176 + serviceId.id(),
177 + host.annotations().value(Instance.SERVICE_ID)))
178 + .map(Instance::of)
179 + .collect(Collectors.toSet());
180 + }
181 +
182 + private void defaultConnectionRules(Instance instance, VtnService service, boolean install) {
183 + long vni = service.vni();
184 + Ip4Prefix serviceIpRange = service.subnet().getIp4Prefix();
185 +
186 + inPortRule(instance, install);
187 + dstIpRule(instance, vni, install);
188 + tunnelInRule(instance, vni, install);
189 +
190 + if (install) {
191 + directAccessRule(serviceIpRange, serviceIpRange, true);
192 + serviceIsolationRule(serviceIpRange, true);
193 + } else if (getInstances(service.id()).isEmpty()) {
194 + directAccessRule(serviceIpRange, serviceIpRange, false);
195 + serviceIsolationRule(serviceIpRange, false);
196 + }
197 + }
198 +
199 + private void managementNetworkRules(Instance instance, VtnService service, boolean install) {
200 +
201 + managementPerInstanceRule(instance, install);
202 + if (install) {
203 + managementBaseRule(instance, service, true);
204 + } else if (!hostService.getConnectedHosts(instance.deviceId()).stream()
205 + .filter(host -> Instance.of(host).serviceId().equals(service.id()))
206 + .findAny()
207 + .isPresent()) {
208 + managementBaseRule(instance, service, false);
209 + }
210 + }
211 +
212 + private void managementBaseRule(Instance instance, VtnService service, boolean install) {
213 + TrafficSelector selector = DefaultTrafficSelector.builder()
214 + .matchEthType(Ethernet.TYPE_ARP)
215 + .matchArpTpa(service.serviceIp().getIp4Address())
216 + .build();
217 +
218 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
219 + .setOutput(PortNumber.LOCAL)
220 + .build();
221 +
222 + FlowRule flowRule = DefaultFlowRule.builder()
223 + .fromApp(appId)
224 + .withSelector(selector)
225 + .withTreatment(treatment)
226 + .withPriority(PRIORITY_MANAGEMENT)
227 + .forDevice(instance.deviceId())
228 + .forTable(TABLE_ZERO)
229 + .makePermanent()
230 + .build();
231 +
232 + pipeline.processFlowRule(install, flowRule);
233 +
234 + selector = DefaultTrafficSelector.builder()
235 + .matchInPort(PortNumber.LOCAL)
236 + .matchEthType(Ethernet.TYPE_IPV4)
237 + .matchIPDst(service.subnet())
238 + .build();
239 +
240 + treatment = DefaultTrafficTreatment.builder()
241 + .transition(TABLE_DST_IP)
242 + .build();
243 +
244 + flowRule = DefaultFlowRule.builder()
245 + .fromApp(appId)
246 + .withSelector(selector)
247 + .withTreatment(treatment)
248 + .withPriority(PRIORITY_MANAGEMENT)
249 + .forDevice(instance.deviceId())
250 + .forTable(TABLE_ZERO)
251 + .makePermanent()
252 + .build();
253 +
254 + pipeline.processFlowRule(install, flowRule);
255 +
256 + selector = DefaultTrafficSelector.builder()
257 + .matchEthType(Ethernet.TYPE_IPV4)
258 + .matchIPDst(service.serviceIp().toIpPrefix())
259 + .build();
260 +
261 + treatment = DefaultTrafficTreatment.builder()
262 + .setOutput(PortNumber.LOCAL)
263 + .build();
264 +
265 + flowRule = DefaultFlowRule.builder()
266 + .fromApp(appId)
267 + .withSelector(selector)
268 + .withTreatment(treatment)
269 + .withPriority(PRIORITY_MANAGEMENT)
270 + .forDevice(instance.deviceId())
271 + .forTable(TABLE_ACCESS_TYPE)
272 + .makePermanent()
273 + .build();
274 +
275 + pipeline.processFlowRule(install, flowRule);
276 + }
277 +
278 + private void managementPerInstanceRule(Instance instance, boolean install) {
279 + TrafficSelector selector = DefaultTrafficSelector.builder()
280 + .matchInPort(PortNumber.LOCAL)
281 + .matchEthType(Ethernet.TYPE_ARP)
282 + .matchArpTpa(instance.ipAddress().getIp4Address())
283 + .build();
284 +
285 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
286 + .setOutput(instance.portNumber())
287 + .build();
288 +
289 + FlowRule flowRule = DefaultFlowRule.builder()
290 + .fromApp(appId)
291 + .withSelector(selector)
292 + .withTreatment(treatment)
293 + .withPriority(PRIORITY_MANAGEMENT)
294 + .forDevice(instance.deviceId())
295 + .forTable(TABLE_ZERO)
296 + .makePermanent()
297 + .build();
298 +
299 + pipeline.processFlowRule(install, flowRule);
300 + }
301 +
302 + private void inPortRule(Instance instance, boolean install) {
303 + TrafficSelector selector = DefaultTrafficSelector.builder()
304 + .matchInPort(instance.portNumber())
305 + .matchEthType(Ethernet.TYPE_IPV4)
306 + .matchIPSrc(instance.ipAddress().toIpPrefix())
307 + .build();
308 +
309 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
310 + .transition(TABLE_ACCESS_TYPE)
311 + .build();
312 +
313 +
314 + FlowRule flowRule = DefaultFlowRule.builder()
315 + .fromApp(appId)
316 + .withSelector(selector)
317 + .withTreatment(treatment)
318 + .withPriority(PRIORITY_DEFAULT)
319 + .forDevice(instance.deviceId())
320 + .forTable(TABLE_IN_PORT)
321 + .makePermanent()
322 + .build();
323 +
324 + pipeline.processFlowRule(install, flowRule);
325 +
326 + selector = DefaultTrafficSelector.builder()
327 + .matchInPort(instance.portNumber())
328 + .build();
329 +
330 + treatment = DefaultTrafficTreatment.builder()
331 + .transition(TABLE_IN_SERVICE)
332 + .build();
333 +
334 + flowRule = DefaultFlowRule.builder()
335 + .fromApp(appId)
336 + .withSelector(selector)
337 + .withTreatment(treatment)
338 + .withPriority(PRIORITY_LOW)
339 + .forDevice(instance.deviceId())
340 + .forTable(TABLE_IN_PORT)
341 + .makePermanent()
342 + .build();
343 +
344 + pipeline.processFlowRule(install, flowRule);
345 + }
346 +
347 + private void dstIpRule(Instance instance, long vni, boolean install) {
348 + Ip4Address tunnelIp = nodeManager.dpIp(instance.deviceId()).getIp4Address();
349 +
350 + TrafficSelector selector = DefaultTrafficSelector.builder()
351 + .matchEthType(Ethernet.TYPE_IPV4)
352 + .matchIPDst(instance.ipAddress().toIpPrefix())
353 + .build();
354 +
355 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
356 + .setEthDst(instance.mac())
357 + .setOutput(instance.portNumber())
358 + .build();
359 +
360 + FlowRule flowRule = DefaultFlowRule.builder()
361 + .fromApp(appId)
362 + .withSelector(selector)
363 + .withTreatment(treatment)
364 + .withPriority(PRIORITY_DEFAULT)
365 + .forDevice(instance.deviceId())
366 + .forTable(TABLE_DST_IP)
367 + .makePermanent()
368 + .build();
369 +
370 + pipeline.processFlowRule(install, flowRule);
371 +
372 + for (CordVtnNode node : nodeManager.completeNodes()) {
373 + if (node.intBrId().equals(instance.deviceId())) {
374 + continue;
375 + }
376 +
377 + ExtensionTreatment tunnelDst = pipeline.tunnelDstTreatment(node.intBrId(), tunnelIp);
378 + if (tunnelDst == null) {
379 + continue;
380 + }
381 +
382 + treatment = DefaultTrafficTreatment.builder()
383 + .setEthDst(instance.mac())
384 + .setTunnelId(vni)
385 + .extension(tunnelDst, node.intBrId())
386 + .setOutput(nodeManager.tunnelPort(node.intBrId()))
387 + .build();
388 +
389 + flowRule = DefaultFlowRule.builder()
390 + .fromApp(appId)
391 + .withSelector(selector)
392 + .withTreatment(treatment)
393 + .withPriority(PRIORITY_DEFAULT)
394 + .forDevice(node.intBrId())
395 + .forTable(TABLE_DST_IP)
396 + .makePermanent()
397 + .build();
398 +
399 + pipeline.processFlowRule(install, flowRule);
400 + }
401 + }
402 +
403 + private void tunnelInRule(Instance instance, long vni, boolean install) {
404 + TrafficSelector selector = DefaultTrafficSelector.builder()
405 + .matchTunnelId(vni)
406 + .matchEthDst(instance.mac())
407 + .build();
408 +
409 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
410 + .setOutput(instance.portNumber())
411 + .build();
412 +
413 + FlowRule flowRule = DefaultFlowRule.builder()
414 + .fromApp(appId)
415 + .withSelector(selector)
416 + .withTreatment(treatment)
417 + .withPriority(PRIORITY_DEFAULT)
418 + .forDevice(instance.deviceId())
419 + .forTable(TABLE_TUNNEL_IN)
420 + .makePermanent()
421 + .build();
422 +
423 + pipeline.processFlowRule(install, flowRule);
424 + }
425 +
426 + private void directAccessRule(Ip4Prefix srcRange, Ip4Prefix dstRange, boolean install) {
427 + TrafficSelector selector = DefaultTrafficSelector.builder()
428 + .matchEthType(Ethernet.TYPE_IPV4)
429 + .matchIPSrc(srcRange)
430 + .matchIPDst(dstRange)
431 + .build();
432 +
433 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
434 + .transition(TABLE_DST_IP)
435 + .build();
436 +
437 +
438 + nodeManager.completeNodes().stream().forEach(node -> {
439 + FlowRule flowRuleDirect = DefaultFlowRule.builder()
440 + .fromApp(appId)
441 + .withSelector(selector)
442 + .withTreatment(treatment)
443 + .withPriority(PRIORITY_DEFAULT)
444 + .forDevice(node.intBrId())
445 + .forTable(TABLE_ACCESS_TYPE)
446 + .makePermanent()
447 + .build();
448 +
449 + pipeline.processFlowRule(install, flowRuleDirect);
450 + });
451 + }
452 +
453 + private void serviceIsolationRule(Ip4Prefix dstRange, boolean install) {
454 + TrafficSelector selector = DefaultTrafficSelector.builder()
455 + .matchEthType(Ethernet.TYPE_IPV4)
456 + .matchIPDst(dstRange)
457 + .build();
458 +
459 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
460 + .drop()
461 + .build();
462 +
463 + nodeManager.completeNodes().stream().forEach(node -> {
464 + FlowRule flowRuleDirect = DefaultFlowRule.builder()
465 + .fromApp(appId)
466 + .withSelector(selector)
467 + .withTreatment(treatment)
468 + .withPriority(PRIORITY_LOW)
469 + .forDevice(node.intBrId())
470 + .forTable(TABLE_ACCESS_TYPE)
471 + .makePermanent()
472 + .build();
473 +
474 + pipeline.processFlowRule(install, flowRuleDirect);
475 + });
476 + }
477 +
478 + protected void readConfiguration() {
479 + CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
480 + if (config == null) {
481 + log.debug("No configuration found");
482 + return;
483 + }
484 + osAccess = config.openstackAccess();
485 + xosAccess = config.xosAccess();
486 + }
487 +
488 + public class InternalHostListener implements HostListener {
489 +
490 + @Override
491 + public void event(HostEvent event) {
492 + Host host = event.subject();
493 + if (!mastershipService.isLocalMaster(host.location().deviceId())) {
494 + // do not allow to proceed without mastership
495 + return;
496 + }
497 +
498 + Instance instance = Instance.of(host);
499 + if (!Objects.equals(instance.serviceType(), serviceType)) {
500 + // not my service instance, do nothing
501 + return;
502 + }
503 +
504 + switch (event.type()) {
505 + case HOST_UPDATED:
506 + case HOST_ADDED:
507 + eventExecutor.execute(() -> instanceDetected(instance));
508 + break;
509 + case HOST_REMOVED:
510 + eventExecutor.execute(() -> instanceRemoved(instance));
511 + break;
512 + default:
513 + break;
514 + }
515 + }
516 + }
517 +
518 + public class InternalConfigListener implements NetworkConfigListener {
519 +
520 + @Override
521 + public void event(NetworkConfigEvent event) {
522 + if (!event.configClass().equals(CordVtnConfig.class)) {
523 + return;
524 + }
525 +
526 + switch (event.type()) {
527 + case CONFIG_ADDED:
528 + case CONFIG_UPDATED:
529 + readConfiguration();
530 + break;
531 + default:
532 + break;
533 + }
534 + }
535 + }
536 +}
1 +/*
2 + * Copyright 2015-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.cordvtn.impl;
17 +
18 +import com.google.common.collect.Lists;
19 +import com.google.common.collect.Sets;
20 +import org.apache.felix.scr.annotations.Activate;
21 +import org.apache.felix.scr.annotations.Component;
22 +import org.apache.felix.scr.annotations.Deactivate;
23 +import org.apache.felix.scr.annotations.Reference;
24 +import org.apache.felix.scr.annotations.ReferenceCardinality;
25 +import org.apache.felix.scr.annotations.Service;
26 +import org.onlab.packet.Ethernet;
27 +import org.onlab.packet.Ip4Address;
28 +import org.onlab.packet.IpAddress;
29 +import org.onlab.packet.MacAddress;
30 +import org.onlab.packet.VlanId;
31 +import org.onosproject.cordvtn.api.CordVtnConfig;
32 +import org.onosproject.cordvtn.api.CordVtnService;
33 +import org.onosproject.cordvtn.api.Instance;
34 +import org.onosproject.core.ApplicationId;
35 +import org.onosproject.core.CoreService;
36 +import org.onosproject.dhcp.DhcpService;
37 +import org.onosproject.mastership.MastershipService;
38 +import org.onosproject.net.ConnectPoint;
39 +import org.onosproject.net.DefaultAnnotations;
40 +import org.onosproject.net.Host;
41 +import org.onosproject.net.HostId;
42 +import org.onosproject.net.HostLocation;
43 +import org.onosproject.net.Port;
44 +import org.onosproject.net.config.ConfigFactory;
45 +import org.onosproject.net.config.NetworkConfigEvent;
46 +import org.onosproject.net.config.NetworkConfigListener;
47 +import org.onosproject.net.config.NetworkConfigRegistry;
48 +import org.onosproject.net.config.basics.SubjectFactories;
49 +import org.onosproject.net.device.DeviceService;
50 +import org.onosproject.net.host.DefaultHostDescription;
51 +import org.onosproject.net.host.HostDescription;
52 +import org.onosproject.net.host.HostEvent;
53 +import org.onosproject.net.host.HostListener;
54 +import org.onosproject.net.host.HostProvider;
55 +import org.onosproject.net.host.HostProviderRegistry;
56 +import org.onosproject.net.host.HostProviderService;
57 +import org.onosproject.net.host.HostService;
58 +import org.onosproject.net.packet.PacketContext;
59 +import org.onosproject.net.packet.PacketProcessor;
60 +import org.onosproject.net.packet.PacketService;
61 +import org.onosproject.net.provider.AbstractProvider;
62 +import org.onosproject.net.provider.ProviderId;
63 +import org.onosproject.xosclient.api.OpenStackAccess;
64 +import org.onosproject.xosclient.api.VtnPort;
65 +import org.onosproject.xosclient.api.VtnPortApi;
66 +import org.onosproject.xosclient.api.VtnService;
67 +import org.onosproject.xosclient.api.VtnServiceApi;
68 +import org.onosproject.xosclient.api.VtnServiceId;
69 +import org.onosproject.xosclient.api.XosAccess;
70 +import org.onosproject.xosclient.api.XosClientService;
71 +import org.slf4j.Logger;
72 +
73 +import java.util.List;
74 +import java.util.Map;
75 +import java.util.Objects;
76 +import java.util.Set;
77 +import java.util.concurrent.ExecutorService;
78 +import java.util.stream.Collectors;
79 +import java.util.stream.StreamSupport;
80 +
81 +import static com.google.common.base.Preconditions.checkNotNull;
82 +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
83 +import static org.onlab.util.Tools.groupedThreads;
84 +import static org.onosproject.cordvtn.api.Instance.*;
85 +import static org.onosproject.xosclient.api.VtnService.NetworkType.PRIVATE;
86 +import static org.slf4j.LoggerFactory.getLogger;
87 +
88 +/**
89 + * Adds or removes instances to network services.
90 + */
91 +@Component(immediate = true)
92 +@Service(value = CordVtnInstanceManager.class)
93 +public class CordVtnInstanceManager extends AbstractProvider implements HostProvider {
94 +
95 + protected final Logger log = getLogger(getClass());
96 +
97 + private static final String XOS_ACCESS_ERROR = "XOS access is not configured";
98 + private static final String OPENSTACK_ACCESS_ERROR = "OpenStack access is not configured";
99 + private static final Ip4Address DEFAULT_DNS = Ip4Address.valueOf("8.8.8.8");
100 +
101 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 + protected CoreService coreService;
103 +
104 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 + protected NetworkConfigRegistry configRegistry;
106 +
107 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 + protected HostProviderRegistry hostProviderRegistry;
109 +
110 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 + protected DeviceService deviceService;
112 +
113 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 + protected HostService hostService;
115 +
116 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 + protected PacketService packetService;
118 +
119 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
120 + protected DhcpService dhcpService;
121 +
122 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 + protected MastershipService mastershipService;
124 +
125 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 + protected XosClientService xosClient;
127 +
128 + private final ConfigFactory configFactory =
129 + new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, CordVtnConfig.class, "cordvtn") {
130 + @Override
131 + public CordVtnConfig createConfig() {
132 + return new CordVtnConfig();
133 + }
134 + };
135 +
136 + private final ExecutorService eventExecutor =
137 + newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn-instance", "event-handler"));
138 + private final PacketProcessor packetProcessor = new InternalPacketProcessor();
139 + private final HostListener hostListener = new InternalHostListener();
140 + private final NetworkConfigListener configListener = new InternalConfigListener();
141 +
142 + private ApplicationId appId;
143 + private HostProviderService hostProvider;
144 + private CordVtnArpProxy arpProxy; // TODO make it a component service
145 + private MacAddress privateGatewayMac = MacAddress.NONE;
146 + private XosAccess xosAccess = null;
147 + private OpenStackAccess osAccess = null;
148 +
149 + /**
150 + * Creates an cordvtn host location provider.
151 + */
152 + public CordVtnInstanceManager() {
153 + super(new ProviderId("host", CordVtnService.CORDVTN_APP_ID));
154 + }
155 +
156 + @Activate
157 + protected void activate() {
158 + appId = coreService.registerApplication(CordVtnService.CORDVTN_APP_ID);
159 +
160 + arpProxy = new CordVtnArpProxy(appId, packetService, hostService);
161 + packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
162 + arpProxy.requestPacket();
163 +
164 + hostService.addListener(hostListener);
165 + hostProvider = hostProviderRegistry.register(this);
166 +
167 + configRegistry.registerConfigFactory(configFactory);
168 + configRegistry.addListener(configListener);
169 +
170 + log.info("Started");
171 + }
172 +
173 + @Deactivate
174 + protected void deactivate() {
175 + hostProviderRegistry.unregister(this);
176 + hostService.removeListener(hostListener);
177 +
178 + packetService.removeProcessor(packetProcessor);
179 +
180 + configRegistry.unregisterConfigFactory(configFactory);
181 + configRegistry.removeListener(configListener);
182 +
183 + eventExecutor.shutdown();
184 + log.info("Stopped");
185 + }
186 +
187 + @Override
188 + public void triggerProbe(Host host) {
189 + /*
190 + * Note: In CORD deployment, we assume that all hosts are configured.
191 + * Therefore no probe is required.
192 + */
193 + }
194 +
195 + /**
196 + * Adds a service instance at a given connect point.
197 + *
198 + * @param connectPoint connect point of the instance
199 + */
200 + public void addInstance(ConnectPoint connectPoint) {
201 + Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
202 + if (port == null) {
203 + log.debug("No port found from {}", connectPoint);
204 + return;
205 + }
206 +
207 + VtnPort vtnPort = getVtnPort(port.annotations().value("portName"));
208 + if (vtnPort == null) {
209 + return;
210 + }
211 +
212 + VtnService vtnService = getVtnService(vtnPort.serviceId());
213 + if (vtnService == null) {
214 + return;
215 + }
216 +
217 + // Added CREATE_TIME intentionally to trigger HOST_UPDATED event for the
218 + // existing instances.
219 + DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
220 + .set(SERVICE_TYPE, vtnService.serviceType().toString())
221 + .set(SERVICE_ID, vtnPort.serviceId().id())
222 + .set(PORT_ID, vtnPort.id().id())
223 + .set(CREATE_TIME, String.valueOf(System.currentTimeMillis()));
224 +
225 + HostDescription hostDesc = new DefaultHostDescription(
226 + vtnPort.mac(),
227 + VlanId.NONE,
228 + new HostLocation(connectPoint, System.currentTimeMillis()),
229 + Sets.newHashSet(vtnPort.ip()),
230 + annotations.build());
231 +
232 + HostId hostId = HostId.hostId(vtnPort.mac());
233 + hostProvider.hostDetected(hostId, hostDesc, false);
234 + }
235 +
236 + /**
237 + * Adds a service instance with given host ID and host description.
238 + *
239 + * @param hostId host id
240 + * @param description host description
241 + */
242 + public void addInstance(HostId hostId, HostDescription description) {
243 + hostProvider.hostDetected(hostId, description, false);
244 + }
245 +
246 + /**
247 + * Removes a service instance from a given connect point.
248 + *
249 + * @param connectPoint connect point
250 + */
251 + public void removeInstance(ConnectPoint connectPoint) {
252 + hostService.getConnectedHosts(connectPoint)
253 + .stream()
254 + .forEach(host -> hostProvider.hostVanished(host.id()));
255 + }
256 +
257 + /**
258 + * Removes service instance with given host ID.
259 + *
260 + * @param hostId host id
261 + */
262 + public void removeInstance(HostId hostId) {
263 + hostProvider.hostVanished(hostId);
264 + }
265 +
266 + private void instanceDetected(Instance instance) {
267 + VtnService service = getVtnService(instance.serviceId());
268 + if (service == null) {
269 + return;
270 + }
271 +
272 + if (service.networkType().equals(PRIVATE)) {
273 + arpProxy.addGateway(service.serviceIp(), privateGatewayMac);
274 + arpProxy.sendGratuitousArpForGateway(service.serviceIp(), Sets.newHashSet(instance));
275 + }
276 +
277 + if (!instance.isNestedInstance()) {
278 + registerDhcpLease(instance, service);
279 + }
280 + }
281 +
282 + private void instanceRemoved(Instance instance) {
283 + VtnService service = getVtnService(instance.serviceId());
284 + if (service == null) {
285 + return;
286 + }
287 +
288 + if (service.networkType().equals(PRIVATE) && getInstances(service.id()).isEmpty()) {
289 + arpProxy.removeGateway(service.serviceIp());
290 + }
291 +
292 + if (!instance.isNestedInstance()) {
293 + dhcpService.removeStaticMapping(instance.mac());
294 + }
295 + }
296 +
297 + private void registerDhcpLease(Instance instance, VtnService service) {
298 + List<Ip4Address> options = Lists.newArrayList();
299 + options.add(Ip4Address.makeMaskPrefix(service.subnet().prefixLength()));
300 + options.add(service.serviceIp().getIp4Address());
301 + options.add(service.serviceIp().getIp4Address());
302 + options.add(DEFAULT_DNS);
303 +
304 + log.debug("Set static DHCP mapping for {} {}", instance.mac(), instance.ipAddress());
305 + dhcpService.setStaticMapping(instance.mac(),
306 + instance.ipAddress(),
307 + true,
308 + options);
309 + }
310 +
311 + private VtnService getVtnService(VtnServiceId serviceId) {
312 + checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
313 + checkNotNull(xosAccess, XOS_ACCESS_ERROR);
314 +
315 + // TODO remove openstack access when XOS provides all information
316 + VtnServiceApi serviceApi = xosClient.getClient(xosAccess).vtnService();
317 + VtnService service = serviceApi.service(serviceId, osAccess);
318 + if (service == null) {
319 + log.warn("Failed to get VtnService for {}", serviceId);
320 + }
321 + return service;
322 + }
323 +
324 + private VtnPort getVtnPort(String portName) {
325 + checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
326 + checkNotNull(xosAccess, XOS_ACCESS_ERROR);
327 +
328 + // TODO remove openstack access when XOS provides all information
329 + VtnPortApi portApi = xosClient.getClient(xosAccess).vtnPort();
330 + VtnPort vtnPort = portApi.vtnPort(portName, osAccess);
331 + if (vtnPort == null) {
332 + log.warn("Failed to get port information of {}", portName);
333 + }
334 + return vtnPort;
335 + }
336 +
337 + private Set<Instance> getInstances(VtnServiceId serviceId) {
338 + return StreamSupport.stream(hostService.getHosts().spliterator(), false)
339 + .filter(host -> Objects.equals(
340 + serviceId.id(),
341 + host.annotations().value(Instance.SERVICE_ID)))
342 + .map(Instance::of)
343 + .collect(Collectors.toSet());
344 + }
345 +
346 + private void readConfiguration() {
347 + CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
348 + if (config == null) {
349 + log.debug("No configuration found");
350 + return;
351 + }
352 +
353 + log.info("Load CORD-VTN configurations");
354 +
355 + xosAccess = config.xosAccess();
356 + osAccess = config.openstackAccess();
357 + privateGatewayMac = config.privateGatewayMac();
358 +
359 + Map<IpAddress, MacAddress> publicGateways = config.publicGateways();
360 + publicGateways.entrySet()
361 + .stream()
362 + .forEach(entry -> {
363 + arpProxy.addGateway(entry.getKey(), entry.getValue());
364 + log.debug("Added public gateway IP {}, MAC {}",
365 + entry.getKey(), entry.getValue());
366 + });
367 + // TODO notice gateway MAC change to VMs holds this gateway IP
368 + }
369 +
370 + private class InternalHostListener implements HostListener {
371 +
372 + @Override
373 + public void event(HostEvent event) {
374 + Host host = event.subject();
375 + if (!mastershipService.isLocalMaster(host.location().deviceId())) {
376 + // do not allow to proceed without mastership
377 + return;
378 + }
379 +
380 + Instance instance = Instance.of(host);
381 + switch (event.type()) {
382 + case HOST_UPDATED:
383 + case HOST_ADDED:
384 + eventExecutor.execute(() -> instanceDetected(instance));
385 + break;
386 + case HOST_REMOVED:
387 + eventExecutor.execute(() -> instanceRemoved(instance));
388 + break;
389 + default:
390 + break;
391 + }
392 + }
393 + }
394 +
395 + private class InternalPacketProcessor implements PacketProcessor {
396 +
397 + @Override
398 + public void process(PacketContext context) {
399 + if (context.isHandled()) {
400 + return;
401 + }
402 + Ethernet ethPacket = context.inPacket().parsed();
403 + if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
404 + return;
405 + }
406 + arpProxy.processArpPacket(context, ethPacket);
407 + }
408 + }
409 +
410 + private class InternalConfigListener implements NetworkConfigListener {
411 +
412 + @Override
413 + public void event(NetworkConfigEvent event) {
414 + if (!event.configClass().equals(CordVtnConfig.class)) {
415 + return;
416 + }
417 +
418 + switch (event.type()) {
419 + case CONFIG_ADDED:
420 + case CONFIG_UPDATED:
421 + readConfiguration();
422 + break;
423 + default:
424 + break;
425 + }
426 + }
427 + }
428 +}
...@@ -44,6 +44,7 @@ import org.onosproject.net.Device; ...@@ -44,6 +44,7 @@ import org.onosproject.net.Device;
44 import org.onosproject.net.DeviceId; 44 import org.onosproject.net.DeviceId;
45 import org.onosproject.net.Host; 45 import org.onosproject.net.Host;
46 import org.onosproject.net.Port; 46 import org.onosproject.net.Port;
47 +import org.onosproject.net.PortNumber;
47 import org.onosproject.net.behaviour.BridgeConfig; 48 import org.onosproject.net.behaviour.BridgeConfig;
48 import org.onosproject.net.behaviour.BridgeName; 49 import org.onosproject.net.behaviour.BridgeName;
49 import org.onosproject.net.behaviour.ControllerInfo; 50 import org.onosproject.net.behaviour.ControllerInfo;
...@@ -59,8 +60,6 @@ import org.onosproject.net.device.DeviceAdminService; ...@@ -59,8 +60,6 @@ import org.onosproject.net.device.DeviceAdminService;
59 import org.onosproject.net.device.DeviceEvent; 60 import org.onosproject.net.device.DeviceEvent;
60 import org.onosproject.net.device.DeviceListener; 61 import org.onosproject.net.device.DeviceListener;
61 import org.onosproject.net.device.DeviceService; 62 import org.onosproject.net.device.DeviceService;
62 -import org.onosproject.net.flow.FlowRuleService;
63 -import org.onosproject.net.group.GroupService;
64 import org.onosproject.net.host.HostService; 63 import org.onosproject.net.host.HostService;
65 import org.onosproject.ovsdb.controller.OvsdbClientService; 64 import org.onosproject.ovsdb.controller.OvsdbClientService;
66 import org.onosproject.ovsdb.controller.OvsdbController; 65 import org.onosproject.ovsdb.controller.OvsdbController;
...@@ -86,6 +85,7 @@ import java.util.stream.Collectors; ...@@ -86,6 +85,7 @@ import java.util.stream.Collectors;
86 import static com.google.common.base.Preconditions.checkNotNull; 85 import static com.google.common.base.Preconditions.checkNotNull;
87 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; 86 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
88 import static org.onlab.util.Tools.groupedThreads; 87 import static org.onlab.util.Tools.groupedThreads;
88 +import static org.onosproject.cordvtn.impl.CordVtnPipeline.DEFAULT_TUNNEL;
89 import static org.onosproject.cordvtn.impl.RemoteIpCommandUtil.*; 89 import static org.onosproject.cordvtn.impl.RemoteIpCommandUtil.*;
90 import static org.onosproject.net.Device.Type.SWITCH; 90 import static org.onosproject.net.Device.Type.SWITCH;
91 import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN; 91 import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN;
...@@ -110,7 +110,6 @@ public class CordVtnNodeManager { ...@@ -110,7 +110,6 @@ public class CordVtnNodeManager {
110 .register(NetworkAddress.class); 110 .register(NetworkAddress.class);
111 111
112 private static final String DEFAULT_BRIDGE = "br-int"; 112 private static final String DEFAULT_BRIDGE = "br-int";
113 - private static final String DEFAULT_TUNNEL = "vxlan";
114 private static final String VPORT_PREFIX = "tap"; 113 private static final String VPORT_PREFIX = "tap";
115 private static final String OK = "OK"; 114 private static final String OK = "OK";
116 private static final String NO = "NO"; 115 private static final String NO = "NO";
...@@ -152,19 +151,16 @@ public class CordVtnNodeManager { ...@@ -152,19 +151,16 @@ public class CordVtnNodeManager {
152 protected HostService hostService; 151 protected HostService hostService;
153 152
154 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 153 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
155 - protected FlowRuleService flowRuleService;
156 -
157 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
158 protected LeadershipService leadershipService; 154 protected LeadershipService leadershipService;
159 155
160 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 156 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
161 - protected GroupService groupService; 157 + protected CordVtnInstanceManager instanceManager;
162 158
163 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 159 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
164 - protected CordVtnService cordVtnService; 160 + protected CordVtnPipeline pipeline;
165 161
166 private final ExecutorService eventExecutor = 162 private final ExecutorService eventExecutor =
167 - newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtncfg", "event-handler")); 163 + newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn-node", "event-handler"));
168 164
169 private final NetworkConfigListener configListener = new InternalConfigListener(); 165 private final NetworkConfigListener configListener = new InternalConfigListener();
170 private final DeviceListener deviceListener = new InternalDeviceListener(); 166 private final DeviceListener deviceListener = new InternalDeviceListener();
...@@ -174,7 +170,6 @@ public class CordVtnNodeManager { ...@@ -174,7 +170,6 @@ public class CordVtnNodeManager {
174 private final BridgeHandler bridgeHandler = new BridgeHandler(); 170 private final BridgeHandler bridgeHandler = new BridgeHandler();
175 171
176 private ConsistentMap<String, CordVtnNode> nodeStore; 172 private ConsistentMap<String, CordVtnNode> nodeStore;
177 - private CordVtnRuleInstaller ruleInstaller;
178 private ApplicationId appId; 173 private ApplicationId appId;
179 private NodeId localNodeId; 174 private NodeId localNodeId;
180 175
...@@ -225,6 +220,7 @@ public class CordVtnNodeManager { ...@@ -225,6 +220,7 @@ public class CordVtnNodeManager {
225 @Activate 220 @Activate
226 protected void activate() { 221 protected void activate() {
227 appId = coreService.getAppId(CordVtnService.CORDVTN_APP_ID); 222 appId = coreService.getAppId(CordVtnService.CORDVTN_APP_ID);
223 +
228 localNodeId = clusterService.getLocalNode().id(); 224 localNodeId = clusterService.getLocalNode().id();
229 leadershipService.runForLeadership(appId.name()); 225 leadershipService.runForLeadership(appId.name());
230 226
...@@ -234,13 +230,6 @@ public class CordVtnNodeManager { ...@@ -234,13 +230,6 @@ public class CordVtnNodeManager {
234 .withApplicationId(appId) 230 .withApplicationId(appId)
235 .build(); 231 .build();
236 232
237 - ruleInstaller = new CordVtnRuleInstaller(appId, flowRuleService,
238 - deviceService,
239 - groupService,
240 - hostService,
241 - configRegistry,
242 - DEFAULT_TUNNEL);
243 -
244 nodeStore.addListener(nodeStoreListener); 233 nodeStore.addListener(nodeStoreListener);
245 deviceService.addListener(deviceListener); 234 deviceService.addListener(deviceListener);
246 configService.addListener(configListener); 235 configService.addListener(configListener);
...@@ -286,20 +275,6 @@ public class CordVtnNodeManager { ...@@ -286,20 +275,6 @@ public class CordVtnNodeManager {
286 } 275 }
287 276
288 /** 277 /**
289 - * Initiates node to serve virtual tenant network.
290 - *
291 - * @param node cordvtn node
292 - */
293 - private void initNode(CordVtnNode node) {
294 - checkNotNull(node);
295 -
296 - NodeState state = (NodeState) node.state();
297 - log.debug("Processing node: {} state: {}", node.hostname(), state);
298 -
299 - state.process(this, node);
300 - }
301 -
302 - /**
303 * Returns node initialization state. 278 * Returns node initialization state.
304 * 279 *
305 * @param node cordvtn node 280 * @param node cordvtn node
...@@ -311,30 +286,6 @@ public class CordVtnNodeManager { ...@@ -311,30 +286,6 @@ public class CordVtnNodeManager {
311 } 286 }
312 287
313 /** 288 /**
314 - * Flush flows installed by cordvtn.
315 - */
316 - public void flushRules() {
317 - ruleInstaller.flushRules();
318 - }
319 -
320 - /**
321 - * Returns if current node state saved in nodeStore is COMPLETE or not.
322 - *
323 - * @param node cordvtn node
324 - * @return true if it's complete state, otherwise false
325 - */
326 - private boolean isNodeStateComplete(CordVtnNode node) {
327 - checkNotNull(node);
328 -
329 - // the state saved in nodeStore can be wrong if IP address settings are changed
330 - // after the node init has been completed since there's no way to detect it
331 - // getNodeState and checkNodeInitState always return correct answer but can be slow
332 - Versioned<CordVtnNode> versionedNode = nodeStore.get(node.hostname());
333 - CordVtnNodeState state = versionedNode.value().state();
334 - return state != null && state.equals(NodeState.COMPLETE);
335 - }
336 -
337 - /**
338 * Returns detailed node initialization state. 289 * Returns detailed node initialization state.
339 * 290 *
340 * @param node cordvtn node 291 * @param node cordvtn node
...@@ -392,67 +343,96 @@ public class CordVtnNodeManager { ...@@ -392,67 +343,96 @@ public class CordVtnNodeManager {
392 * @return list of nodes 343 * @return list of nodes
393 */ 344 */
394 public List<CordVtnNode> getNodes() { 345 public List<CordVtnNode> getNodes() {
395 - return nodeStore.values().stream() 346 + return nodeStore.values().stream().map(Versioned::value).collect(Collectors.toList());
396 - .map(Versioned::value)
397 - .collect(Collectors.toList());
398 } 347 }
399 348
400 /** 349 /**
401 - * Returns cordvtn node associated with a given OVSDB device. 350 + * Returns all nodes in complete state.
402 * 351 *
403 - * @param ovsdbId OVSDB device id 352 + * @return set of nodes
404 - * @return cordvtn node, null if it fails to find the node
405 */ 353 */
406 - private CordVtnNode getNodeByOvsdbId(DeviceId ovsdbId) { 354 + public Set<CordVtnNode> completeNodes() {
407 - return getNodes().stream() 355 + return getNodes().stream().filter(this::isNodeInitComplete).collect(Collectors.toSet());
408 - .filter(node -> node.ovsdbId().equals(ovsdbId)) 356 + }
357 +
358 + /**
359 + * Returns physical data plane port number of a given device.
360 + *
361 + * @param deviceId integration bridge device id
362 + * @return port number; null otherwise
363 + */
364 + public PortNumber dpPort(DeviceId deviceId) {
365 + CordVtnNode node = nodeByBridgeId(deviceId);
366 + if (node == null) {
367 + log.warn("Failed to get node for {}", deviceId);
368 + return null;
369 + }
370 + Port port = deviceService.getPorts(deviceId).stream()
371 + .filter(p -> portName(p).contains(node.dpIntf()) &&
372 + p.isEnabled())
409 .findFirst().orElse(null); 373 .findFirst().orElse(null);
374 +
375 + return port == null ? null : port.number();
410 } 376 }
411 377
412 /** 378 /**
413 - * Returns cordvtn node associated with a given integration bridge. 379 + * Returns physical data plane IP address of a given device.
414 * 380 *
415 - * @param bridgeId device id of integration bridge 381 + * @param deviceId integration bridge device id
416 - * @return cordvtn node, null if it fails to find the node 382 + * @return ip address; null otherwise
417 */ 383 */
418 - private CordVtnNode getNodeByBridgeId(DeviceId bridgeId) { 384 + public IpAddress dpIp(DeviceId deviceId) {
419 - return getNodes().stream() 385 + CordVtnNode node = nodeByBridgeId(deviceId);
420 - .filter(node -> node.intBrId().equals(bridgeId)) 386 + if (node == null) {
387 + log.warn("Failed to get node for {}", deviceId);
388 + return null;
389 + }
390 + return node.dpIp().ip();
391 + }
392 +
393 + /**
394 + * Returns tunnel port number of a given device.
395 + *
396 + * @param deviceId integration bridge device id
397 + * @return port number
398 + */
399 + public PortNumber tunnelPort(DeviceId deviceId) {
400 + Port port = deviceService.getPorts(deviceId).stream()
401 + .filter(p -> portName(p).contains(DEFAULT_TUNNEL))
421 .findFirst().orElse(null); 402 .findFirst().orElse(null);
403 +
404 + return port == null ? null : port.number();
422 } 405 }
423 406
424 /** 407 /**
425 - * Sets a new state for a given cordvtn node. 408 + * Returns if current node state saved in nodeStore is COMPLETE or not.
426 * 409 *
427 * @param node cordvtn node 410 * @param node cordvtn node
428 - * @param newState new node state 411 + * @return true if it's complete state, otherwise false
429 */ 412 */
430 - private void setNodeState(CordVtnNode node, NodeState newState) { 413 + private boolean isNodeStateComplete(CordVtnNode node) {
431 checkNotNull(node); 414 checkNotNull(node);
432 415
433 - log.debug("Changed {} state: {}", node.hostname(), newState); 416 + // the state saved in nodeStore can be wrong if IP address settings are changed
434 - nodeStore.put(node.hostname(), CordVtnNode.getUpdatedNode(node, newState)); 417 + // after the node init has been completed since there's no way to detect it
418 + // getNodeState and checkNodeInitState always return correct answer but can be slow
419 + Versioned<CordVtnNode> versionedNode = nodeStore.get(node.hostname());
420 + CordVtnNodeState state = versionedNode.value().state();
421 + return state != null && state.equals(NodeState.COMPLETE);
435 } 422 }
436 423
437 /** 424 /**
438 - * Checks current state of a given cordvtn node and returns it. 425 + * Initiates node to serve virtual tenant network.
439 * 426 *
440 * @param node cordvtn node 427 * @param node cordvtn node
441 - * @return node state
442 */ 428 */
443 - private NodeState getNodeState(CordVtnNode node) { 429 + private void initNode(CordVtnNode node) {
444 checkNotNull(node); 430 checkNotNull(node);
445 431
446 - if (isBrIntCreated(node) && isTunnelIntfCreated(node) && 432 + NodeState state = (NodeState) node.state();
447 - isDataPlaneIntfAdded(node) && isIpAddressSet(node)) { 433 + log.debug("Processing node: {} state: {}", node.hostname(), state);
448 - return NodeState.COMPLETE; 434 +
449 - } else if (isDataPlaneIntfAdded(node) && isTunnelIntfCreated(node)) { 435 + state.process(this, node);
450 - return NodeState.PORTS_ADDED;
451 - } else if (isBrIntCreated(node)) {
452 - return NodeState.BRIDGE_CREATED;
453 - } else {
454 - return NodeState.INIT;
455 - }
456 } 436 }
457 437
458 /** 438 /**
...@@ -464,20 +444,17 @@ public class CordVtnNodeManager { ...@@ -464,20 +444,17 @@ public class CordVtnNodeManager {
464 */ 444 */
465 private void postInit(CordVtnNode node) { 445 private void postInit(CordVtnNode node) {
466 disconnectOvsdb(node); 446 disconnectOvsdb(node);
447 + pipeline.initPipeline(node, dpPort(node.intBrId()), tunnelPort(node.intBrId()));
467 448
468 - ruleInstaller.init(node.intBrId(), node.dpIntf(), node.dpIp().ip());
469 -
470 - // add existing hosts to the service
471 deviceService.getPorts(node.intBrId()).stream() 449 deviceService.getPorts(node.intBrId()).stream()
472 - .filter(port -> getPortName(port).startsWith(VPORT_PREFIX) && 450 + .filter(port -> portName(port).startsWith(VPORT_PREFIX) &&
473 port.isEnabled()) 451 port.isEnabled())
474 - .forEach(port -> cordVtnService.addServiceVm(node, getConnectPoint(port))); 452 + .forEach(port -> instanceManager.addInstance(connectPoint(port)));
475 453
476 - // remove stale hosts from the service
477 hostService.getHosts().forEach(host -> { 454 hostService.getHosts().forEach(host -> {
478 - Port port = deviceService.getPort(host.location().deviceId(), host.location().port()); 455 + if (deviceService.getPort(host.location().deviceId(),
479 - if (port == null) { 456 + host.location().port()) == null) {
480 - cordVtnService.removeServiceVm(getConnectPoint(host)); 457 + instanceManager.removeInstance(connectPoint(host));
481 } 458 }
482 }); 459 });
483 460
...@@ -485,13 +462,37 @@ public class CordVtnNodeManager { ...@@ -485,13 +462,37 @@ public class CordVtnNodeManager {
485 } 462 }
486 463
487 /** 464 /**
488 - * Returns port name. 465 + * Sets a new state for a given cordvtn node.
489 * 466 *
490 - * @param port port 467 + * @param node cordvtn node
491 - * @return port name 468 + * @param newState new node state
492 */ 469 */
493 - private String getPortName(Port port) { 470 + private void setNodeState(CordVtnNode node, NodeState newState) {
494 - return port.annotations().value("portName"); 471 + checkNotNull(node);
472 +
473 + log.debug("Changed {} state: {}", node.hostname(), newState);
474 + nodeStore.put(node.hostname(), CordVtnNode.getUpdatedNode(node, newState));
475 + }
476 +
477 + /**
478 + * Checks current state of a given cordvtn node and returns it.
479 + *
480 + * @param node cordvtn node
481 + * @return node state
482 + */
483 + private NodeState getNodeState(CordVtnNode node) {
484 + checkNotNull(node);
485 +
486 + if (isBrIntCreated(node) && isTunnelIntfCreated(node) &&
487 + isDataPlaneIntfAdded(node) && isIpAddressSet(node)) {
488 + return NodeState.COMPLETE;
489 + } else if (isDataPlaneIntfAdded(node) && isTunnelIntfCreated(node)) {
490 + return NodeState.PORTS_ADDED;
491 + } else if (isBrIntCreated(node)) {
492 + return NodeState.BRIDGE_CREATED;
493 + } else {
494 + return NodeState.INIT;
495 + }
495 } 496 }
496 497
497 /** 498 /**
...@@ -587,7 +588,7 @@ public class CordVtnNodeManager { ...@@ -587,7 +588,7 @@ public class CordVtnNodeManager {
587 BridgeConfig bridgeConfig = device.as(BridgeConfig.class); 588 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
588 bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE), dpid, controllers); 589 bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE), dpid, controllers);
589 } else { 590 } else {
590 - log.warn("The bridging behaviour is not supported in device {}", device.id().toString()); 591 + log.warn("The bridging behaviour is not supported in device {}", device.id());
591 } 592 }
592 } catch (ItemNotFoundException e) { 593 } catch (ItemNotFoundException e) {
593 log.warn("Failed to create integration bridge on {}", node.hostname()); 594 log.warn("Failed to create integration bridge on {}", node.hostname());
...@@ -619,7 +620,7 @@ public class CordVtnNodeManager { ...@@ -619,7 +620,7 @@ public class CordVtnNodeManager {
619 TunnelConfig tunnelConfig = device.as(TunnelConfig.class); 620 TunnelConfig tunnelConfig = device.as(TunnelConfig.class);
620 tunnelConfig.createTunnelInterface(BridgeName.bridgeName(DEFAULT_BRIDGE), description); 621 tunnelConfig.createTunnelInterface(BridgeName.bridgeName(DEFAULT_BRIDGE), description);
621 } else { 622 } else {
622 - log.warn("The tunneling behaviour is not supported in device {}", device.id().toString()); 623 + log.warn("The tunneling behaviour is not supported in device {}", device.id());
623 } 624 }
624 } catch (ItemNotFoundException e) { 625 } catch (ItemNotFoundException e) {
625 log.warn("Failed to create tunnel interface on {}", node.hostname()); 626 log.warn("Failed to create tunnel interface on {}", node.hostname());
...@@ -654,7 +655,7 @@ public class CordVtnNodeManager { ...@@ -654,7 +655,7 @@ public class CordVtnNodeManager {
654 BridgeConfig bridgeConfig = device.as(BridgeConfig.class); 655 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
655 bridgeConfig.addPort(BridgeName.bridgeName(DEFAULT_BRIDGE), node.dpIntf()); 656 bridgeConfig.addPort(BridgeName.bridgeName(DEFAULT_BRIDGE), node.dpIntf());
656 } else { 657 } else {
657 - log.warn("The bridging behaviour is not supported in device {}", device.id().toString()); 658 + log.warn("The bridging behaviour is not supported in device {}", device.id());
658 } 659 }
659 } catch (ItemNotFoundException e) { 660 } catch (ItemNotFoundException e) {
660 log.warn("Failed to add {} on {}", node.dpIntf(), node.hostname()); 661 log.warn("Failed to add {} on {}", node.dpIntf(), node.hostname());
...@@ -712,7 +713,7 @@ public class CordVtnNodeManager { ...@@ -712,7 +713,7 @@ public class CordVtnNodeManager {
712 private boolean isTunnelIntfCreated(CordVtnNode node) { 713 private boolean isTunnelIntfCreated(CordVtnNode node) {
713 return deviceService.getPorts(node.intBrId()) 714 return deviceService.getPorts(node.intBrId())
714 .stream() 715 .stream()
715 - .filter(p -> getPortName(p).contains(DEFAULT_TUNNEL) && 716 + .filter(p -> portName(p).contains(DEFAULT_TUNNEL) &&
716 p.isEnabled()) 717 p.isEnabled())
717 .findAny().isPresent(); 718 .findAny().isPresent();
718 } 719 }
...@@ -726,7 +727,7 @@ public class CordVtnNodeManager { ...@@ -726,7 +727,7 @@ public class CordVtnNodeManager {
726 private boolean isDataPlaneIntfAdded(CordVtnNode node) { 727 private boolean isDataPlaneIntfAdded(CordVtnNode node) {
727 return deviceService.getPorts(node.intBrId()) 728 return deviceService.getPorts(node.intBrId())
728 .stream() 729 .stream()
729 - .filter(p -> getPortName(p).contains(node.dpIntf()) && 730 + .filter(p -> portName(p).contains(node.dpIntf()) &&
730 p.isEnabled()) 731 p.isEnabled())
731 .findAny().isPresent(); 732 .findAny().isPresent();
732 } 733 }
...@@ -761,7 +762,7 @@ public class CordVtnNodeManager { ...@@ -761,7 +762,7 @@ public class CordVtnNodeManager {
761 * @param port port 762 * @param port port
762 * @return connect point 763 * @return connect point
763 */ 764 */
764 - private ConnectPoint getConnectPoint(Port port) { 765 + private ConnectPoint connectPoint(Port port) {
765 return new ConnectPoint(port.element().id(), port.number()); 766 return new ConnectPoint(port.element().id(), port.number());
766 } 767 }
767 768
...@@ -771,15 +772,49 @@ public class CordVtnNodeManager { ...@@ -771,15 +772,49 @@ public class CordVtnNodeManager {
771 * @param host host 772 * @param host host
772 * @return connect point 773 * @return connect point
773 */ 774 */
774 - private ConnectPoint getConnectPoint(Host host) { 775 + private ConnectPoint connectPoint(Host host) {
775 return new ConnectPoint(host.location().deviceId(), host.location().port()); 776 return new ConnectPoint(host.location().deviceId(), host.location().port());
776 } 777 }
777 778
779 + /**
780 + * Returns cordvtn node associated with a given OVSDB device.
781 + *
782 + * @param ovsdbId OVSDB device id
783 + * @return cordvtn node, null if it fails to find the node
784 + */
785 + private CordVtnNode nodeByOvsdbId(DeviceId ovsdbId) {
786 + return getNodes().stream()
787 + .filter(node -> node.ovsdbId().equals(ovsdbId))
788 + .findFirst().orElse(null);
789 + }
790 +
791 + /**
792 + * Returns cordvtn node associated with a given integration bridge.
793 + *
794 + * @param bridgeId device id of integration bridge
795 + * @return cordvtn node, null if it fails to find the node
796 + */
797 + private CordVtnNode nodeByBridgeId(DeviceId bridgeId) {
798 + return getNodes().stream()
799 + .filter(node -> node.intBrId().equals(bridgeId))
800 + .findFirst().orElse(null);
801 + }
802 +
803 + /**
804 + * Returns port name.
805 + *
806 + * @param port port
807 + * @return port name
808 + */
809 + private String portName(Port port) {
810 + return port.annotations().value("portName");
811 + }
812 +
778 private class OvsdbHandler implements ConnectionHandler<Device> { 813 private class OvsdbHandler implements ConnectionHandler<Device> {
779 814
780 @Override 815 @Override
781 public void connected(Device device) { 816 public void connected(Device device) {
782 - CordVtnNode node = getNodeByOvsdbId(device.id()); 817 + CordVtnNode node = nodeByOvsdbId(device.id());
783 if (node != null) { 818 if (node != null) {
784 setNodeState(node, getNodeState(node)); 819 setNodeState(node, getNodeState(node));
785 } else { 820 } else {
...@@ -800,7 +835,7 @@ public class CordVtnNodeManager { ...@@ -800,7 +835,7 @@ public class CordVtnNodeManager {
800 835
801 @Override 836 @Override
802 public void connected(Device device) { 837 public void connected(Device device) {
803 - CordVtnNode node = getNodeByBridgeId(device.id()); 838 + CordVtnNode node = nodeByBridgeId(device.id());
804 if (node != null) { 839 if (node != null) {
805 setNodeState(node, getNodeState(node)); 840 setNodeState(node, getNodeState(node));
806 } else { 841 } else {
...@@ -810,7 +845,7 @@ public class CordVtnNodeManager { ...@@ -810,7 +845,7 @@ public class CordVtnNodeManager {
810 845
811 @Override 846 @Override
812 public void disconnected(Device device) { 847 public void disconnected(Device device) {
813 - CordVtnNode node = getNodeByBridgeId(device.id()); 848 + CordVtnNode node = nodeByBridgeId(device.id());
814 if (node != null) { 849 if (node != null) {
815 log.debug("Integration Bridge is disconnected from {}", node.hostname()); 850 log.debug("Integration Bridge is disconnected from {}", node.hostname());
816 setNodeState(node, NodeState.INCOMPLETE); 851 setNodeState(node, NodeState.INCOMPLETE);
...@@ -825,8 +860,8 @@ public class CordVtnNodeManager { ...@@ -825,8 +860,8 @@ public class CordVtnNodeManager {
825 * @param port port 860 * @param port port
826 */ 861 */
827 public void portAdded(Port port) { 862 public void portAdded(Port port) {
828 - CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id()); 863 + CordVtnNode node = nodeByBridgeId((DeviceId) port.element().id());
829 - String portName = getPortName(port); 864 + String portName = portName(port);
830 865
831 if (node == null) { 866 if (node == null) {
832 log.debug("{} is added to unregistered node, ignore it.", portName); 867 log.debug("{} is added to unregistered node, ignore it.", portName);
...@@ -837,7 +872,7 @@ public class CordVtnNodeManager { ...@@ -837,7 +872,7 @@ public class CordVtnNodeManager {
837 872
838 if (portName.startsWith(VPORT_PREFIX)) { 873 if (portName.startsWith(VPORT_PREFIX)) {
839 if (isNodeStateComplete(node)) { 874 if (isNodeStateComplete(node)) {
840 - cordVtnService.addServiceVm(node, getConnectPoint(port)); 875 + instanceManager.addInstance(connectPoint(port));
841 } else { 876 } else {
842 log.debug("VM is detected on incomplete node, ignore it.", portName); 877 log.debug("VM is detected on incomplete node, ignore it.", portName);
843 } 878 }
...@@ -854,8 +889,8 @@ public class CordVtnNodeManager { ...@@ -854,8 +889,8 @@ public class CordVtnNodeManager {
854 * @param port port 889 * @param port port
855 */ 890 */
856 public void portRemoved(Port port) { 891 public void portRemoved(Port port) {
857 - CordVtnNode node = getNodeByBridgeId((DeviceId) port.element().id()); 892 + CordVtnNode node = nodeByBridgeId((DeviceId) port.element().id());
858 - String portName = getPortName(port); 893 + String portName = portName(port);
859 894
860 if (node == null) { 895 if (node == null) {
861 return; 896 return;
...@@ -865,7 +900,7 @@ public class CordVtnNodeManager { ...@@ -865,7 +900,7 @@ public class CordVtnNodeManager {
865 900
866 if (portName.startsWith(VPORT_PREFIX)) { 901 if (portName.startsWith(VPORT_PREFIX)) {
867 if (isNodeStateComplete(node)) { 902 if (isNodeStateComplete(node)) {
868 - cordVtnService.removeServiceVm(getConnectPoint(port)); 903 + instanceManager.removeInstance(connectPoint(port));
869 } else { 904 } else {
870 log.debug("VM is vanished from incomplete node, ignore it.", portName); 905 log.debug("VM is vanished from incomplete node, ignore it.", portName);
871 } 906 }
...@@ -922,7 +957,6 @@ public class CordVtnNodeManager { ...@@ -922,7 +957,6 @@ public class CordVtnNodeManager {
922 log.debug("No configuration found"); 957 log.debug("No configuration found");
923 return; 958 return;
924 } 959 }
925 -
926 config.cordVtnNodes().forEach(this::addOrUpdateNode); 960 config.cordVtnNodes().forEach(this::addOrUpdateNode);
927 } 961 }
928 962
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.cordvtn.impl;
17 +
18 +import org.apache.felix.scr.annotations.Activate;
19 +import org.apache.felix.scr.annotations.Component;
20 +import org.apache.felix.scr.annotations.Deactivate;
21 +import org.apache.felix.scr.annotations.Reference;
22 +import org.apache.felix.scr.annotations.ReferenceCardinality;
23 +import org.apache.felix.scr.annotations.Service;
24 +import org.onlab.packet.Ethernet;
25 +import org.onlab.packet.IPv4;
26 +import org.onlab.packet.Ip4Address;
27 +import org.onlab.packet.IpAddress;
28 +import org.onlab.packet.TpPort;
29 +import org.onlab.packet.VlanId;
30 +import org.onlab.util.ItemNotFoundException;
31 +import org.onosproject.cordvtn.api.CordVtnNode;
32 +import org.onosproject.cordvtn.api.CordVtnService;
33 +import org.onosproject.core.ApplicationId;
34 +import org.onosproject.core.CoreService;
35 +import org.onosproject.net.Device;
36 +import org.onosproject.net.DeviceId;
37 +import org.onosproject.net.PortNumber;
38 +import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
39 +import org.onosproject.net.device.DeviceService;
40 +import org.onosproject.net.flow.DefaultFlowRule;
41 +import org.onosproject.net.flow.DefaultTrafficSelector;
42 +import org.onosproject.net.flow.DefaultTrafficTreatment;
43 +import org.onosproject.net.flow.FlowRule;
44 +import org.onosproject.net.flow.FlowRuleOperations;
45 +import org.onosproject.net.flow.FlowRuleOperationsContext;
46 +import org.onosproject.net.flow.FlowRuleService;
47 +import org.onosproject.net.flow.TrafficSelector;
48 +import org.onosproject.net.flow.TrafficTreatment;
49 +import org.onosproject.net.flow.instructions.ExtensionPropertyException;
50 +import org.onosproject.net.flow.instructions.ExtensionTreatment;
51 +import org.slf4j.Logger;
52 +
53 +import static com.google.common.base.Preconditions.checkNotNull;
54 +import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
55 +import static org.slf4j.LoggerFactory.getLogger;
56 +
57 +/**
58 + * Provides CORD VTN pipeline.
59 + */
60 +@Component(immediate = true)
61 +@Service(value = CordVtnPipeline.class)
62 +public final class CordVtnPipeline {
63 +
64 + protected final Logger log = getLogger(getClass());
65 +
66 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
67 + protected CoreService coreService;
68 +
69 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
70 + protected FlowRuleService flowRuleService;
71 +
72 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
73 + protected DeviceService deviceService;
74 +
75 + // tables
76 + public static final int TABLE_ZERO = 0;
77 + public static final int TABLE_IN_PORT = 1;
78 + public static final int TABLE_ACCESS_TYPE = 2;
79 + public static final int TABLE_IN_SERVICE = 3;
80 + public static final int TABLE_DST_IP = 4;
81 + public static final int TABLE_TUNNEL_IN = 5;
82 + public static final int TABLE_VLAN = 6;
83 +
84 + // priorities
85 + public static final int PRIORITY_MANAGEMENT = 55000;
86 + public static final int PRIORITY_HIGH = 50000;
87 + public static final int PRIORITY_DEFAULT = 5000;
88 + public static final int PRIORITY_LOW = 4000;
89 + public static final int PRIORITY_ZERO = 0;
90 +
91 + public static final int VXLAN_UDP_PORT = 4789;
92 + public static final VlanId VLAN_WAN = VlanId.vlanId((short) 500);
93 + public static final String DEFAULT_TUNNEL = "vxlan";
94 + private static final String PORT_NAME = "portName";
95 +
96 + private ApplicationId appId;
97 +
98 + @Activate
99 + protected void activate() {
100 + appId = coreService.registerApplication(CordVtnService.CORDVTN_APP_ID);
101 + log.info("Started");
102 + }
103 +
104 + @Deactivate
105 + protected void deactivate() {
106 + log.info("Stopped");
107 + }
108 +
109 + /**
110 + * Flush flows installed by this application.
111 + */
112 + public void flushRules() {
113 + flowRuleService.getFlowRulesById(appId).forEach(flowRule -> processFlowRule(false, flowRule));
114 + }
115 +
116 + /**
117 + * Installs table miss rule to a give device.
118 + *
119 + * @param node cordvtn node
120 + * @param dpPort data plane port number
121 + * @param tunnelPort tunnel port number
122 + */
123 + public void initPipeline(CordVtnNode node, PortNumber dpPort, PortNumber tunnelPort) {
124 + checkNotNull(node);
125 +
126 + processTableZero(node.intBrId(), dpPort, node.dpIp().ip());
127 + processInPortTable(node.intBrId(), tunnelPort, dpPort);
128 + processAccessTypeTable(node.intBrId(), dpPort);
129 + processVlanTable(node.intBrId(), dpPort);
130 + }
131 +
132 + private void processTableZero(DeviceId deviceId, PortNumber dpPort, IpAddress dpIp) {
133 + // take vxlan packet out onto the physical port
134 + TrafficSelector selector = DefaultTrafficSelector.builder()
135 + .matchInPort(PortNumber.LOCAL)
136 + .build();
137 +
138 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
139 + .setOutput(dpPort)
140 + .build();
141 +
142 + FlowRule flowRule = DefaultFlowRule.builder()
143 + .fromApp(appId)
144 + .withSelector(selector)
145 + .withTreatment(treatment)
146 + .withPriority(PRIORITY_HIGH)
147 + .forDevice(deviceId)
148 + .forTable(TABLE_ZERO)
149 + .makePermanent()
150 + .build();
151 +
152 + processFlowRule(true, flowRule);
153 +
154 + // take a vxlan encap'd packet through the Linux stack
155 + selector = DefaultTrafficSelector.builder()
156 + .matchInPort(dpPort)
157 + .matchEthType(Ethernet.TYPE_IPV4)
158 + .matchIPProtocol(IPv4.PROTOCOL_UDP)
159 + .matchUdpDst(TpPort.tpPort(VXLAN_UDP_PORT))
160 + .build();
161 +
162 + treatment = DefaultTrafficTreatment.builder()
163 + .setOutput(PortNumber.LOCAL)
164 + .build();
165 +
166 + flowRule = DefaultFlowRule.builder()
167 + .fromApp(appId)
168 + .withSelector(selector)
169 + .withTreatment(treatment)
170 + .withPriority(PRIORITY_HIGH)
171 + .forDevice(deviceId)
172 + .forTable(TABLE_ZERO)
173 + .makePermanent()
174 + .build();
175 +
176 + processFlowRule(true, flowRule);
177 +
178 + // take a packet to the data plane ip through Linux stack
179 + selector = DefaultTrafficSelector.builder()
180 + .matchInPort(dpPort)
181 + .matchEthType(Ethernet.TYPE_IPV4)
182 + .matchIPDst(dpIp.toIpPrefix())
183 + .build();
184 +
185 + treatment = DefaultTrafficTreatment.builder()
186 + .setOutput(PortNumber.LOCAL)
187 + .build();
188 +
189 + flowRule = DefaultFlowRule.builder()
190 + .fromApp(appId)
191 + .withSelector(selector)
192 + .withTreatment(treatment)
193 + .withPriority(PRIORITY_HIGH)
194 + .forDevice(deviceId)
195 + .forTable(TABLE_ZERO)
196 + .makePermanent()
197 + .build();
198 +
199 + processFlowRule(true, flowRule);
200 +
201 + // take an arp packet from physical through Linux stack
202 + selector = DefaultTrafficSelector.builder()
203 + .matchInPort(dpPort)
204 + .matchEthType(Ethernet.TYPE_ARP)
205 + .matchArpTpa(dpIp.getIp4Address())
206 + .build();
207 +
208 + treatment = DefaultTrafficTreatment.builder()
209 + .setOutput(PortNumber.LOCAL)
210 + .build();
211 +
212 + flowRule = DefaultFlowRule.builder()
213 + .fromApp(appId)
214 + .withSelector(selector)
215 + .withTreatment(treatment)
216 + .withPriority(PRIORITY_HIGH)
217 + .forDevice(deviceId)
218 + .forTable(TABLE_ZERO)
219 + .makePermanent()
220 + .build();
221 +
222 + processFlowRule(true, flowRule);
223 +
224 + // take all else to the next table
225 + selector = DefaultTrafficSelector.builder()
226 + .build();
227 +
228 + treatment = DefaultTrafficTreatment.builder()
229 + .transition(TABLE_IN_PORT)
230 + .build();
231 +
232 + flowRule = DefaultFlowRule.builder()
233 + .fromApp(appId)
234 + .withSelector(selector)
235 + .withTreatment(treatment)
236 + .withPriority(PRIORITY_ZERO)
237 + .forDevice(deviceId)
238 + .forTable(TABLE_ZERO)
239 + .makePermanent()
240 + .build();
241 +
242 + processFlowRule(true, flowRule);
243 +
244 + // take all vlan tagged packet to the VLAN table
245 + selector = DefaultTrafficSelector.builder()
246 + .matchVlanId(VlanId.ANY)
247 + .build();
248 +
249 + treatment = DefaultTrafficTreatment.builder()
250 + .transition(TABLE_VLAN)
251 + .build();
252 +
253 + flowRule = DefaultFlowRule.builder()
254 + .fromApp(appId)
255 + .withSelector(selector)
256 + .withTreatment(treatment)
257 + .withPriority(PRIORITY_MANAGEMENT)
258 + .forDevice(deviceId)
259 + .forTable(TABLE_ZERO)
260 + .makePermanent()
261 + .build();
262 +
263 + processFlowRule(true, flowRule);
264 + }
265 +
266 + private void processInPortTable(DeviceId deviceId, PortNumber tunnelPort, PortNumber dpPort) {
267 + checkNotNull(tunnelPort);
268 +
269 + TrafficSelector selector = DefaultTrafficSelector.builder()
270 + .matchInPort(tunnelPort)
271 + .build();
272 +
273 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
274 + .transition(TABLE_TUNNEL_IN)
275 + .build();
276 +
277 + FlowRule flowRule = DefaultFlowRule.builder()
278 + .fromApp(appId)
279 + .withSelector(selector)
280 + .withTreatment(treatment)
281 + .withPriority(PRIORITY_DEFAULT)
282 + .forDevice(deviceId)
283 + .forTable(TABLE_IN_PORT)
284 + .makePermanent()
285 + .build();
286 +
287 + processFlowRule(true, flowRule);
288 +
289 + selector = DefaultTrafficSelector.builder()
290 + .matchInPort(dpPort)
291 + .build();
292 +
293 + treatment = DefaultTrafficTreatment.builder()
294 + .transition(TABLE_DST_IP)
295 + .build();
296 +
297 + flowRule = DefaultFlowRule.builder()
298 + .fromApp(appId)
299 + .withSelector(selector)
300 + .withTreatment(treatment)
301 + .withPriority(PRIORITY_DEFAULT)
302 + .forDevice(deviceId)
303 + .forTable(TABLE_IN_PORT)
304 + .makePermanent()
305 + .build();
306 +
307 + processFlowRule(true, flowRule);
308 + }
309 +
310 + private void processAccessTypeTable(DeviceId deviceId, PortNumber dpPort) {
311 + TrafficSelector selector = DefaultTrafficSelector.builder()
312 + .build();
313 +
314 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
315 + .setOutput(dpPort)
316 + .build();
317 +
318 + FlowRule flowRule = DefaultFlowRule.builder()
319 + .fromApp(appId)
320 + .withSelector(selector)
321 + .withTreatment(treatment)
322 + .withPriority(PRIORITY_ZERO)
323 + .forDevice(deviceId)
324 + .forTable(TABLE_ACCESS_TYPE)
325 + .makePermanent()
326 + .build();
327 +
328 + processFlowRule(true, flowRule);
329 + }
330 +
331 + private void processVlanTable(DeviceId deviceId, PortNumber dpPort) {
332 + // for traffic going out to WAN, strip vid 500 and take through data plane interface
333 + TrafficSelector selector = DefaultTrafficSelector.builder()
334 + .matchVlanId(VLAN_WAN)
335 + .build();
336 +
337 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
338 + .popVlan()
339 + .setOutput(dpPort)
340 + .build();
341 +
342 + FlowRule flowRule = DefaultFlowRule.builder()
343 + .fromApp(appId)
344 + .withSelector(selector)
345 + .withTreatment(treatment)
346 + .withPriority(PRIORITY_DEFAULT)
347 + .forDevice(deviceId)
348 + .forTable(TABLE_VLAN)
349 + .makePermanent()
350 + .build();
351 +
352 + processFlowRule(true, flowRule);
353 +
354 + selector = DefaultTrafficSelector.builder()
355 + .matchVlanId(VLAN_WAN)
356 + .matchEthType(Ethernet.TYPE_ARP)
357 + .build();
358 +
359 + treatment = DefaultTrafficTreatment.builder()
360 + .setOutput(PortNumber.CONTROLLER)
361 + .build();
362 +
363 + flowRule = DefaultFlowRule.builder()
364 + .fromApp(appId)
365 + .withSelector(selector)
366 + .withTreatment(treatment)
367 + .withPriority(PRIORITY_HIGH)
368 + .forDevice(deviceId)
369 + .forTable(TABLE_VLAN)
370 + .makePermanent()
371 + .build();
372 +
373 + processFlowRule(true, flowRule);
374 + }
375 +
376 + public void processFlowRule(boolean install, FlowRule rule) {
377 + FlowRuleOperations.Builder oBuilder = FlowRuleOperations.builder();
378 + oBuilder = install ? oBuilder.add(rule) : oBuilder.remove(rule);
379 +
380 + flowRuleService.apply(oBuilder.build(new FlowRuleOperationsContext() {
381 + @Override
382 + public void onError(FlowRuleOperations ops) {
383 + log.error(String.format("Failed %s, %s", ops.toString(), rule.toString()));
384 + }
385 + }));
386 + }
387 +
388 + public ExtensionTreatment tunnelDstTreatment(DeviceId deviceId, Ip4Address remoteIp) {
389 + try {
390 + Device device = deviceService.getDevice(deviceId);
391 +
392 + if (device.is(ExtensionTreatmentResolver.class)) {
393 + ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
394 + ExtensionTreatment treatment =
395 + resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
396 + treatment.setPropertyValue("tunnelDst", remoteIp);
397 + return treatment;
398 + } else {
399 + log.warn("The extension treatment resolving behaviour is not supported in device {}",
400 + device.id().toString());
401 + return null;
402 + }
403 + } catch (ItemNotFoundException | UnsupportedOperationException |
404 + ExtensionPropertyException e) {
405 + log.error("Failed to get extension instruction {}", deviceId);
406 + return null;
407 + }
408 + }
409 +}
1 -/*
2 - * Copyright 2015-present Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -package org.onosproject.cordvtn.impl;
17 -
18 -import com.google.common.collect.Lists;
19 -import com.google.common.collect.Maps;
20 -import com.google.common.collect.Sets;
21 -import org.onlab.packet.Ethernet;
22 -import org.onlab.packet.IPv4;
23 -import org.onlab.packet.Ip4Address;
24 -import org.onlab.packet.Ip4Prefix;
25 -import org.onlab.packet.IpAddress;
26 -import org.onlab.packet.IpPrefix;
27 -import org.onlab.packet.MacAddress;
28 -import org.onlab.packet.TpPort;
29 -import org.onlab.packet.VlanId;
30 -import org.onlab.util.ItemNotFoundException;
31 -import org.onosproject.cordvtn.api.CordVtnConfig;
32 -import org.onosproject.cordvtn.api.CordVtnNode;
33 -import org.onosproject.core.ApplicationId;
34 -import org.onosproject.core.DefaultGroupId;
35 -import org.onosproject.core.GroupId;
36 -import org.onosproject.net.Device;
37 -import org.onosproject.net.DeviceId;
38 -import org.onosproject.net.Host;
39 -import org.onosproject.net.Port;
40 -import org.onosproject.net.PortNumber;
41 -import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
42 -import org.onosproject.net.config.NetworkConfigRegistry;
43 -import org.onosproject.net.device.DeviceService;
44 -import org.onosproject.net.flow.DefaultFlowRule;
45 -import org.onosproject.net.flow.DefaultTrafficSelector;
46 -import org.onosproject.net.flow.DefaultTrafficTreatment;
47 -import org.onosproject.net.flow.FlowRule;
48 -import org.onosproject.net.flow.FlowRuleOperations;
49 -import org.onosproject.net.flow.FlowRuleOperationsContext;
50 -import org.onosproject.net.flow.FlowRuleService;
51 -import org.onosproject.net.flow.TrafficSelector;
52 -import org.onosproject.net.flow.TrafficTreatment;
53 -import org.onosproject.net.flow.criteria.Criterion;
54 -import org.onosproject.net.flow.criteria.IPCriterion;
55 -import org.onosproject.net.flow.instructions.ExtensionPropertyException;
56 -import org.onosproject.net.flow.instructions.ExtensionTreatment;
57 -import org.onosproject.net.flow.instructions.Instruction;
58 -import org.onosproject.net.flow.instructions.Instructions;
59 -import org.onosproject.net.flow.instructions.L2ModificationInstruction;
60 -import org.onosproject.net.group.DefaultGroupBucket;
61 -import org.onosproject.net.group.DefaultGroupDescription;
62 -import org.onosproject.net.group.DefaultGroupKey;
63 -import org.onosproject.net.group.Group;
64 -import org.onosproject.net.group.GroupBucket;
65 -import org.onosproject.net.group.GroupBuckets;
66 -import org.onosproject.net.group.GroupDescription;
67 -import org.onosproject.net.group.GroupKey;
68 -import org.onosproject.net.group.GroupService;
69 -import org.onosproject.net.host.HostService;
70 -import org.onosproject.xosclient.api.VtnService;
71 -import org.onosproject.xosclient.api.VtnServiceId;
72 -import org.slf4j.Logger;
73 -
74 -import java.util.ArrayList;
75 -import java.util.List;
76 -import java.util.Map;
77 -import java.util.Objects;
78 -import java.util.Set;
79 -import java.util.stream.Collectors;
80 -import java.util.stream.StreamSupport;
81 -
82 -import static com.google.common.base.Preconditions.checkNotNull;
83 -import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
84 -import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
85 -import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_PUSH;
86 -import static org.slf4j.LoggerFactory.getLogger;
87 -
88 -/**
89 - * Populates rules for CORD VTN service.
90 - */
91 -public class CordVtnRuleInstaller {
92 -
93 - protected final Logger log = getLogger(getClass());
94 -
95 - private static final int TABLE_FIRST = 0;
96 - private static final int TABLE_IN_PORT = 1;
97 - private static final int TABLE_ACCESS_TYPE = 2;
98 - private static final int TABLE_IN_SERVICE = 3;
99 - private static final int TABLE_DST_IP = 4;
100 - private static final int TABLE_TUNNEL_IN = 5;
101 - private static final int TABLE_Q_IN_Q = 6;
102 -
103 - private static final int MANAGEMENT_PRIORITY = 55000;
104 - private static final int VSG_PRIORITY = 55000;
105 - private static final int HIGH_PRIORITY = 50000;
106 - private static final int DEFAULT_PRIORITY = 5000;
107 - private static final int LOW_PRIORITY = 4000;
108 - private static final int LOWEST_PRIORITY = 0;
109 -
110 - private static final int VXLAN_UDP_PORT = 4789;
111 - private static final VlanId VLAN_WAN = VlanId.vlanId((short) 500);
112 -
113 - private static final String PORT_NAME = "portName";
114 - private static final String DATA_PLANE_INTF = "dataPlaneIntf";
115 - private static final String DATA_PLANE_IP = "dataPlaneIp";
116 - private static final String S_TAG = "stag";
117 - private static final String SERVICE_ID = "serviceId";
118 -
119 - private final ApplicationId appId;
120 - private final FlowRuleService flowRuleService;
121 - private final DeviceService deviceService;
122 - private final GroupService groupService;
123 - private final HostService hostService;
124 - private final NetworkConfigRegistry configRegistry;
125 - private final String tunnelType;
126 -
127 - /**
128 - * Creates a new rule populator.
129 - *
130 - * @param appId application id
131 - * @param flowRuleService flow rule service
132 - * @param deviceService device service
133 - * @param groupService group service
134 - * @param configRegistry config registry
135 - * @param tunnelType tunnel type
136 - */
137 - public CordVtnRuleInstaller(ApplicationId appId,
138 - FlowRuleService flowRuleService,
139 - DeviceService deviceService,
140 - GroupService groupService,
141 - HostService hostService,
142 - NetworkConfigRegistry configRegistry,
143 - String tunnelType) {
144 - this.appId = appId;
145 - this.flowRuleService = flowRuleService;
146 - this.deviceService = deviceService;
147 - this.groupService = groupService;
148 - this.hostService = hostService;
149 - this.configRegistry = configRegistry;
150 - this.tunnelType = checkNotNull(tunnelType);
151 - }
152 -
153 - /**
154 - * Installs table miss rule to a give device.
155 - *
156 - * @param deviceId device id to install the rules
157 - * @param dpIntf data plane interface name
158 - * @param dpIp data plane ip address
159 - */
160 - public void init(DeviceId deviceId, String dpIntf, IpAddress dpIp) {
161 - // default is drop packets which can be accomplished without
162 - // a table miss entry for all table.
163 - PortNumber tunnelPort = getTunnelPort(deviceId);
164 - PortNumber dpPort = getDpPort(deviceId, dpIntf);
165 -
166 - processFirstTable(deviceId, dpPort, dpIp);
167 - processInPortTable(deviceId, tunnelPort, dpPort);
168 - processAccessTypeTable(deviceId, dpPort);
169 - processQInQTable(deviceId, dpPort);
170 - }
171 -
172 - /**
173 - * Flush flows installed by this application.
174 - */
175 - public void flushRules() {
176 - flowRuleService.getFlowRulesById(appId).forEach(flowRule -> processFlowRule(false, flowRule));
177 - }
178 -
179 - /**
180 - * Populates basic rules that connect a VM to the other VMs in the system.
181 - *
182 - * @param host host
183 - * @param service cord service
184 - * @param install true to install or false to remove
185 - */
186 - public void populateBasicConnectionRules(Host host, VtnService service, boolean install) {
187 - checkNotNull(host);
188 - checkNotNull(service);
189 -
190 - DeviceId deviceId = host.location().deviceId();
191 - PortNumber inPort = host.location().port();
192 - MacAddress dstMac = host.mac();
193 - IpAddress hostIp = host.ipAddresses().stream().findFirst().get();
194 -
195 - long tunnelId = service.vni();
196 - Ip4Prefix serviceIpRange = service.subnet().getIp4Prefix();
197 -
198 - populateLocalInPortRule(deviceId, inPort, hostIp, install);
199 - populateDstIpRule(deviceId, inPort, dstMac, hostIp, tunnelId, getTunnelIp(host), install);
200 - populateTunnelInRule(deviceId, inPort, dstMac, tunnelId, install);
201 -
202 - if (install) {
203 - populateDirectAccessRule(serviceIpRange, serviceIpRange, true);
204 - populateServiceIsolationRule(serviceIpRange, true);
205 - } else if (getInstances(service.id()).isEmpty()) {
206 - // removes network related rules only if there's no hosts left in this network
207 - populateDirectAccessRule(serviceIpRange, serviceIpRange, false);
208 - populateServiceIsolationRule(serviceIpRange, false);
209 - }
210 - }
211 -
212 - /**
213 - * Creates provider service group and populates service dependency rules.
214 - *
215 - * @param tService tenant cord service
216 - * @param pService provider cord service
217 - * @param isBidirectional true to enable bidirectional connection between two services
218 - * @param install true to install or false to remove
219 - */
220 - public void populateServiceDependencyRules(VtnService tService, VtnService pService,
221 - boolean isBidirectional, boolean install) {
222 - checkNotNull(tService);
223 - checkNotNull(pService);
224 -
225 - Ip4Prefix srcRange = tService.subnet().getIp4Prefix();
226 - Ip4Prefix dstRange = pService.subnet().getIp4Prefix();
227 - Ip4Address serviceIp = pService.serviceIp().getIp4Address();
228 -
229 - Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
230 - Map<DeviceId, Set<PortNumber>> inPorts = Maps.newHashMap();
231 -
232 - getVirtualSwitches().stream().forEach(deviceId -> {
233 - GroupId groupId = createServiceGroup(deviceId, pService);
234 - outGroups.put(deviceId, groupId);
235 -
236 - Set<PortNumber> tServiceVms = getInstances(tService.id())
237 - .stream()
238 - .filter(host -> host.location().deviceId().equals(deviceId))
239 - .map(host -> host.location().port())
240 - .collect(Collectors.toSet());
241 - inPorts.put(deviceId, tServiceVms);
242 - });
243 -
244 - populateIndirectAccessRule(srcRange, serviceIp, outGroups, install);
245 - populateDirectAccessRule(srcRange, dstRange, install);
246 - if (isBidirectional) {
247 - populateDirectAccessRule(dstRange, srcRange, install);
248 - }
249 - populateInServiceRule(inPorts, outGroups, install);
250 - }
251 -
252 - /**
253 - * Updates group buckets for a given service to all devices.
254 - *
255 - * @param service cord service
256 - */
257 - public void updateProviderServiceGroup(VtnService service) {
258 - checkNotNull(service);
259 -
260 - GroupKey groupKey = getGroupKey(service.id());
261 -
262 - for (DeviceId deviceId : getVirtualSwitches()) {
263 - Group group = groupService.getGroup(deviceId, groupKey);
264 - if (group == null) {
265 - log.trace("No group exists for service {} in {}, do nothing.", service.id(), deviceId);
266 - continue;
267 - }
268 -
269 - List<GroupBucket> oldBuckets = group.buckets().buckets();
270 - List<GroupBucket> newBuckets = getServiceGroupBuckets(
271 - deviceId, service.vni(), getInstances(service.id())).buckets();
272 -
273 - if (oldBuckets.equals(newBuckets)) {
274 - continue;
275 - }
276 -
277 - List<GroupBucket> bucketsToRemove = new ArrayList<>(oldBuckets);
278 - bucketsToRemove.removeAll(newBuckets);
279 - if (!bucketsToRemove.isEmpty()) {
280 - groupService.removeBucketsFromGroup(
281 - deviceId,
282 - groupKey,
283 - new GroupBuckets(bucketsToRemove),
284 - groupKey, appId);
285 - }
286 -
287 - List<GroupBucket> bucketsToAdd = new ArrayList<>(newBuckets);
288 - bucketsToAdd.removeAll(oldBuckets);
289 - if (!bucketsToAdd.isEmpty()) {
290 - groupService.addBucketsToGroup(
291 - deviceId,
292 - groupKey,
293 - new GroupBuckets(bucketsToAdd),
294 - groupKey, appId);
295 - }
296 - }
297 - }
298 -
299 - /**
300 - * Updates tenant service indirect access rules when VM is created or removed.
301 - *
302 - * @param host removed vm
303 - * @param service tenant service
304 - */
305 - public void updateTenantServiceVm(Host host, VtnService service) {
306 - checkNotNull(host);
307 - checkNotNull(service);
308 -
309 - DeviceId deviceId = host.location().deviceId();
310 - PortNumber inPort = host.location().port();
311 -
312 - service.providerServices().stream().forEach(pServiceId -> {
313 - Map<DeviceId, Set<PortNumber>> inPorts = Maps.newHashMap();
314 - Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
315 -
316 - inPorts.put(deviceId, Sets.newHashSet(inPort));
317 - outGroups.put(deviceId, getGroupId(pServiceId, deviceId));
318 -
319 - populateInServiceRule(inPorts, outGroups, false);
320 - });
321 - }
322 -
323 - /**
324 - * Populates flow rules for management network access.
325 - *
326 - * @param host host which has management network interface
327 - * @param mService management network service
328 - */
329 - public void populateManagementNetworkRules(Host host, VtnService mService) {
330 - checkNotNull(mService);
331 -
332 - DeviceId deviceId = host.location().deviceId();
333 - IpAddress hostIp = host.ipAddresses().stream().findFirst().get();
334 -
335 - TrafficSelector selector = DefaultTrafficSelector.builder()
336 - .matchEthType(Ethernet.TYPE_ARP)
337 - .matchArpTpa(mService.serviceIp().getIp4Address())
338 - .build();
339 -
340 - TrafficTreatment treatment = DefaultTrafficTreatment.builder()
341 - .setOutput(PortNumber.LOCAL)
342 - .build();
343 -
344 - FlowRule flowRule = DefaultFlowRule.builder()
345 - .fromApp(appId)
346 - .withSelector(selector)
347 - .withTreatment(treatment)
348 - .withPriority(MANAGEMENT_PRIORITY)
349 - .forDevice(deviceId)
350 - .forTable(TABLE_FIRST)
351 - .makePermanent()
352 - .build();
353 -
354 - processFlowRule(true, flowRule);
355 -
356 - selector = DefaultTrafficSelector.builder()
357 - .matchInPort(PortNumber.LOCAL)
358 - .matchEthType(Ethernet.TYPE_ARP)
359 - .matchArpTpa(hostIp.getIp4Address())
360 - .build();
361 -
362 - treatment = DefaultTrafficTreatment.builder()
363 - .setOutput(host.location().port())
364 - .build();
365 -
366 - flowRule = DefaultFlowRule.builder()
367 - .fromApp(appId)
368 - .withSelector(selector)
369 - .withTreatment(treatment)
370 - .withPriority(MANAGEMENT_PRIORITY)
371 - .forDevice(deviceId)
372 - .forTable(TABLE_FIRST)
373 - .makePermanent()
374 - .build();
375 -
376 - processFlowRule(true, flowRule);
377 -
378 - selector = DefaultTrafficSelector.builder()
379 - .matchInPort(PortNumber.LOCAL)
380 - .matchEthType(Ethernet.TYPE_IPV4)
381 - .matchIPDst(mService.subnet())
382 - .build();
383 -
384 - treatment = DefaultTrafficTreatment.builder()
385 - .transition(TABLE_DST_IP)
386 - .build();
387 -
388 - flowRule = DefaultFlowRule.builder()
389 - .fromApp(appId)
390 - .withSelector(selector)
391 - .withTreatment(treatment)
392 - .withPriority(MANAGEMENT_PRIORITY)
393 - .forDevice(deviceId)
394 - .forTable(TABLE_FIRST)
395 - .makePermanent()
396 - .build();
397 -
398 - processFlowRule(true, flowRule);
399 -
400 - selector = DefaultTrafficSelector.builder()
401 - .matchEthType(Ethernet.TYPE_IPV4)
402 - .matchIPDst(mService.serviceIp().toIpPrefix())
403 - .build();
404 -
405 - treatment = DefaultTrafficTreatment.builder()
406 - .setOutput(PortNumber.LOCAL)
407 - .build();
408 -
409 - flowRule = DefaultFlowRule.builder()
410 - .fromApp(appId)
411 - .withSelector(selector)
412 - .withTreatment(treatment)
413 - .withPriority(MANAGEMENT_PRIORITY)
414 - .forDevice(deviceId)
415 - .forTable(TABLE_ACCESS_TYPE)
416 - .makePermanent()
417 - .build();
418 -
419 - processFlowRule(true, flowRule);
420 - }
421 -
422 - /**
423 - * Populates rules for vSG VM.
424 - *
425 - * @param vSgHost vSG host
426 - * @param vSgIps set of ip addresses of vSGs running inside the vSG VM
427 - */
428 - public void populateSubscriberGatewayRules(Host vSgHost, Set<IpAddress> vSgIps) {
429 - VlanId serviceVlan = getServiceVlan(vSgHost);
430 - PortNumber dpPort = getDpPort(vSgHost);
431 -
432 - if (serviceVlan == null || dpPort == null) {
433 - log.warn("Failed to populate rules for vSG VM {}", vSgHost.id());
434 - return;
435 - }
436 -
437 - // for traffics with s-tag, strip the tag and take through the vSG VM
438 - TrafficSelector selector = DefaultTrafficSelector.builder()
439 - .matchInPort(dpPort)
440 - .matchVlanId(serviceVlan)
441 - .build();
442 -
443 - TrafficTreatment treatment = DefaultTrafficTreatment.builder()
444 - .setOutput(vSgHost.location().port())
445 - .build();
446 -
447 - FlowRule flowRule = DefaultFlowRule.builder()
448 - .fromApp(appId)
449 - .withSelector(selector)
450 - .withTreatment(treatment)
451 - .withPriority(DEFAULT_PRIORITY)
452 - .forDevice(vSgHost.location().deviceId())
453 - .forTable(TABLE_Q_IN_Q)
454 - .makePermanent()
455 - .build();
456 -
457 - processFlowRule(true, flowRule);
458 -
459 - // for traffics with customer vlan, tag with the service vlan based on input port with
460 - // lower priority to avoid conflict with WAN tag
461 - selector = DefaultTrafficSelector.builder()
462 - .matchInPort(vSgHost.location().port())
463 - .matchVlanId(serviceVlan)
464 - .build();
465 -
466 - treatment = DefaultTrafficTreatment.builder()
467 - .setOutput(dpPort)
468 - .build();
469 -
470 - flowRule = DefaultFlowRule.builder()
471 - .fromApp(appId)
472 - .withSelector(selector)
473 - .withTreatment(treatment)
474 - .withPriority(DEFAULT_PRIORITY)
475 - .forDevice(vSgHost.location().deviceId())
476 - .forTable(TABLE_Q_IN_Q)
477 - .makePermanent()
478 - .build();
479 -
480 - processFlowRule(true, flowRule);
481 -
482 - // for traffic coming from WAN, tag 500 and take through the vSG VM
483 - // based on destination ip
484 - vSgIps.stream().forEach(ip -> {
485 - TrafficSelector downstream = DefaultTrafficSelector.builder()
486 - .matchEthType(Ethernet.TYPE_IPV4)
487 - .matchIPDst(ip.toIpPrefix())
488 - .build();
489 -
490 - TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
491 - .pushVlan()
492 - .setVlanId(VLAN_WAN)
493 - .setEthDst(vSgHost.mac())
494 - .setOutput(vSgHost.location().port())
495 - .build();
496 -
497 - FlowRule downstreamFlowRule = DefaultFlowRule.builder()
498 - .fromApp(appId)
499 - .withSelector(downstream)
500 - .withTreatment(downstreamTreatment)
501 - .withPriority(DEFAULT_PRIORITY)
502 - .forDevice(vSgHost.location().deviceId())
503 - .forTable(TABLE_DST_IP)
504 - .makePermanent()
505 - .build();
506 -
507 - processFlowRule(true, downstreamFlowRule);
508 - });
509 -
510 - // remove downstream flow rules for the vSG not shown in vSgIps
511 - for (FlowRule rule : flowRuleService.getFlowRulesById(appId)) {
512 - if (!rule.deviceId().equals(vSgHost.location().deviceId())) {
513 - continue;
514 - }
515 - PortNumber output = getOutputFromTreatment(rule);
516 - if (output == null || !output.equals(vSgHost.location().port()) ||
517 - !isVlanPushFromTreatment(rule)) {
518 - continue;
519 - }
520 -
521 - IpPrefix dstIp = getDstIpFromSelector(rule);
522 - if (dstIp != null && !vSgIps.contains(dstIp.address())) {
523 - processFlowRule(false, rule);
524 - }
525 - }
526 - }
527 -
528 - /**
529 - * Populates default rules on the first table.
530 - * It includes the rules for shuttling vxlan-encapped packets between ovs and
531 - * linux stack,and external network connectivity.
532 - *
533 - * @param deviceId device id
534 - * @param dpPort data plane interface port number
535 - * @param dpIp data plane ip address
536 - */
537 - private void processFirstTable(DeviceId deviceId, PortNumber dpPort, IpAddress dpIp) {
538 - // take vxlan packet out onto the physical port
539 - TrafficSelector selector = DefaultTrafficSelector.builder()
540 - .matchInPort(PortNumber.LOCAL)
541 - .build();
542 -
543 - TrafficTreatment treatment = DefaultTrafficTreatment.builder()
544 - .setOutput(dpPort)
545 - .build();
546 -
547 - FlowRule flowRule = DefaultFlowRule.builder()
548 - .fromApp(appId)
549 - .withSelector(selector)
550 - .withTreatment(treatment)
551 - .withPriority(HIGH_PRIORITY)
552 - .forDevice(deviceId)
553 - .forTable(TABLE_FIRST)
554 - .makePermanent()
555 - .build();
556 -
557 - processFlowRule(true, flowRule);
558 -
559 - // take a vxlan encap'd packet through the Linux stack
560 - selector = DefaultTrafficSelector.builder()
561 - .matchInPort(dpPort)
562 - .matchEthType(Ethernet.TYPE_IPV4)
563 - .matchIPProtocol(IPv4.PROTOCOL_UDP)
564 - .matchUdpDst(TpPort.tpPort(VXLAN_UDP_PORT))
565 - .build();
566 -
567 - treatment = DefaultTrafficTreatment.builder()
568 - .setOutput(PortNumber.LOCAL)
569 - .build();
570 -
571 - flowRule = DefaultFlowRule.builder()
572 - .fromApp(appId)
573 - .withSelector(selector)
574 - .withTreatment(treatment)
575 - .withPriority(HIGH_PRIORITY)
576 - .forDevice(deviceId)
577 - .forTable(TABLE_FIRST)
578 - .makePermanent()
579 - .build();
580 -
581 - processFlowRule(true, flowRule);
582 -
583 - // take a packet to the data plane ip through Linux stack
584 - selector = DefaultTrafficSelector.builder()
585 - .matchInPort(dpPort)
586 - .matchEthType(Ethernet.TYPE_IPV4)
587 - .matchIPDst(dpIp.toIpPrefix())
588 - .build();
589 -
590 - treatment = DefaultTrafficTreatment.builder()
591 - .setOutput(PortNumber.LOCAL)
592 - .build();
593 -
594 - flowRule = DefaultFlowRule.builder()
595 - .fromApp(appId)
596 - .withSelector(selector)
597 - .withTreatment(treatment)
598 - .withPriority(HIGH_PRIORITY)
599 - .forDevice(deviceId)
600 - .forTable(TABLE_FIRST)
601 - .makePermanent()
602 - .build();
603 -
604 - processFlowRule(true, flowRule);
605 -
606 - // take an arp packet from physical through Linux stack
607 - selector = DefaultTrafficSelector.builder()
608 - .matchInPort(dpPort)
609 - .matchEthType(Ethernet.TYPE_ARP)
610 - .matchArpTpa(dpIp.getIp4Address())
611 - .build();
612 -
613 - treatment = DefaultTrafficTreatment.builder()
614 - .setOutput(PortNumber.LOCAL)
615 - .build();
616 -
617 - flowRule = DefaultFlowRule.builder()
618 - .fromApp(appId)
619 - .withSelector(selector)
620 - .withTreatment(treatment)
621 - .withPriority(HIGH_PRIORITY)
622 - .forDevice(deviceId)
623 - .forTable(TABLE_FIRST)
624 - .makePermanent()
625 - .build();
626 -
627 - processFlowRule(true, flowRule);
628 -
629 - // take all else to the next table
630 - selector = DefaultTrafficSelector.builder()
631 - .build();
632 -
633 - treatment = DefaultTrafficTreatment.builder()
634 - .transition(TABLE_IN_PORT)
635 - .build();
636 -
637 - flowRule = DefaultFlowRule.builder()
638 - .fromApp(appId)
639 - .withSelector(selector)
640 - .withTreatment(treatment)
641 - .withPriority(LOWEST_PRIORITY)
642 - .forDevice(deviceId)
643 - .forTable(TABLE_FIRST)
644 - .makePermanent()
645 - .build();
646 -
647 - processFlowRule(true, flowRule);
648 -
649 - // take all vlan tagged packet to the Q_IN_Q table
650 - selector = DefaultTrafficSelector.builder()
651 - .matchVlanId(VlanId.ANY)
652 - .build();
653 -
654 - treatment = DefaultTrafficTreatment.builder()
655 - .transition(TABLE_Q_IN_Q)
656 - .build();
657 -
658 - flowRule = DefaultFlowRule.builder()
659 - .fromApp(appId)
660 - .withSelector(selector)
661 - .withTreatment(treatment)
662 - .withPriority(VSG_PRIORITY)
663 - .forDevice(deviceId)
664 - .forTable(TABLE_FIRST)
665 - .makePermanent()
666 - .build();
667 -
668 - processFlowRule(true, flowRule);
669 - }
670 -
671 - /**
672 - * Forward table miss packets in ACCESS_TYPE table to data plane port.
673 - *
674 - * @param deviceId device id
675 - * @param dpPort data plane interface port number
676 - */
677 - private void processAccessTypeTable(DeviceId deviceId, PortNumber dpPort) {
678 - TrafficSelector selector = DefaultTrafficSelector.builder()
679 - .build();
680 -
681 - TrafficTreatment treatment = DefaultTrafficTreatment.builder()
682 - .setOutput(dpPort)
683 - .build();
684 -
685 - FlowRule flowRule = DefaultFlowRule.builder()
686 - .fromApp(appId)
687 - .withSelector(selector)
688 - .withTreatment(treatment)
689 - .withPriority(LOWEST_PRIORITY)
690 - .forDevice(deviceId)
691 - .forTable(TABLE_ACCESS_TYPE)
692 - .makePermanent()
693 - .build();
694 -
695 - processFlowRule(true, flowRule);
696 - }
697 -
698 - /**
699 - * Populates default rules for IN_PORT table.
700 - * All packets from tunnel port are forwarded to TUNNEL_ID table and all packets
701 - * from data plane interface port to ACCESS_TYPE table.
702 - *
703 - * @param deviceId device id to install the rules
704 - * @param tunnelPort tunnel port number
705 - * @param dpPort data plane interface port number
706 - */
707 - private void processInPortTable(DeviceId deviceId, PortNumber tunnelPort, PortNumber dpPort) {
708 - checkNotNull(tunnelPort);
709 -
710 - TrafficSelector selector = DefaultTrafficSelector.builder()
711 - .matchInPort(tunnelPort)
712 - .build();
713 -
714 - TrafficTreatment treatment = DefaultTrafficTreatment.builder()
715 - .transition(TABLE_TUNNEL_IN)
716 - .build();
717 -
718 - FlowRule flowRule = DefaultFlowRule.builder()
719 - .fromApp(appId)
720 - .withSelector(selector)
721 - .withTreatment(treatment)
722 - .withPriority(DEFAULT_PRIORITY)
723 - .forDevice(deviceId)
724 - .forTable(TABLE_IN_PORT)
725 - .makePermanent()
726 - .build();
727 -
728 - processFlowRule(true, flowRule);
729 -
730 - selector = DefaultTrafficSelector.builder()
731 - .matchInPort(dpPort)
732 - .build();
733 -
734 - treatment = DefaultTrafficTreatment.builder()
735 - .transition(TABLE_DST_IP)
736 - .build();
737 -
738 - flowRule = DefaultFlowRule.builder()
739 - .fromApp(appId)
740 - .withSelector(selector)
741 - .withTreatment(treatment)
742 - .withPriority(DEFAULT_PRIORITY)
743 - .forDevice(deviceId)
744 - .forTable(TABLE_IN_PORT)
745 - .makePermanent()
746 - .build();
747 -
748 - processFlowRule(true, flowRule);
749 - }
750 -
751 - /**
752 - * Populates default rules for Q_IN_Q table.
753 - *
754 - * @param deviceId device id
755 - * @param dpPort data plane interface port number
756 - */
757 - private void processQInQTable(DeviceId deviceId, PortNumber dpPort) {
758 - // for traffic going out to WAN, strip vid 500 and take through data plane interface
759 - TrafficSelector selector = DefaultTrafficSelector.builder()
760 - .matchVlanId(VLAN_WAN)
761 - .build();
762 -
763 - TrafficTreatment treatment = DefaultTrafficTreatment.builder()
764 - .popVlan()
765 - .setOutput(dpPort)
766 - .build();
767 -
768 - FlowRule flowRule = DefaultFlowRule.builder()
769 - .fromApp(appId)
770 - .withSelector(selector)
771 - .withTreatment(treatment)
772 - .withPriority(DEFAULT_PRIORITY)
773 - .forDevice(deviceId)
774 - .forTable(TABLE_Q_IN_Q)
775 - .makePermanent()
776 - .build();
777 -
778 - processFlowRule(true, flowRule);
779 -
780 - selector = DefaultTrafficSelector.builder()
781 - .matchVlanId(VLAN_WAN)
782 - .matchEthType(Ethernet.TYPE_ARP)
783 - .build();
784 -
785 - treatment = DefaultTrafficTreatment.builder()
786 - .setOutput(PortNumber.CONTROLLER)
787 - .build();
788 -
789 - flowRule = DefaultFlowRule.builder()
790 - .fromApp(appId)
791 - .withSelector(selector)
792 - .withTreatment(treatment)
793 - .withPriority(HIGH_PRIORITY)
794 - .forDevice(deviceId)
795 - .forTable(TABLE_Q_IN_Q)
796 - .makePermanent()
797 - .build();
798 -
799 - processFlowRule(true, flowRule);
800 - }
801 -
802 - /**
803 - * Populates rules for local in port in IN_PORT table.
804 - * Flows from a given in port, whose source IP is service IP transition
805 - * to DST_TYPE table. Other flows transition to IN_SERVICE table.
806 - *
807 - * @param deviceId device id to install the rules
808 - * @param inPort in port
809 - * @param srcIp source ip
810 - * @param install true to install or false to remove
811 - */
812 - private void populateLocalInPortRule(DeviceId deviceId, PortNumber inPort, IpAddress srcIp,
813 - boolean install) {
814 - TrafficSelector selector = DefaultTrafficSelector.builder()
815 - .matchInPort(inPort)
816 - .matchEthType(Ethernet.TYPE_IPV4)
817 - .matchIPSrc(srcIp.toIpPrefix())
818 - .build();
819 -
820 - TrafficTreatment treatment = DefaultTrafficTreatment.builder()
821 - .transition(TABLE_ACCESS_TYPE)
822 - .build();
823 -
824 -
825 - FlowRule flowRule = DefaultFlowRule.builder()
826 - .fromApp(appId)
827 - .withSelector(selector)
828 - .withTreatment(treatment)
829 - .withPriority(DEFAULT_PRIORITY)
830 - .forDevice(deviceId)
831 - .forTable(TABLE_IN_PORT)
832 - .makePermanent()
833 - .build();
834 -
835 - processFlowRule(install, flowRule);
836 -
837 - selector = DefaultTrafficSelector.builder()
838 - .matchInPort(inPort)
839 - .build();
840 -
841 - treatment = DefaultTrafficTreatment.builder()
842 - .transition(TABLE_IN_SERVICE)
843 - .build();
844 -
845 - flowRule = DefaultFlowRule.builder()
846 - .fromApp(appId)
847 - .withSelector(selector)
848 - .withTreatment(treatment)
849 - .withPriority(LOW_PRIORITY)
850 - .forDevice(deviceId)
851 - .forTable(TABLE_IN_PORT)
852 - .makePermanent()
853 - .build();
854 -
855 - processFlowRule(install, flowRule);
856 - }
857 -
858 - /**
859 - * Populates direct VM access rules for ACCESS_TYPE table.
860 - * These rules are installed to all devices.
861 - *
862 - * @param srcRange source ip range
863 - * @param dstRange destination ip range
864 - * @param install true to install or false to remove
865 - */
866 - private void populateDirectAccessRule(Ip4Prefix srcRange, Ip4Prefix dstRange, boolean install) {
867 - TrafficSelector selector = DefaultTrafficSelector.builder()
868 - .matchEthType(Ethernet.TYPE_IPV4)
869 - .matchIPSrc(srcRange)
870 - .matchIPDst(dstRange)
871 - .build();
872 -
873 - TrafficTreatment treatment = DefaultTrafficTreatment.builder()
874 - .transition(TABLE_DST_IP)
875 - .build();
876 -
877 -
878 - getVirtualSwitches().stream().forEach(deviceId -> {
879 - FlowRule flowRuleDirect = DefaultFlowRule.builder()
880 - .fromApp(appId)
881 - .withSelector(selector)
882 - .withTreatment(treatment)
883 - .withPriority(DEFAULT_PRIORITY)
884 - .forDevice(deviceId)
885 - .forTable(TABLE_ACCESS_TYPE)
886 - .makePermanent()
887 - .build();
888 -
889 - processFlowRule(install, flowRuleDirect);
890 - });
891 - }
892 -
893 - /**
894 - * Populates drop rules that does not match any direct access rules but has
895 - * destination to a different service network in ACCESS_TYPE table.
896 - *
897 - * @param dstRange destination ip range
898 - * @param install true to install or false to remove
899 - */
900 - private void populateServiceIsolationRule(Ip4Prefix dstRange, boolean install) {
901 - TrafficSelector selector = DefaultTrafficSelector.builder()
902 - .matchEthType(Ethernet.TYPE_IPV4)
903 - .matchIPDst(dstRange)
904 - .build();
905 -
906 - TrafficTreatment treatment = DefaultTrafficTreatment.builder()
907 - .drop()
908 - .build();
909 -
910 - getVirtualSwitches().stream().forEach(deviceId -> {
911 - FlowRule flowRuleDirect = DefaultFlowRule.builder()
912 - .fromApp(appId)
913 - .withSelector(selector)
914 - .withTreatment(treatment)
915 - .withPriority(LOW_PRIORITY)
916 - .forDevice(deviceId)
917 - .forTable(TABLE_ACCESS_TYPE)
918 - .makePermanent()
919 - .build();
920 -
921 - processFlowRule(install, flowRuleDirect);
922 - });
923 - }
924 -
925 - /**
926 - * Populates indirect service access rules for ACCESS_TYPE table.
927 - * These rules are installed to all devices.
928 - *
929 - * @param srcRange source range
930 - * @param serviceIp service ip
931 - * @param outGroups list of output group
932 - * @param install true to install or false to remove
933 - */
934 - private void populateIndirectAccessRule(Ip4Prefix srcRange, Ip4Address serviceIp,
935 - Map<DeviceId, GroupId> outGroups, boolean install) {
936 - TrafficSelector selector = DefaultTrafficSelector.builder()
937 - .matchEthType(Ethernet.TYPE_IPV4)
938 - .matchIPSrc(srcRange)
939 - .matchIPDst(serviceIp.toIpPrefix())
940 - .build();
941 -
942 - for (Map.Entry<DeviceId, GroupId> outGroup : outGroups.entrySet()) {
943 - TrafficTreatment treatment = DefaultTrafficTreatment.builder()
944 - .group(outGroup.getValue())
945 - .build();
946 -
947 - FlowRule flowRule = DefaultFlowRule.builder()
948 - .fromApp(appId)
949 - .withSelector(selector)
950 - .withTreatment(treatment)
951 - .withPriority(HIGH_PRIORITY)
952 - .forDevice(outGroup.getKey())
953 - .forTable(TABLE_ACCESS_TYPE)
954 - .makePermanent()
955 - .build();
956 -
957 - processFlowRule(install, flowRule);
958 - }
959 - }
960 -
961 - /**
962 - * Populates flow rules for IN_SERVICE table.
963 - *
964 - * @param inPorts list of inports related to the service for each device
965 - * @param outGroups set of output groups
966 - * @param install true to install or false to remove
967 - */
968 - private void populateInServiceRule(Map<DeviceId, Set<PortNumber>> inPorts,
969 - Map<DeviceId, GroupId> outGroups, boolean install) {
970 - checkNotNull(inPorts);
971 - checkNotNull(outGroups);
972 -
973 - for (Map.Entry<DeviceId, Set<PortNumber>> entry : inPorts.entrySet()) {
974 - Set<PortNumber> ports = entry.getValue();
975 - DeviceId deviceId = entry.getKey();
976 -
977 - GroupId groupId = outGroups.get(deviceId);
978 - if (groupId == null) {
979 - continue;
980 - }
981 -
982 - ports.stream().forEach(port -> {
983 - TrafficSelector selector = DefaultTrafficSelector.builder()
984 - .matchInPort(port)
985 - .build();
986 -
987 - TrafficTreatment treatment = DefaultTrafficTreatment.builder()
988 - .group(groupId)
989 - .build();
990 -
991 - FlowRule flowRule = DefaultFlowRule.builder()
992 - .fromApp(appId)
993 - .withSelector(selector)
994 - .withTreatment(treatment)
995 - .withPriority(DEFAULT_PRIORITY)
996 - .forDevice(deviceId)
997 - .forTable(TABLE_IN_SERVICE)
998 - .makePermanent()
999 - .build();
1000 -
1001 - processFlowRule(install, flowRule);
1002 - });
1003 - }
1004 - }
1005 -
1006 - /**
1007 - * Populates flow rules for DST_IP table.
1008 - *
1009 - * @param deviceId device id
1010 - * @param inPort in port
1011 - * @param dstMac mac address
1012 - * @param dstIp destination ip
1013 - * @param tunnelId tunnel id
1014 - * @param tunnelIp tunnel remote ip
1015 - * @param install true to install or false to remove
1016 - */
1017 - private void populateDstIpRule(DeviceId deviceId, PortNumber inPort, MacAddress dstMac,
1018 - IpAddress dstIp, long tunnelId, IpAddress tunnelIp,
1019 - boolean install) {
1020 - TrafficSelector selector = DefaultTrafficSelector.builder()
1021 - .matchEthType(Ethernet.TYPE_IPV4)
1022 - .matchIPDst(dstIp.toIpPrefix())
1023 - .build();
1024 -
1025 - TrafficTreatment treatment = DefaultTrafficTreatment.builder()
1026 - .setEthDst(dstMac)
1027 - .setOutput(inPort)
1028 - .build();
1029 -
1030 - FlowRule flowRule = DefaultFlowRule.builder()
1031 - .fromApp(appId)
1032 - .withSelector(selector)
1033 - .withTreatment(treatment)
1034 - .withPriority(DEFAULT_PRIORITY)
1035 - .forDevice(deviceId)
1036 - .forTable(TABLE_DST_IP)
1037 - .makePermanent()
1038 - .build();
1039 -
1040 - processFlowRule(install, flowRule);
1041 -
1042 - for (DeviceId vSwitchId : getVirtualSwitches()) {
1043 - if (vSwitchId.equals(deviceId)) {
1044 - continue;
1045 - }
1046 -
1047 - ExtensionTreatment tunnelDst = getTunnelDst(vSwitchId, tunnelIp.getIp4Address());
1048 - if (tunnelDst == null) {
1049 - continue;
1050 - }
1051 -
1052 - treatment = DefaultTrafficTreatment.builder()
1053 - .setEthDst(dstMac)
1054 - .setTunnelId(tunnelId)
1055 - .extension(tunnelDst, vSwitchId)
1056 - .setOutput(getTunnelPort(vSwitchId))
1057 - .build();
1058 -
1059 - flowRule = DefaultFlowRule.builder()
1060 - .fromApp(appId)
1061 - .withSelector(selector)
1062 - .withTreatment(treatment)
1063 - .withPriority(DEFAULT_PRIORITY)
1064 - .forDevice(vSwitchId)
1065 - .forTable(TABLE_DST_IP)
1066 - .makePermanent()
1067 - .build();
1068 -
1069 - processFlowRule(install, flowRule);
1070 - }
1071 - }
1072 -
1073 - /**
1074 - * Populates flow rules for TUNNEL_ID table.
1075 - *
1076 - * @param deviceId device id
1077 - * @param inPort in port
1078 - * @param mac mac address
1079 - * @param tunnelId tunnel id
1080 - * @param install true to install or false to remove
1081 - */
1082 - private void populateTunnelInRule(DeviceId deviceId, PortNumber inPort, MacAddress mac,
1083 - long tunnelId, boolean install) {
1084 - TrafficSelector selector = DefaultTrafficSelector.builder()
1085 - .matchTunnelId(tunnelId)
1086 - .matchEthDst(mac)
1087 - .build();
1088 -
1089 - TrafficTreatment treatment = DefaultTrafficTreatment.builder()
1090 - .setOutput(inPort)
1091 - .build();
1092 -
1093 - FlowRule flowRule = DefaultFlowRule.builder()
1094 - .fromApp(appId)
1095 - .withSelector(selector)
1096 - .withTreatment(treatment)
1097 - .withPriority(DEFAULT_PRIORITY)
1098 - .forDevice(deviceId)
1099 - .forTable(TABLE_TUNNEL_IN)
1100 - .makePermanent()
1101 - .build();
1102 -
1103 - processFlowRule(install, flowRule);
1104 - }
1105 -
1106 - /**
1107 - * Installs or uninstall a given rule.
1108 - *
1109 - * @param install true to install, false to uninstall
1110 - * @param rule rule
1111 - */
1112 - private void processFlowRule(boolean install, FlowRule rule) {
1113 - FlowRuleOperations.Builder oBuilder = FlowRuleOperations.builder();
1114 - oBuilder = install ? oBuilder.add(rule) : oBuilder.remove(rule);
1115 -
1116 - flowRuleService.apply(oBuilder.build(new FlowRuleOperationsContext() {
1117 - @Override
1118 - public void onError(FlowRuleOperations ops) {
1119 - log.error(String.format("Failed %s, %s", ops.toString(), rule.toString()));
1120 - }
1121 - }));
1122 - }
1123 -
1124 - /**
1125 - * Returns tunnel port of the device.
1126 - *
1127 - * @param deviceId device id
1128 - * @return tunnel port number, or null if no tunnel port exists on a given device
1129 - */
1130 - private PortNumber getTunnelPort(DeviceId deviceId) {
1131 - Port port = deviceService.getPorts(deviceId).stream()
1132 - .filter(p -> p.annotations().value(PORT_NAME).contains(tunnelType))
1133 - .findFirst().orElse(null);
1134 -
1135 - return port == null ? null : port.number();
1136 - }
1137 -
1138 - /**
1139 - * Returns data plane interface port name of a given device.
1140 - *
1141 - * @param deviceId device id
1142 - * @param dpIntf data plane interface port name
1143 - * @return data plane interface port number, or null if no such port exists
1144 - */
1145 - private PortNumber getDpPort(DeviceId deviceId, String dpIntf) {
1146 - Port port = deviceService.getPorts(deviceId).stream()
1147 - .filter(p -> p.annotations().value(PORT_NAME).contains(dpIntf) &&
1148 - p.isEnabled())
1149 - .findFirst().orElse(null);
1150 -
1151 - return port == null ? null : port.number();
1152 - }
1153 -
1154 - /** Returns data plane interface port number of a given host.
1155 - *
1156 - * @param host host
1157 - * @return port number, or null
1158 - */
1159 - private PortNumber getDpPort(Host host) {
1160 - String portName = host.annotations().value(DATA_PLANE_INTF);
1161 - return portName == null ? null : getDpPort(host.location().deviceId(), portName);
1162 - }
1163 -
1164 - /**
1165 - * Returns service vlan from a given host.
1166 - *
1167 - * @param host host
1168 - * @return vlan id, or null
1169 - */
1170 - private VlanId getServiceVlan(Host host) {
1171 - String serviceVlan = host.annotations().value(S_TAG);
1172 - return serviceVlan == null ? null : VlanId.vlanId(Short.parseShort(serviceVlan));
1173 - }
1174 -
1175 - /**
1176 - * Returns IP address for tunneling for a given host.
1177 - *
1178 - * @param host host
1179 - * @return ip address, or null
1180 - */
1181 - private IpAddress getTunnelIp(Host host) {
1182 - String ip = host.annotations().value(DATA_PLANE_IP);
1183 - return ip == null ? null : IpAddress.valueOf(ip);
1184 - }
1185 -
1186 -
1187 - /**
1188 - * Returns the destination IP from a given flow rule if the rule contains
1189 - * the match of it.
1190 - *
1191 - * @param flowRule flow rule
1192 - * @return ip prefix, or null if the rule doesn't have ip match
1193 - */
1194 - private IpPrefix getDstIpFromSelector(FlowRule flowRule) {
1195 - Criterion criterion = flowRule.selector().getCriterion(IPV4_DST);
1196 - if (criterion != null && criterion instanceof IPCriterion) {
1197 - IPCriterion ip = (IPCriterion) criterion;
1198 - return ip.ip();
1199 - } else {
1200 - return null;
1201 - }
1202 - }
1203 -
1204 - /**
1205 - * Returns the output port number from a given flow rule.
1206 - *
1207 - * @param flowRule flow rule
1208 - * @return port number, or null if the rule does not have output instruction
1209 - */
1210 - private PortNumber getOutputFromTreatment(FlowRule flowRule) {
1211 - Instruction instruction = flowRule.treatment().allInstructions().stream()
1212 - .filter(inst -> inst instanceof Instructions.OutputInstruction)
1213 - .findFirst()
1214 - .orElse(null);
1215 -
1216 - if (instruction == null) {
1217 - return null;
1218 - }
1219 -
1220 - return ((Instructions.OutputInstruction) instruction).port();
1221 - }
1222 -
1223 - /**
1224 - * Returns if a given flow rule has vlan push instruction or not.
1225 - *
1226 - * @param flowRule flow rule
1227 - * @return true if it includes vlan push, or false
1228 - */
1229 - private boolean isVlanPushFromTreatment(FlowRule flowRule) {
1230 - Instruction instruction = flowRule.treatment().allInstructions().stream()
1231 - .filter(inst -> inst instanceof L2ModificationInstruction)
1232 - .filter(inst -> ((L2ModificationInstruction) inst).subtype().equals(VLAN_PUSH))
1233 - .findAny()
1234 - .orElse(null);
1235 -
1236 - return instruction != null;
1237 - }
1238 -
1239 - /**
1240 - * Creates a new group for a given service.
1241 - *
1242 - * @param deviceId device id to create a group
1243 - * @param service cord service
1244 - * @return group id, or null if it fails to create
1245 - */
1246 - private GroupId createServiceGroup(DeviceId deviceId, VtnService service) {
1247 - checkNotNull(service);
1248 -
1249 - GroupKey groupKey = getGroupKey(service.id());
1250 - Group group = groupService.getGroup(deviceId, groupKey);
1251 - GroupId groupId = getGroupId(service.id(), deviceId);
1252 -
1253 - if (group != null) {
1254 - log.debug("Group {} is already exist in {}", service.id(), deviceId);
1255 - return groupId;
1256 - }
1257 -
1258 - GroupBuckets buckets = getServiceGroupBuckets(
1259 - deviceId, service.vni(), getInstances(service.id()));
1260 - GroupDescription groupDescription = new DefaultGroupDescription(
1261 - deviceId,
1262 - GroupDescription.Type.SELECT,
1263 - buckets,
1264 - groupKey,
1265 - groupId.id(),
1266 - appId);
1267 -
1268 - groupService.addGroup(groupDescription);
1269 -
1270 - return groupId;
1271 - }
1272 -
1273 - /**
1274 - * Returns group buckets for a given device.
1275 - *
1276 - * @param deviceId device id
1277 - * @param tunnelId tunnel id
1278 - * @param hosts list of host
1279 - * @return group buckets
1280 - */
1281 - private GroupBuckets getServiceGroupBuckets(DeviceId deviceId, long tunnelId,
1282 - Set<Host> hosts) {
1283 - List<GroupBucket> buckets = Lists.newArrayList();
1284 -
1285 - hosts.stream().forEach(host -> {
1286 - Ip4Address tunnelIp = getTunnelIp(host).getIp4Address();
1287 - DeviceId hostDevice = host.location().deviceId();
1288 -
1289 - TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
1290 - .builder()
1291 - .setEthDst(host.mac());
1292 - if (deviceId.equals(hostDevice)) {
1293 - tBuilder.setOutput(host.location().port());
1294 - } else {
1295 - ExtensionTreatment tunnelDst = getTunnelDst(deviceId, tunnelIp);
1296 - tBuilder.extension(tunnelDst, deviceId)
1297 - .setTunnelId(tunnelId)
1298 - .setOutput(getTunnelPort(hostDevice));
1299 - }
1300 - buckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
1301 - });
1302 -
1303 - return new GroupBuckets(buckets);
1304 - }
1305 -
1306 - /**
1307 - * Returns globally unique group ID.
1308 - *
1309 - * @param serviceId service id
1310 - * @param deviceId device id
1311 - * @return group id
1312 - */
1313 - private GroupId getGroupId(VtnServiceId serviceId, DeviceId deviceId) {
1314 - return new DefaultGroupId(Objects.hash(serviceId, deviceId));
1315 - }
1316 -
1317 - /**
1318 - * Returns group key of a service.
1319 - *
1320 - * @param serviceId service id
1321 - * @return group key
1322 - */
1323 - private GroupKey getGroupKey(VtnServiceId serviceId) {
1324 - return new DefaultGroupKey(serviceId.id().getBytes());
1325 - }
1326 -
1327 - /**
1328 - * Returns extension instruction to set tunnel destination.
1329 - *
1330 - * @param deviceId device id
1331 - * @param remoteIp tunnel destination address
1332 - * @return extension treatment or null if it fails to get instruction
1333 - */
1334 - private ExtensionTreatment getTunnelDst(DeviceId deviceId, Ip4Address remoteIp) {
1335 - try {
1336 - Device device = deviceService.getDevice(deviceId);
1337 -
1338 - if (device.is(ExtensionTreatmentResolver.class)) {
1339 - ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
1340 - ExtensionTreatment treatment =
1341 - resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
1342 - treatment.setPropertyValue("tunnelDst", remoteIp);
1343 -
1344 - return treatment;
1345 - } else {
1346 - log.warn("The extension treatment resolving behaviour is not supported in device {}",
1347 - device.id().toString());
1348 - return null;
1349 - }
1350 - } catch (ItemNotFoundException | UnsupportedOperationException |
1351 - ExtensionPropertyException e) {
1352 - log.error("Failed to get extension instruction {}", deviceId);
1353 - return null;
1354 - }
1355 - }
1356 -
1357 - /**
1358 - * Returns integration bridges configured in the system.
1359 - *
1360 - * @return set of device ids
1361 - */
1362 - private Set<DeviceId> getVirtualSwitches() {
1363 - CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
1364 - if (config == null) {
1365 - log.debug("No configuration found for {}", appId.name());
1366 - return Sets.newHashSet();
1367 - }
1368 -
1369 - return config.cordVtnNodes().stream()
1370 - .map(CordVtnNode::intBrId).collect(Collectors.toSet());
1371 - }
1372 -
1373 - /**
1374 - * Returns instances with a given network service.
1375 - *
1376 - * @param serviceId service id
1377 - * @return set of hosts
1378 - */
1379 - private Set<Host> getInstances(VtnServiceId serviceId) {
1380 - return StreamSupport.stream(hostService.getHosts().spliterator(), false)
1381 - .filter(host -> Objects.equals(
1382 - serviceId.id(),
1383 - host.annotations().value(SERVICE_ID)))
1384 - .collect(Collectors.toSet());
1385 - }
1386 -}
1387 -
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.cordvtn.impl.service;
17 +
18 +import org.apache.felix.scr.annotations.Activate;
19 +import org.apache.felix.scr.annotations.Component;
20 +
21 +import org.apache.felix.scr.annotations.Deactivate;
22 +import org.onosproject.cordvtn.api.Instance;
23 +import org.onosproject.cordvtn.api.InstanceHandler;
24 +import org.onosproject.cordvtn.impl.CordVtnInstanceHandler;
25 +import org.onosproject.xosclient.api.VtnService;
26 +
27 +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
28 +import static org.onlab.util.Tools.groupedThreads;
29 +
30 +/**
31 + * Provides network connectivity for dummy service instances.
32 + */
33 +@Component(immediate = true)
34 +public class DummyInstanceHandler extends CordVtnInstanceHandler implements InstanceHandler {
35 +
36 + @Activate
37 + protected void activate() {
38 + serviceType = VtnService.ServiceType.DUMMY;
39 + eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn-dummy", "event-handler"));
40 + super.activate();
41 + }
42 +
43 + @Deactivate
44 + protected void deactivate() {
45 + super.deactivate();
46 + }
47 +
48 + @Override
49 + public void instanceDetected(Instance instance) {
50 + super.instanceDetected(instance);
51 + }
52 +
53 + @Override
54 + public void instanceRemoved(Instance instance) {
55 + super.instanceRemoved(instance);
56 + }
57 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.cordvtn.impl.service;
17 +
18 +import com.google.common.collect.Maps;
19 +import org.apache.felix.scr.annotations.Activate;
20 +import org.apache.felix.scr.annotations.Component;
21 +import org.apache.felix.scr.annotations.Deactivate;
22 +import org.onlab.packet.Ethernet;
23 +import org.onlab.packet.IpPrefix;
24 +import org.onosproject.cordconfig.access.AccessAgentConfig;
25 +import org.onosproject.cordconfig.access.AccessAgentData;
26 +import org.onosproject.cordvtn.api.CordVtnConfig;
27 +import org.onosproject.cordvtn.api.Instance;
28 +import org.onosproject.cordvtn.api.InstanceHandler;
29 +import org.onosproject.cordvtn.impl.CordVtnInstanceHandler;
30 +import org.onosproject.net.DeviceId;
31 +import org.onosproject.net.PortNumber;
32 +import org.onosproject.net.config.ConfigFactory;
33 +import org.onosproject.net.config.NetworkConfigEvent;
34 +import org.onosproject.net.config.NetworkConfigListener;
35 +import org.onosproject.net.config.basics.SubjectFactories;
36 +import org.onosproject.net.flow.DefaultFlowRule;
37 +import org.onosproject.net.flow.DefaultTrafficSelector;
38 +import org.onosproject.net.flow.DefaultTrafficTreatment;
39 +import org.onosproject.net.flow.FlowRule;
40 +import org.onosproject.net.flow.TrafficSelector;
41 +import org.onosproject.net.flow.TrafficTreatment;
42 +import org.onosproject.xosclient.api.VtnService;
43 +
44 +import java.util.Map;
45 +import java.util.Set;
46 +
47 +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
48 +import static org.onlab.util.Tools.groupedThreads;
49 +import static org.onosproject.cordvtn.impl.CordVtnPipeline.PRIORITY_MANAGEMENT;
50 +import static org.onosproject.cordvtn.impl.CordVtnPipeline.TABLE_ACCESS_TYPE;
51 +
52 +/**
53 + * Provides network connectivity for OLT agent instances.
54 + */
55 +@Component(immediate = true)
56 +public class OltAgentInstanceHandler extends CordVtnInstanceHandler implements InstanceHandler {
57 +
58 + private static final Class<AccessAgentConfig> CONFIG_CLASS = AccessAgentConfig.class;
59 + private ConfigFactory<DeviceId, AccessAgentConfig> configFactory =
60 + new ConfigFactory<DeviceId, AccessAgentConfig>(
61 + SubjectFactories.DEVICE_SUBJECT_FACTORY, CONFIG_CLASS, "accessAgent") {
62 + @Override
63 + public AccessAgentConfig createConfig() {
64 + return new AccessAgentConfig();
65 + }
66 + };
67 +
68 + private Map<DeviceId, AccessAgentData> oltAgentData = Maps.newConcurrentMap();
69 + private IpPrefix mgmtIpRange = null;
70 +
71 + @Activate
72 + protected void activate() {
73 + eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn-olt", "event-handler"));
74 + serviceType = VtnService.ServiceType.OLT_AGENT;
75 +
76 + configRegistry.registerConfigFactory(configFactory);
77 + configListener = new InternalConfigListener();
78 +
79 + super.activate();
80 + }
81 +
82 + @Deactivate
83 + protected void deactivate() {
84 + super.deactivate();
85 + }
86 +
87 + @Override
88 + public void instanceDetected(Instance instance) {
89 + log.info("OLT agent instance detected {}", instance);
90 +
91 + managementAccessRule(instance.deviceId(), true);
92 + // TODO implement
93 + }
94 +
95 + @Override
96 + public void instanceRemoved(Instance instance) {
97 + log.info("OLT agent instance removed {}", instance);
98 +
99 + if (getInstances(instance.serviceId()).isEmpty()) {
100 + nodeManager.completeNodes().stream().forEach(node ->
101 + managementAccessRule(node.intBrId(), false));
102 + }
103 +
104 + // TODO implement
105 + }
106 +
107 + private void managementAccessRule(DeviceId deviceId, boolean install) {
108 + // TODO remove this rule after long term management network is done
109 + if (mgmtIpRange != null) {
110 + TrafficSelector selector = DefaultTrafficSelector.builder()
111 + .matchEthType(Ethernet.TYPE_IPV4)
112 + .matchIPDst(mgmtIpRange)
113 + .build();
114 +
115 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
116 + .setOutput(PortNumber.LOCAL)
117 + .build();
118 +
119 + FlowRule flowRule = DefaultFlowRule.builder()
120 + .fromApp(appId)
121 + .withSelector(selector)
122 + .withTreatment(treatment)
123 + .withPriority(PRIORITY_MANAGEMENT)
124 + .forDevice(deviceId)
125 + .forTable(TABLE_ACCESS_TYPE)
126 + .makePermanent()
127 + .build();
128 +
129 + pipeline.processFlowRule(install, flowRule);
130 + }
131 + }
132 +
133 + private void readAccessAgentConfig() {
134 +
135 + Set<DeviceId> deviceSubjects = configRegistry.getSubjects(DeviceId.class, CONFIG_CLASS);
136 + deviceSubjects.stream().forEach(subject -> {
137 + AccessAgentConfig config = configRegistry.getConfig(subject, CONFIG_CLASS);
138 + if (config != null) {
139 + oltAgentData.put(subject, config.getAgent());
140 + }
141 + });
142 + }
143 +
144 + @Override
145 + protected void readConfiguration() {
146 + CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
147 + if (config == null) {
148 + log.debug("No configuration found");
149 + return;
150 + }
151 +
152 + osAccess = config.openstackAccess();
153 + xosAccess = config.xosAccess();
154 + mgmtIpRange = config.managementIpRange();
155 + }
156 +
157 + public class InternalConfigListener implements NetworkConfigListener {
158 +
159 + @Override
160 + public void event(NetworkConfigEvent event) {
161 +
162 + switch (event.type()) {
163 + case CONFIG_UPDATED:
164 + case CONFIG_ADDED:
165 + if (event.configClass().equals(CordVtnConfig.class)) {
166 + readConfiguration();
167 + } else if (event.configClass().equals(CONFIG_CLASS)) {
168 + readAccessAgentConfig();
169 + }
170 + break;
171 + default:
172 + break;
173 + }
174 + }
175 + }
176 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.cordvtn.impl.service;
17 +
18 +import com.google.common.base.Strings;
19 +import com.google.common.collect.Sets;
20 +import org.apache.felix.scr.annotations.Activate;
21 +import org.apache.felix.scr.annotations.Component;
22 +import org.apache.felix.scr.annotations.Deactivate;
23 +import org.apache.felix.scr.annotations.Reference;
24 +import org.apache.felix.scr.annotations.ReferenceCardinality;
25 +import org.apache.felix.scr.annotations.Service;
26 +import org.onlab.packet.Ethernet;
27 +import org.onlab.packet.IpAddress;
28 +import org.onlab.packet.IpPrefix;
29 +import org.onlab.packet.MacAddress;
30 +import org.onlab.packet.VlanId;
31 +import org.onosproject.cordvtn.api.Instance;
32 +import org.onosproject.cordvtn.api.InstanceHandler;
33 +import org.onosproject.cordvtn.impl.CordVtnInstanceHandler;
34 +import org.onosproject.cordvtn.impl.CordVtnInstanceManager;
35 +import org.onosproject.net.DefaultAnnotations;
36 +import org.onosproject.net.HostId;
37 +import org.onosproject.net.PortNumber;
38 +import org.onosproject.net.flow.DefaultFlowRule;
39 +import org.onosproject.net.flow.DefaultTrafficSelector;
40 +import org.onosproject.net.flow.DefaultTrafficTreatment;
41 +import org.onosproject.net.flow.FlowRule;
42 +import org.onosproject.net.flow.FlowRuleService;
43 +import org.onosproject.net.flow.TrafficSelector;
44 +import org.onosproject.net.flow.TrafficTreatment;
45 +import org.onosproject.net.flow.criteria.Criterion;
46 +import org.onosproject.net.flow.criteria.IPCriterion;
47 +import org.onosproject.net.flow.instructions.Instruction;
48 +import org.onosproject.net.flow.instructions.Instructions;
49 +import org.onosproject.net.flow.instructions.L2ModificationInstruction;
50 +import org.onosproject.net.host.DefaultHostDescription;
51 +import org.onosproject.net.host.HostDescription;
52 +import org.onosproject.xosclient.api.VtnPort;
53 +import org.onosproject.xosclient.api.VtnPortApi;
54 +import org.onosproject.xosclient.api.VtnPortId;
55 +import org.onosproject.xosclient.api.VtnService;
56 +
57 +import java.util.Map;
58 +import java.util.Set;
59 +
60 +import static com.google.common.base.Preconditions.checkNotNull;
61 +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
62 +import static org.onlab.util.Tools.groupedThreads;
63 +import static org.onosproject.cordvtn.api.Instance.*;
64 +import static org.onosproject.cordvtn.impl.CordVtnPipeline.*;
65 +import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
66 +import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_PUSH;
67 +
68 +/**
69 + * Provides network connectivity for vSG instances.
70 + */
71 +@Component(immediate = true)
72 +@Service(value = VsgInstanceHandler.class)
73 +public final class VsgInstanceHandler extends CordVtnInstanceHandler implements InstanceHandler {
74 +
75 + private static final String STAG = "stag";
76 + private static final String VSG_VM = "vsgVm";
77 +
78 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 + protected FlowRuleService flowRuleService;
80 +
81 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 + protected CordVtnInstanceManager instanceManager;
83 +
84 + @Activate
85 + protected void activate() {
86 + serviceType = VtnService.ServiceType.VSG;
87 + eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn-vsg", "event-handler"));
88 + super.activate();
89 + }
90 +
91 + @Deactivate
92 + protected void deactivate() {
93 + super.deactivate();
94 + }
95 +
96 + @Override
97 + public void instanceDetected(Instance instance) {
98 + if (isVsgContainer(instance)) {
99 + log.info("vSG container detected {}", instance);
100 +
101 + // find vsg vm for this vsg container
102 + String vsgVmId = instance.getAnnotation(VSG_VM);
103 + if (Strings.isNullOrEmpty(vsgVmId)) {
104 + log.warn("Failed to find VSG VM for {}", instance);
105 + return;
106 + }
107 +
108 + Instance vsgVm = Instance.of(hostService.getHost(HostId.hostId(vsgVmId)));
109 + VtnPort vtnPort = getVtnPort(vsgVm);
110 + if (vtnPort == null || getStag(vtnPort) == null) {
111 + return;
112 + }
113 +
114 + populateVsgRules(vsgVm, getStag(vtnPort),
115 + nodeManager.dpPort(vsgVm.deviceId()),
116 + vtnPort.addressPairs().keySet(),
117 + true);
118 +
119 + } else {
120 + VtnPort vtnPort = getVtnPort(instance);
121 + if (vtnPort == null || getStag(vtnPort) == null) {
122 + return;
123 + }
124 +
125 + vtnPort.addressPairs().entrySet().stream()
126 + .forEach(pair -> addVsgContainer(
127 + instance,
128 + pair.getKey(),
129 + pair.getValue(),
130 + getStag(vtnPort).toString()
131 + ));
132 + super.instanceDetected(instance);
133 + }
134 + }
135 +
136 + @Override
137 + public void instanceRemoved(Instance instance) {
138 + if (isVsgContainer(instance)) {
139 + log.info("vSG container vanished {}", instance);
140 +
141 + // find vsg vm for this vsg container
142 + String vsgVmId = instance.getAnnotation(VSG_VM);
143 + if (Strings.isNullOrEmpty(vsgVmId)) {
144 + log.warn("Failed to find VSG VM for {}", instance);
145 + return;
146 + }
147 +
148 + Instance vsgVm = Instance.of(hostService.getHost(HostId.hostId(vsgVmId)));
149 + VtnPort vtnPort = getVtnPort(vsgVm);
150 + if (vtnPort == null || getStag(vtnPort) == null) {
151 + return;
152 + }
153 +
154 + populateVsgRules(vsgVm, getStag(vtnPort),
155 + nodeManager.dpPort(vsgVm.deviceId()),
156 + vtnPort.addressPairs().keySet(),
157 + false);
158 +
159 + } else {
160 + // TODO remove vsg vm related rules
161 + super.instanceRemoved(instance);
162 + }
163 + }
164 +
165 + /**
166 + * Updates set of vSGs in a given vSG VM.
167 + *
168 + * @param vsgVmId vsg vm host id
169 + * @param stag stag
170 + * @param vsgInstances full set of vsg wan ip and mac address pairs in this vsg vm
171 + */
172 + public void updateVsgInstances(HostId vsgVmId, String stag, Map<IpAddress, MacAddress> vsgInstances) {
173 + if (hostService.getHost(vsgVmId) == null) {
174 + log.debug("vSG VM {} is not added yet, ignore this update", vsgVmId);
175 + return;
176 + }
177 +
178 + Instance vsgVm = Instance.of(hostService.getHost(vsgVmId));
179 + if (vsgVm == null) {
180 + log.warn("Failed to find existing vSG VM for STAG: {}", stag);
181 + return;
182 + }
183 +
184 + log.info("Updates vSGs in {} with STAG: {}", vsgVm, stag);
185 +
186 + // adds vSGs in the address pair
187 + vsgInstances.entrySet().stream()
188 + .filter(addr -> hostService.getHostsByMac(addr.getValue()).isEmpty())
189 + .forEach(addr -> addVsgContainer(
190 + vsgVm,
191 + addr.getKey(),
192 + addr.getValue(),
193 + stag));
194 +
195 + // removes vSGs not listed in the address pair
196 + hostService.getConnectedHosts(vsgVm.host().location()).stream()
197 + .filter(host -> !host.mac().equals(vsgVm.mac()))
198 + .filter(host -> !vsgInstances.values().contains(host.mac()))
199 + .forEach(host -> {
200 + log.info("Removed vSG {}", host.toString());
201 + instanceManager.removeInstance(host.id());
202 + });
203 + }
204 +
205 + private boolean isVsgContainer(Instance instance) {
206 + return !Strings.isNullOrEmpty(instance.host().annotations().value(STAG));
207 + }
208 +
209 + private void addVsgContainer(Instance vsgVm, IpAddress vsgWanIp, MacAddress vsgMac,
210 + String stag) {
211 + HostId hostId = HostId.hostId(vsgMac);
212 + DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
213 + .set(SERVICE_TYPE, vsgVm.serviceType().toString())
214 + .set(SERVICE_ID, vsgVm.serviceId().id())
215 + .set(PORT_ID, vsgVm.portId().id())
216 + .set(NESTED_INSTANCE, TRUE)
217 + .set(STAG, stag)
218 + .set(VSG_VM, vsgVm.host().id().toString())
219 + .set(CREATE_TIME, String.valueOf(System.currentTimeMillis()));
220 +
221 + HostDescription hostDesc = new DefaultHostDescription(
222 + vsgMac,
223 + VlanId.NONE,
224 + vsgVm.host().location(),
225 + Sets.newHashSet(vsgWanIp),
226 + annotations.build());
227 +
228 + instanceManager.addInstance(hostId, hostDesc);
229 + }
230 +
231 + private void populateVsgRules(Instance vsgVm, VlanId stag, PortNumber dpPort,
232 + Set<IpAddress> vsgWanIps, boolean install) {
233 + // for traffics with s-tag, strip the tag and take through the vSG VM
234 + TrafficSelector selector = DefaultTrafficSelector.builder()
235 + .matchInPort(dpPort)
236 + .matchVlanId(stag)
237 + .build();
238 +
239 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
240 + .setOutput(vsgVm.portNumber())
241 + .build();
242 +
243 + FlowRule flowRule = DefaultFlowRule.builder()
244 + .fromApp(appId)
245 + .withSelector(selector)
246 + .withTreatment(treatment)
247 + .withPriority(PRIORITY_DEFAULT)
248 + .forDevice(vsgVm.deviceId())
249 + .forTable(TABLE_VLAN)
250 + .makePermanent()
251 + .build();
252 +
253 + pipeline.processFlowRule(install, flowRule);
254 +
255 + // for traffics with customer vlan, tag with the service vlan based on input port with
256 + // lower priority to avoid conflict with WAN tag
257 + selector = DefaultTrafficSelector.builder()
258 + .matchInPort(vsgVm.portNumber())
259 + .matchVlanId(stag)
260 + .build();
261 +
262 + treatment = DefaultTrafficTreatment.builder()
263 + .setOutput(dpPort)
264 + .build();
265 +
266 + flowRule = DefaultFlowRule.builder()
267 + .fromApp(appId)
268 + .withSelector(selector)
269 + .withTreatment(treatment)
270 + .withPriority(PRIORITY_DEFAULT)
271 + .forDevice(vsgVm.deviceId())
272 + .forTable(TABLE_VLAN)
273 + .makePermanent()
274 + .build();
275 +
276 + pipeline.processFlowRule(install, flowRule);
277 +
278 + // for traffic coming from WAN, tag 500 and take through the vSG VM
279 + // based on destination ip
280 + vsgWanIps.stream().forEach(ip -> {
281 + TrafficSelector downstream = DefaultTrafficSelector.builder()
282 + .matchEthType(Ethernet.TYPE_IPV4)
283 + .matchIPDst(ip.toIpPrefix())
284 + .build();
285 +
286 + TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
287 + .pushVlan()
288 + .setVlanId(VLAN_WAN)
289 + .setEthDst(vsgVm.mac())
290 + .setOutput(vsgVm.portNumber())
291 + .build();
292 +
293 + FlowRule downstreamFlowRule = DefaultFlowRule.builder()
294 + .fromApp(appId)
295 + .withSelector(downstream)
296 + .withTreatment(downstreamTreatment)
297 + .withPriority(PRIORITY_DEFAULT)
298 + .forDevice(vsgVm.deviceId())
299 + .forTable(TABLE_DST_IP)
300 + .makePermanent()
301 + .build();
302 +
303 + pipeline.processFlowRule(install, downstreamFlowRule);
304 + });
305 +
306 + // remove downstream flow rules for the vSG not shown in vsgWanIps
307 + for (FlowRule rule : flowRuleService.getFlowRulesById(appId)) {
308 + if (!rule.deviceId().equals(vsgVm.deviceId())) {
309 + continue;
310 + }
311 + PortNumber output = getOutputFromTreatment(rule);
312 + if (output == null || !output.equals(vsgVm.portNumber()) ||
313 + !isVlanPushFromTreatment(rule)) {
314 + continue;
315 + }
316 +
317 + IpPrefix dstIp = getDstIpFromSelector(rule);
318 + if (dstIp != null && !vsgWanIps.contains(dstIp.address())) {
319 + pipeline.processFlowRule(false, rule);
320 + }
321 + }
322 + }
323 +
324 + private VtnPort getVtnPort(Instance instance) {
325 + checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
326 + checkNotNull(xosAccess, XOS_ACCESS_ERROR);
327 +
328 + VtnPortId vtnPortId = instance.portId();
329 + VtnPortApi portApi = xosClient.getClient(xosAccess).vtnPort();
330 + VtnPort vtnPort = portApi.vtnPort(vtnPortId, osAccess);
331 + if (vtnPort == null) {
332 + log.warn("Failed to get port information of {}", instance);
333 + return null;
334 + }
335 + return vtnPort;
336 + }
337 +
338 + // TODO get stag from XOS when XOS provides it, extract if from port name for now
339 + private VlanId getStag(VtnPort vtnPort) {
340 + checkNotNull(vtnPort);
341 +
342 + String portName = vtnPort.name();
343 + if (portName != null && portName.startsWith(STAG)) {
344 + return VlanId.vlanId(portName.split("-")[1]);
345 + } else {
346 + return null;
347 + }
348 + }
349 +
350 + private PortNumber getOutputFromTreatment(FlowRule flowRule) {
351 + Instruction instruction = flowRule.treatment().allInstructions().stream()
352 + .filter(inst -> inst instanceof Instructions.OutputInstruction)
353 + .findFirst()
354 + .orElse(null);
355 + if (instruction == null) {
356 + return null;
357 + }
358 + return ((Instructions.OutputInstruction) instruction).port();
359 + }
360 +
361 + private IpPrefix getDstIpFromSelector(FlowRule flowRule) {
362 + Criterion criterion = flowRule.selector().getCriterion(IPV4_DST);
363 + if (criterion != null && criterion instanceof IPCriterion) {
364 + IPCriterion ip = (IPCriterion) criterion;
365 + return ip.ip();
366 + } else {
367 + return null;
368 + }
369 + }
370 +
371 + private boolean isVlanPushFromTreatment(FlowRule flowRule) {
372 + Instruction instruction = flowRule.treatment().allInstructions().stream()
373 + .filter(inst -> inst instanceof L2ModificationInstruction)
374 + .filter(inst -> ((L2ModificationInstruction) inst).subtype().equals(VLAN_PUSH))
375 + .findAny()
376 + .orElse(null);
377 + return instruction != null;
378 + }
379 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +/**
18 + * Implementation of instance handlers for various network services.
19 + */
20 +package org.onosproject.cordvtn.impl.service;
...\ No newline at end of file ...\ No newline at end of file
...@@ -18,9 +18,10 @@ package org.onosproject.cordvtn.rest; ...@@ -18,9 +18,10 @@ package org.onosproject.cordvtn.rest;
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
19 import com.fasterxml.jackson.databind.ObjectMapper; 19 import com.fasterxml.jackson.databind.ObjectMapper;
20 import com.google.common.collect.Maps; 20 import com.google.common.collect.Maps;
21 +import org.onlab.osgi.DefaultServiceDirectory;
21 import org.onlab.packet.IpAddress; 22 import org.onlab.packet.IpAddress;
22 import org.onlab.packet.MacAddress; 23 import org.onlab.packet.MacAddress;
23 -import org.onosproject.cordvtn.api.CordVtnService; 24 +import org.onosproject.cordvtn.impl.service.VsgInstanceHandler;
24 import org.onosproject.net.HostId; 25 import org.onosproject.net.HostId;
25 import org.onosproject.rest.AbstractWebResource; 26 import org.onosproject.rest.AbstractWebResource;
26 import org.slf4j.Logger; 27 import org.slf4j.Logger;
...@@ -57,7 +58,7 @@ public class NeutronMl2PortsWebResource extends AbstractWebResource { ...@@ -57,7 +58,7 @@ public class NeutronMl2PortsWebResource extends AbstractWebResource {
57 private static final String STAG_PREFIX = "stag-"; 58 private static final String STAG_PREFIX = "stag-";
58 private static final int STAG_BEGIN_INDEX = 5; 59 private static final int STAG_BEGIN_INDEX = 5;
59 60
60 - private final CordVtnService service = get(CordVtnService.class); 61 + private final VsgInstanceHandler service = DefaultServiceDirectory.getService(VsgInstanceHandler.class);
61 62
62 @POST 63 @POST
63 @Consumes(MediaType.APPLICATION_JSON) 64 @Consumes(MediaType.APPLICATION_JSON)
...@@ -74,6 +75,7 @@ public class NeutronMl2PortsWebResource extends AbstractWebResource { ...@@ -74,6 +75,7 @@ public class NeutronMl2PortsWebResource extends AbstractWebResource {
74 public Response updatePorts(@PathParam("id") String id, InputStream input) { 75 public Response updatePorts(@PathParam("id") String id, InputStream input) {
75 log.debug(String.format(PORTS_MESSAGE, "update")); 76 log.debug(String.format(PORTS_MESSAGE, "update"));
76 77
78 + // TODO get vSG updates from XOS to CORD VTN service directly
77 try { 79 try {
78 ObjectMapper mapper = new ObjectMapper(); 80 ObjectMapper mapper = new ObjectMapper();
79 JsonNode jsonNode = mapper.readTree(input).get(PORT); 81 JsonNode jsonNode = mapper.readTree(input).get(PORT);
...@@ -88,17 +90,16 @@ public class NeutronMl2PortsWebResource extends AbstractWebResource { ...@@ -88,17 +90,16 @@ public class NeutronMl2PortsWebResource extends AbstractWebResource {
88 90
89 // this is allowed address pairs updates 91 // this is allowed address pairs updates
90 MacAddress mac = MacAddress.valueOf(jsonNode.path(MAC_ADDRESS).asText()); 92 MacAddress mac = MacAddress.valueOf(jsonNode.path(MAC_ADDRESS).asText());
91 - Map<IpAddress, MacAddress> vSgs = Maps.newHashMap(); 93 + Map<IpAddress, MacAddress> vsgInstances = Maps.newHashMap();
92 jsonNode.path(ADDRESS_PAIRS).forEach(addrPair -> { 94 jsonNode.path(ADDRESS_PAIRS).forEach(addrPair -> {
93 IpAddress pairIp = IpAddress.valueOf(addrPair.path(IP_ADDERSS).asText()); 95 IpAddress pairIp = IpAddress.valueOf(addrPair.path(IP_ADDERSS).asText());
94 MacAddress pairMac = MacAddress.valueOf(addrPair.path(MAC_ADDRESS).asText()); 96 MacAddress pairMac = MacAddress.valueOf(addrPair.path(MAC_ADDRESS).asText());
95 - vSgs.put(pairIp, pairMac); 97 + vsgInstances.put(pairIp, pairMac);
96 }); 98 });
97 99
98 - service.updateVirtualSubscriberGateways( 100 + service.updateVsgInstances(HostId.hostId(mac),
99 - HostId.hostId(mac), 101 + name.substring(STAG_BEGIN_INDEX),
100 - name.substring(STAG_BEGIN_INDEX), 102 + vsgInstances);
101 - vSgs);
102 } catch (Exception e) { 103 } catch (Exception e) {
103 return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); 104 return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
104 } 105 }
......