blob: 1fdebe1f9a825e887305b24157fbbf01a84e9761 [file] [log] [blame]
Brian Stanke11f6d532016-07-05 16:17:59 -04001/*
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
17package org.onosproject.net.intent.impl.compiler;
18
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.onlab.osgi.DefaultServiceDirectory;
25import org.onlab.osgi.ServiceDirectory;
26import org.onosproject.core.ApplicationId;
27import org.onosproject.incubator.net.tunnel.TunnelId;
28import org.onosproject.incubator.net.virtual.NetworkId;
29import org.onosproject.incubator.net.virtual.VirtualNetworkIntent;
30import org.onosproject.incubator.net.virtual.VirtualNetworkService;
31import org.onosproject.incubator.net.virtual.VirtualNetworkStore;
32import org.onosproject.incubator.net.virtual.VirtualPort;
33import org.onosproject.net.ConnectPoint;
Brian Stanke11f6d532016-07-05 16:17:59 -040034import org.onosproject.net.Link;
35import org.onosproject.net.Path;
Brian Stanke11f6d532016-07-05 16:17:59 -040036import org.onosproject.net.intent.Intent;
37import org.onosproject.net.intent.IntentService;
38import org.onosproject.net.intent.Key;
39import org.onosproject.net.intent.PointToPointIntent;
Brian Stanke11f6d532016-07-05 16:17:59 -040040import org.onosproject.net.intent.impl.IntentCompilationException;
41import org.onosproject.net.topology.TopologyService;
42import org.slf4j.Logger;
43
44import java.util.ArrayList;
45import java.util.List;
46import java.util.Optional;
47import java.util.Set;
48
49import static org.slf4j.LoggerFactory.getLogger;
50
51/**
52 * An intent compiler for {@link org.onosproject.incubator.net.virtual.VirtualNetworkIntent}.
53 */
54@Component(immediate = true)
55public class VirtualNetworkIntentCompiler
56 extends ConnectivityIntentCompiler<VirtualNetworkIntent> {
57
58 private final Logger log = getLogger(getClass());
59
60 private static final String NETWORK_ID = "networkId=";
61 protected static final String KEY_FORMAT = "{" + NETWORK_ID + "%s, src=%s, dst=%s}";
62
63 protected ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
64
65 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
66 protected VirtualNetworkService manager;
67
68 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
69 protected IntentService intentService;
70
71 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 protected VirtualNetworkStore store;
73
74
75 @Activate
76 public void activate() {
77 intentManager.registerCompiler(VirtualNetworkIntent.class, this);
78 }
79
80 @Deactivate
81 public void deactivate() {
82 intentManager.unregisterCompiler(VirtualNetworkIntent.class);
83 }
84
85 @Override
86 public List<Intent> compile(VirtualNetworkIntent intent, List<Intent> installable) {
87
88 log.debug("Compiling intent: " + intent);
89 List<Intent> intents = new ArrayList<>();
90 Optional<Path> path = getPaths(intent).stream()
91 .findFirst();
92 if (path != null && path.isPresent()) {
Brian Stankefb61df42016-07-25 11:47:51 -040093 List<Link> links = path.get().links();
Brian Stanke11f6d532016-07-05 16:17:59 -040094
Brian Stankefb61df42016-07-25 11:47:51 -040095 // First create an intent between the intent ingress CP and the first link source CP,
96 // only if the two CPs are not the same.
97 Link firstLink = links.get(0);
98 if (!intent.ingressPoint().equals(firstLink.src())) {
99 intents.add(createPtPtIntent(intent, intent.ingressPoint(), firstLink.src()));
100 }
101
102 // Next create an intent between the intent egress CP and the last link destination CP,
103 // only if the two CPs are not the same.
104 Link lastLink = links.get(links.size() - 1);
105 if (!intent.egressPoint().equals(lastLink.dst())) {
106 intents.add(createPtPtIntent(intent, lastLink.dst(), intent.egressPoint()));
107 }
108
109 // Now loop through all of the virtual links in the path and create an intent.
110 // An intent is also created connecting two virtual links.
111 final int[] index = {0};
112 links.forEach(link -> {
113 intents.add(createPtPtIntent(intent, link.src(), link.dst()));
114 if (index[0] > 0) {
115 Link previousLink = links.get(index[0] - 1);
116 intents.add(createPtPtIntent(intent, previousLink.dst(), link.src()));
117 }
118 index[0]++;
Brian Stanke11f6d532016-07-05 16:17:59 -0400119 });
120 } else {
121 throw new IntentCompilationException("Unable to find a path for intent " + intent);
122 }
123
124 return intents;
125 }
126
127 /**
128 * Returns the paths for the virtual network intent.
129 *
130 * @param intent virtual network intent
131 * @return set of paths
132 */
133 private Set<Path> getPaths(VirtualNetworkIntent intent) {
134
135 TopologyService topologyService = manager.get(intent.networkId(), TopologyService.class);
136 if (topologyService == null) {
137 throw new IntentCompilationException("topologyService is null");
138 }
139 return topologyService.getPaths(topologyService.currentTopology(),
140 intent.ingressPoint().deviceId(), intent.egressPoint().deviceId());
141 }
142
143 /**
Brian Stankefb61df42016-07-25 11:47:51 -0400144 * Encodes the key using the network identifier, application identifier, source and destination
Brian Stanke11f6d532016-07-05 16:17:59 -0400145 * connect points.
146 *
147 * @param networkId virtual network identifier
148 * @param applicationId application identifier
149 * @param src source connect point
150 * @param dst destination connect point
151 * @return encoded key
152 */
Brian Stankefb61df42016-07-25 11:47:51 -0400153
Brian Stanke11f6d532016-07-05 16:17:59 -0400154 private static Key encodeKey(NetworkId networkId, ApplicationId applicationId, ConnectPoint src, ConnectPoint dst) {
155 String key = String.format(KEY_FORMAT, networkId, src, dst);
156 return Key.of(key, applicationId);
157 }
158
159 /**
Brian Stankefb61df42016-07-25 11:47:51 -0400160 * Creates a point-to-point intent using the virtual network intent between the source and destination
161 * connect point.
Brian Stanke11f6d532016-07-05 16:17:59 -0400162 *
163 * @param intent virtual network intent
Brian Stankefb61df42016-07-25 11:47:51 -0400164 * @param src source connect point
165 * @param dst destination connect point
Brian Stanke11f6d532016-07-05 16:17:59 -0400166 * @return point to point intent
167 */
Brian Stankefb61df42016-07-25 11:47:51 -0400168 private Intent createPtPtIntent(VirtualNetworkIntent intent, ConnectPoint src, ConnectPoint dst) {
169 ConnectPoint ingressPoint = mapVirtualToPhysicalPort(intent.networkId(), src);
170 ConnectPoint egressPoint = mapVirtualToPhysicalPort(intent.networkId(), dst);
Brian Stanke11f6d532016-07-05 16:17:59 -0400171 Key intentKey = encodeKey(intent.networkId(), intent.appId(), ingressPoint, egressPoint);
172
Brian Stanke11f6d532016-07-05 16:17:59 -0400173 // TODO Currently there can only be one intent between the ingress and egress across
174 // all virtual networks. We may want to support multiple intents between the same src/dst pairs.
175 PointToPointIntent physicalIntent = PointToPointIntent.builder()
176 .key(intentKey)
177 .appId(intent.appId())
178 .ingressPoint(ingressPoint)
179 .egressPoint(egressPoint)
Brian Stankefb61df42016-07-25 11:47:51 -0400180 .constraints(intent.constraints())
181 .selector(intent.selector())
182 .treatment(intent.treatment())
Brian Stanke11f6d532016-07-05 16:17:59 -0400183 .build();
184 log.debug("Submitting physical intent: " + physicalIntent);
185 intentService.submit(physicalIntent);
186
Brian Stankefb61df42016-07-25 11:47:51 -0400187 // Store the physical intent against this virtual intent.
188 store.addTunnelId(intent, TunnelId.valueOf(physicalIntent.key().toString()));
189
Brian Stanke11f6d532016-07-05 16:17:59 -0400190 return physicalIntent;
191 }
192
193 /**
194 * Maps the virtual connect point to a physical connect point.
195 *
196 * @param networkId virtual network identifier
197 * @param virtualCp virtual connect point
198 * @return physical connect point
199 */
200 private ConnectPoint mapVirtualToPhysicalPort(NetworkId networkId, ConnectPoint virtualCp) {
201 Set<VirtualPort> ports = manager.getVirtualPorts(networkId, virtualCp.deviceId());
202 for (VirtualPort port : ports) {
203 if (port.element().id().equals(virtualCp.elementId()) &&
204 port.number().equals(virtualCp.port())) {
205 return new ConnectPoint(port.realizedBy().element().id(), port.realizedBy().number());
206 }
207 }
208 return null;
209 }
210}
211