Refactored to handle service instance by service type
- Added service instance handler - Implemented dummy, vsg, and olt agent instance handler Change-Id: Id3edd5eecb1caadf0f835cb10a952100e18b283b
Showing
20 changed files
with
2647 additions
and
2127 deletions
... | @@ -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 | - | ||
303 | - HostId hostId = HostId.hostId(vtnPort.mac()); | ||
304 | - hostProvider.hostDetected(hostId, hostDesc, false); | ||
305 | - } | ||
306 | 138 | ||
307 | - @Override | 139 | + updateProviderServiceInstances(service); |
308 | - public void removeServiceVm(ConnectPoint connectPoint) { | ||
309 | - hostService.getConnectedHosts(connectPoint) | ||
310 | - .stream() | ||
311 | - .forEach(host -> hostProvider.hostVanished(host.id())); | ||
312 | } | 140 | } |
313 | 141 | ||
314 | @Override | 142 | @Override |
315 | - // TODO address service specific task in a separate package | 143 | + public void instanceRemoved(Instance instance) { |
316 | - public void updateVirtualSubscriberGateways(HostId vSgHostId, String serviceVlan, | 144 | + VtnService service = getVtnService(instance.serviceId()); |
317 | - Map<IpAddress, MacAddress> vSgs) { | 145 | + if (service == null) { |
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()) | 151 | + } |
327 | - .forEach(entry -> addVirtualSubscriberGateway( | 152 | + if (!service.tenantServices().isEmpty()) { |
328 | - vSgHost, | 153 | + updateProviderServiceInstances(service); |
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 | } | 154 | } |
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 | } | 155 | } |
370 | 156 | ||
371 | - /** | 157 | + private void updateProviderServiceInstances(VtnService service) { |
372 | - * Returns public ip addresses of vSGs running inside a give vSG host. | 158 | + GroupKey groupKey = getGroupKey(service.id()); |
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 | 159 | ||
382 | - String vtnPortId = vSgHost.annotations().value(PORT_ID); | 160 | + Set<DeviceId> devices = nodeManager.completeNodes().stream() |
383 | - String sTag = vSgHost.annotations().value(S_TAG); | 161 | + .map(CordVtnNode::intBrId) |
162 | + .collect(Collectors.toSet()); | ||
384 | 163 | ||
385 | - if (Strings.isNullOrEmpty(vtnPortId) || Strings.isNullOrEmpty(sTag)) { | 164 | + for (DeviceId deviceId : devices) { |
386 | - log.warn("PORT_ID and S_TAG is not set, ignore {}", vSgHost); | 165 | + Group group = groupService.getGroup(deviceId, groupKey); |
387 | - return Maps.newHashMap(); | 166 | + if (group == null) { |
167 | + log.trace("No group exists for service {} in {}", service.id(), deviceId); | ||
168 | + continue; | ||
388 | } | 169 | } |
389 | 170 | ||
390 | - // TODO remove openstack access when XOS provides all information | 171 | + List<GroupBucket> oldBuckets = group.buckets().buckets(); |
391 | - VtnPortApi portApi = xosClient.getClient(xosAccess).vtnPort(); | 172 | + List<GroupBucket> newBuckets = getServiceGroupBuckets( |
392 | - VtnPort vtnPort = portApi.vtnPort(VtnPortId.of(vtnPortId), osAccess); | 173 | + deviceId, service.vni(), getInstances(service.id())).buckets(); |
393 | - if (vtnPort == null) { | ||
394 | - log.warn("Failed to get port information of {}", vSgHost); | ||
395 | - return Maps.newHashMap(); | ||
396 | - } | ||
397 | 174 | ||
398 | - if (!sTag.equals(getServiceVlan(vtnPort))) { | 175 | + if (oldBuckets.equals(newBuckets)) { |
399 | - log.error("Host({}) s-tag does not match with VTN port s-tag", vSgHost); | 176 | + continue; |
400 | - return Maps.newHashMap(); | ||
401 | - } | ||
402 | - return vtnPort.addressPairs(); | ||
403 | } | 177 | } |
404 | 178 | ||
405 | - /** | 179 | + List<GroupBucket> bucketsToRemove = Lists.newArrayList(oldBuckets); |
406 | - * Returns s-tag from a given VTN port. | 180 | + bucketsToRemove.removeAll(newBuckets); |
407 | - * | 181 | + if (!bucketsToRemove.isEmpty()) { |
408 | - * @param vtnPort vtn port | 182 | + groupService.removeBucketsFromGroup( |
409 | - * @return s-tag string | 183 | + deviceId, |
410 | - */ | 184 | + groupKey, |
411 | - // TODO address service specific task in a separate package | 185 | + new GroupBuckets(bucketsToRemove), |
412 | - private String getServiceVlan(VtnPort vtnPort) { | 186 | + groupKey, appId); |
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 | } | 187 | } |
422 | 188 | ||
423 | - /** | 189 | + List<GroupBucket> bucketsToAdd = Lists.newArrayList(newBuckets); |
424 | - * Returns instances with a given network service. | 190 | + bucketsToAdd.removeAll(oldBuckets); |
425 | - * | 191 | + if (!bucketsToAdd.isEmpty()) { |
426 | - * @param serviceId service id | 192 | + groupService.addBucketsToGroup( |
427 | - * @return set of hosts | 193 | + deviceId, |
428 | - */ | 194 | + groupKey, |
429 | - private Set<Host> getInstances(VtnServiceId serviceId) { | 195 | + new GroupBuckets(bucketsToAdd), |
430 | - return StreamSupport.stream(hostService.getHosts().spliterator(), false) | 196 | + groupKey, appId); |
431 | - .filter(host -> Objects.equals( | ||
432 | - serviceId.id(), | ||
433 | - host.annotations().value(SERVICE_ID))) | ||
434 | - .collect(Collectors.toSet()); | ||
435 | } | 197 | } |
436 | - | ||
437 | - /** | ||
438 | - * Registers static DHCP lease for a given host. | ||
439 | - * | ||
440 | - * @param host host | ||
441 | - * @param service cord service | ||
442 | - */ | ||
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 | - | ||
457 | - /** | ||
458 | - * Handles VM detected situation. | ||
459 | - * | ||
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 | } | 198 | } |
471 | - | ||
472 | - String serviceId = host.annotations().value(SERVICE_ID); | ||
473 | - if (Strings.isNullOrEmpty(serviceId)) { | ||
474 | - // ignore this host, it is not a service instance | ||
475 | - return; | ||
476 | } | 199 | } |
477 | 200 | ||
478 | - log.info("Instance is detected {}", host); | 201 | + private void removeInstanceFromTenantService(Instance instance, VtnService service) { |
202 | + service.providerServices().stream().forEach(pServiceId -> { | ||
203 | + Map<DeviceId, Set<PortNumber>> inPorts = Maps.newHashMap(); | ||
204 | + Map<DeviceId, GroupId> outGroups = Maps.newHashMap(); | ||
479 | 205 | ||
480 | - // TODO remove openstack access when XOS provides all information | 206 | + inPorts.put(instance.deviceId(), Sets.newHashSet(instance.portNumber())); |
481 | - VtnServiceApi serviceApi = xosClient.getClient(xosAccess).vtnService(); | 207 | + outGroups.put(instance.deviceId(), getGroupId(pServiceId, instance.deviceId())); |
482 | - VtnService service = serviceApi.service(VtnServiceId.of(serviceId), osAccess); | ||
483 | - if (service == null) { | ||
484 | - log.warn("Failed to get VtnService for {}", serviceId); | ||
485 | - return; | ||
486 | - } | ||
487 | 208 | ||
488 | - switch (service.networkType()) { | 209 | + inServiceRule(inPorts, outGroups, false); |
489 | - case MANAGEMENT: | 210 | + }); |
490 | - ruleInstaller.populateManagementNetworkRules(host, service); | ||
491 | - break; | ||
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 | } | 211 | } |
508 | 212 | ||
509 | - registerDhcpLease(host, service); | 213 | + private void serviceDependencyRules(VtnService tService, VtnService pService, |
510 | - ruleInstaller.populateBasicConnectionRules(host, service, true); | 214 | + boolean isBidirectional, boolean install) { |
511 | - } | 215 | + Map<DeviceId, GroupId> outGroups = Maps.newHashMap(); |
216 | + Map<DeviceId, Set<PortNumber>> inPorts = Maps.newHashMap(); | ||
512 | 217 | ||
513 | - /** | 218 | + nodeManager.completeNodes().stream().forEach(node -> { |
514 | - * Handles VM removed situation. | 219 | + DeviceId deviceId = node.intBrId(); |
515 | - * | 220 | + GroupId groupId = createServiceGroup(deviceId, pService); |
516 | - * @param host host | 221 | + outGroups.put(deviceId, groupId); |
517 | - */ | ||
518 | - private void serviceVmRemoved(Host host) { | ||
519 | - checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR); | ||
520 | - checkNotNull(xosAccess, XOS_ACCESS_ERROR); | ||
521 | 222 | ||
522 | - // TODO address service specific task in a separate package | 223 | + Set<PortNumber> tServiceInstances = getInstances(tService.id()) |
523 | - if (host.annotations().value(S_TAG) != null) { | 224 | + .stream() |
524 | - virtualSubscriberGatewayRemoved(host); | 225 | + .filter(instance -> instance.deviceId().equals(deviceId)) |
525 | - } | 226 | + .map(Instance::portNumber) |
227 | + .collect(Collectors.toSet()); | ||
228 | + inPorts.put(deviceId, tServiceInstances); | ||
229 | + }); | ||
526 | 230 | ||
527 | - String serviceId = host.annotations().value(SERVICE_ID); | 231 | + Ip4Prefix srcRange = tService.subnet().getIp4Prefix(); |
528 | - if (Strings.isNullOrEmpty(serviceId)) { | 232 | + Ip4Prefix dstRange = pService.subnet().getIp4Prefix(); |
529 | - // ignore this host, it is not a service instance | 233 | + |
530 | - return; | 234 | + indirectAccessRule(srcRange, pService.serviceIp().getIp4Address(), outGroups, install); |
235 | + directAccessRule(srcRange, dstRange, install); | ||
236 | + if (isBidirectional) { | ||
237 | + directAccessRule(dstRange, srcRange, install); | ||
238 | + } | ||
239 | + inServiceRule(inPorts, outGroups, install); | ||
240 | + } | ||
241 | + | ||
242 | + private void indirectAccessRule(Ip4Prefix srcRange, Ip4Address serviceIp, | ||
243 | + Map<DeviceId, GroupId> outGroups, boolean install) { | ||
244 | + TrafficSelector selector = DefaultTrafficSelector.builder() | ||
245 | + .matchEthType(Ethernet.TYPE_IPV4) | ||
246 | + .matchIPSrc(srcRange) | ||
247 | + .matchIPDst(serviceIp.toIpPrefix()) | ||
248 | + .build(); | ||
249 | + | ||
250 | + for (Map.Entry<DeviceId, GroupId> outGroup : outGroups.entrySet()) { | ||
251 | + TrafficTreatment treatment = DefaultTrafficTreatment.builder() | ||
252 | + .group(outGroup.getValue()) | ||
253 | + .build(); | ||
254 | + | ||
255 | + FlowRule flowRule = DefaultFlowRule.builder() | ||
256 | + .fromApp(appId) | ||
257 | + .withSelector(selector) | ||
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); | ||
266 | + } | ||
267 | + } | ||
268 | + | ||
269 | + private void directAccessRule(Ip4Prefix srcRange, Ip4Prefix dstRange, boolean install) { | ||
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 | + }); | ||
531 | } | 294 | } |
532 | 295 | ||
533 | - log.info("Instance is vanished {}", host); | 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(); | ||
534 | 301 | ||
535 | - // TODO remove openstack access when XOS provides all information | 302 | + GroupId groupId = outGroups.get(deviceId); |
536 | - VtnServiceApi vtnServiceApi = xosClient.getClient(xosAccess).vtnService(); | 303 | + if (groupId == null) { |
537 | - VtnService service = vtnServiceApi.service(VtnServiceId.of(serviceId), osAccess); | 304 | + continue; |
538 | - if (service == null) { | ||
539 | - log.warn("Failed to get VtnService for {}", serviceId); | ||
540 | - return; | ||
541 | } | 305 | } |
542 | 306 | ||
543 | - // TODO need to consider the case that the service is removed also | 307 | + ports.stream().forEach(port -> { |
544 | - switch (service.networkType()) { | 308 | + TrafficSelector selector = DefaultTrafficSelector.builder() |
545 | - case MANAGEMENT: | 309 | + .matchInPort(port) |
546 | - break; | 310 | + .build(); |
547 | - case PRIVATE: | ||
548 | - if (getInstances(VtnServiceId.of(serviceId)).isEmpty()) { | ||
549 | - arpProxy.removeGateway(service.serviceIp()); | ||
550 | - } | ||
551 | - case PUBLIC: | ||
552 | - default: | ||
553 | - if (!service.tenantServices().isEmpty()) { | ||
554 | - ruleInstaller.updateProviderServiceGroup(service); | ||
555 | - } | ||
556 | - if (!service.providerServices().isEmpty()) { | ||
557 | - ruleInstaller.updateTenantServiceVm(host, service); | ||
558 | - } | ||
559 | - break; | ||
560 | - } | ||
561 | 311 | ||
562 | - dhcpService.removeStaticMapping(host.mac()); | 312 | + TrafficTreatment treatment = DefaultTrafficTreatment.builder() |
563 | - ruleInstaller.populateBasicConnectionRules(host, service, false); | 313 | + .group(groupId) |
564 | - } | 314 | + .build(); |
565 | 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(); | ||
566 | 325 | ||
567 | - /** | 326 | + pipeline.processFlowRule(install, flowRule); |
568 | - * Handles virtual subscriber gateway VM or container. | 327 | + }); |
569 | - * | ||
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 | } | 328 | } |
594 | - | ||
595 | - log.info("vSG detected {}", host.id()); | ||
596 | - vSgs = getSubscriberGateways(vSgHost); | ||
597 | } | 329 | } |
598 | 330 | ||
599 | - ruleInstaller.populateSubscriberGatewayRules(vSgHost, vSgs.keySet()); | 331 | + private GroupId getGroupId(VtnServiceId serviceId, DeviceId deviceId) { |
332 | + return new DefaultGroupId(Objects.hash(serviceId, deviceId)); | ||
600 | } | 333 | } |
601 | 334 | ||
602 | - /** | 335 | + private GroupKey getGroupKey(VtnServiceId serviceId) { |
603 | - * Handles virtual subscriber gateway removed. | 336 | + return new DefaultGroupKey(serviceId.id().getBytes()); |
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 | } | 337 | } |
613 | 338 | ||
614 | - Host vSgHost = hostService.getHost(HostId.hostId(vSgHostId)); | 339 | + private GroupId createServiceGroup(DeviceId deviceId, VtnService service) { |
615 | - if (vSgHost == null) { | 340 | + GroupKey groupKey = getGroupKey(service.id()); |
616 | - return; | 341 | + Group group = groupService.getGroup(deviceId, groupKey); |
617 | - } | 342 | + GroupId groupId = getGroupId(service.id(), deviceId); |
618 | 343 | ||
619 | - log.info("vSG removed {}", vSg.id()); | 344 | + if (group != null) { |
620 | - Map<IpAddress, MacAddress> vSgs = getSubscriberGateways(vSgHost); | 345 | + log.debug("Group {} is already exist in {}", service.id(), deviceId); |
621 | - ruleInstaller.populateSubscriberGatewayRules(vSgHost, vSgs.keySet()); | 346 | + return groupId; |
622 | } | 347 | } |
623 | 348 | ||
624 | - /** | 349 | + GroupBuckets buckets = getServiceGroupBuckets( |
625 | - * Sets service network gateway MAC address and sends out gratuitous ARP to all | 350 | + deviceId, service.vni(), getInstances(service.id())); |
626 | - * VMs to update the gateway MAC address. | 351 | + GroupDescription groupDescription = new DefaultGroupDescription( |
627 | - * | 352 | + deviceId, |
628 | - * @param newMac mac address to update | 353 | + GroupDescription.Type.SELECT, |
629 | - */ | 354 | + buckets, |
630 | - private void setPrivateGatewayMac(MacAddress newMac) { | 355 | + groupKey, |
631 | - checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR); | 356 | + groupId.id(), |
632 | - checkNotNull(xosAccess, XOS_ACCESS_ERROR); | 357 | + appId); |
633 | 358 | ||
634 | - if (newMac == null || newMac.equals(privateGatewayMac)) { | 359 | + groupService.addGroup(groupDescription); |
635 | - // no updates, do nothing | 360 | + return groupId; |
636 | - return; | ||
637 | } | 361 | } |
638 | 362 | ||
639 | - privateGatewayMac = newMac; | 363 | + private GroupBuckets getServiceGroupBuckets(DeviceId deviceId, long tunnelId, |
640 | - log.debug("Set service gateway MAC address to {}", privateGatewayMac.toString()); | 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(); | ||
641 | 369 | ||
642 | - VtnServiceApi vtnServiceApi = xosClient.getClient(xosAccess).vtnService(); | 370 | + if (deviceId.equals(instance.deviceId())) { |
643 | - vtnServiceApi.services().stream().forEach(serviceId -> { | 371 | + tBuilder.setEthDst(instance.mac()) |
644 | - VtnService service = vtnServiceApi.service(serviceId, osAccess); | 372 | + .setOutput(instance.portNumber()); |
645 | - if (service != null) { | 373 | + } else { |
646 | - arpProxy.addGateway(service.serviceIp(), privateGatewayMac); | 374 | + ExtensionTreatment tunnelDst = |
647 | - arpProxy.sendGratuitousArpForGateway(service.serviceIp(), getInstances(serviceId)); | 375 | + pipeline.tunnelDstTreatment(deviceId, tunnelIp); |
648 | - } | 376 | + tBuilder.setEthDst(instance.mac()) |
649 | - }); | 377 | + .extension(tunnelDst, deviceId) |
650 | - } | 378 | + .setTunnelId(tunnelId) |
651 | - | 379 | + .setOutput(nodeManager.tunnelPort(instance.deviceId())); |
652 | - /** | 380 | + } |
653 | - * Sets public gateway MAC address. | 381 | + buckets.add(createSelectGroupBucket(tBuilder.build())); |
654 | - * | ||
655 | - * @param publicGateways gateway ip and mac address pairs | ||
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 | }); | 382 | }); |
665 | - // TODO notice gateway MAC change to VMs holds this gateway IP | 383 | + return new GroupBuckets(buckets); |
666 | - } | ||
667 | - | ||
668 | - /** | ||
669 | - * Updates configurations. | ||
670 | - */ | ||
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 | - } | ||
677 | - | ||
678 | - xosAccess = config.xosAccess(); | ||
679 | - osAccess = config.openstackAccess(); | ||
680 | - | ||
681 | - setPrivateGatewayMac(config.privateGatewayMac()); | ||
682 | - setPublicGatewayMac(config.publicGateways()); | ||
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 | +} |
apps/cordvtn/src/main/java/org/onosproject/cordvtn/impl/CordVtnRuleInstaller.java
deleted
100644 → 0
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 | - |
apps/cordvtn/src/main/java/org/onosproject/cordvtn/impl/service/DummyInstanceHandler.java
0 → 100644
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 | +} |
apps/cordvtn/src/main/java/org/onosproject/cordvtn/impl/service/OltAgentInstanceHandler.java
0 → 100644
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), | ||
100 | name.substring(STAG_BEGIN_INDEX), | 101 | name.substring(STAG_BEGIN_INDEX), |
101 | - vSgs); | 102 | + vsgInstances); |
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 | } | ... | ... |
-
Please register or login to post a comment