Sho SHIMIZU
Committed by Gerrit Code Review

Separate resource search from resource allocation

This is a preparation task for the future Intent Framework major
enhancement that aims to consolidate resource allocation invocations
into the Framework side instead of the compiler side.

Declaring required resources and allocating the resources need to be
clearly separated. This patch tries to separate these phases.

Change-Id: Id254fe103803daf60ef2576fb5d717e9faa68c03
...@@ -97,7 +97,7 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical ...@@ -97,7 +97,7 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical
97 @Override 97 @Override
98 public List<Intent> compile(OpticalConnectivityIntent intent, 98 public List<Intent> compile(OpticalConnectivityIntent intent,
99 List<Intent> installable, 99 List<Intent> installable,
100 - Set<LinkResourceAllocations> resources) { 100 + Set<LinkResourceAllocations> linkResources) {
101 // Check if source and destination are optical OCh ports 101 // Check if source and destination are optical OCh ports
102 ConnectPoint src = intent.getSrc(); 102 ConnectPoint src = intent.getSrc();
103 ConnectPoint dst = intent.getDst(); 103 ConnectPoint dst = intent.getDst();
...@@ -113,94 +113,117 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical ...@@ -113,94 +113,117 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical
113 // TODO: try to release intent resources in IntentManager. 113 // TODO: try to release intent resources in IntentManager.
114 resourceService.release(intent.id()); 114 resourceService.release(intent.id());
115 115
116 - // Reserve OCh ports 116 + // Check OCh port availability
117 Resource srcPortResource = Resources.discrete(src.deviceId(), src.port()).resource(); 117 Resource srcPortResource = Resources.discrete(src.deviceId(), src.port()).resource();
118 Resource dstPortResource = Resources.discrete(dst.deviceId(), dst.port()).resource(); 118 Resource dstPortResource = Resources.discrete(dst.deviceId(), dst.port()).resource();
119 - List<ResourceAllocation> allocation = resourceService.allocate(intent.id(), srcPortResource, dstPortResource); 119 + // If ports are not available, compilation fails
120 - if (allocation.isEmpty()) { 120 + if (!Stream.of(srcPortResource, dstPortResource).allMatch(resourceService::isAvailable)) {
121 - throw new IntentCompilationException("Unable to reserve ports for intent " + intent); 121 + throw new IntentCompilationException("Ports for the intent are not available. Intent: " + intent);
122 } 122 }
123 123
124 + List<Resource> resources = new ArrayList<>();
125 + resources.add(srcPortResource);
126 + resources.add(dstPortResource);
127 +
124 // Calculate available light paths 128 // Calculate available light paths
125 Set<Path> paths = getOpticalPaths(intent); 129 Set<Path> paths = getOpticalPaths(intent);
126 130
131 + if (paths.isEmpty()) {
132 + throw new IntentCompilationException("Unable to find suitable lightpath for intent " + intent);
133 + }
134 +
127 // Static or dynamic lambda allocation 135 // Static or dynamic lambda allocation
128 String staticLambda = srcPort.annotations().value(AnnotationKeys.STATIC_LAMBDA); 136 String staticLambda = srcPort.annotations().value(AnnotationKeys.STATIC_LAMBDA);
129 OchPort srcOchPort = (OchPort) srcPort; 137 OchPort srcOchPort = (OchPort) srcPort;
130 OchPort dstOchPort = (OchPort) dstPort; 138 OchPort dstOchPort = (OchPort) dstPort;
131 - OchSignal ochSignal; 139 +
140 + Path firstPath = paths.iterator().next();
141 + // FIXME: need to actually reserve the lambda for static lambda's
142 + // static lambda case: early return
143 + if (staticLambda != null) {
144 + allocateResources(intent, resources);
145 +
146 + OchSignal lambda = new OchSignal(Frequency.ofHz(Long.parseLong(staticLambda)),
147 + srcOchPort.lambda().channelSpacing(),
148 + srcOchPort.lambda().slotGranularity());
149 + return ImmutableList.of(createIntent(intent, firstPath, lambda));
150 + }
151 +
152 + // FIXME: also check destination OCh port
153 + // non-tunable case: early return
154 + if (!srcOchPort.isTunable() || !dstOchPort.isTunable()) {
155 + allocateResources(intent, resources);
156 +
157 + OchSignal lambda = srcOchPort.lambda();
158 + return ImmutableList.of(createIntent(intent, firstPath, lambda));
159 + }
132 160
133 // Use first path that can be successfully reserved 161 // Use first path that can be successfully reserved
134 for (Path path : paths) { 162 for (Path path : paths) {
135 - 163 + // find common lambda on path
136 - // FIXME: need to actually reserve the lambda for static lambda's 164 + // a list of OchSignal indicates consecutive OchSignals
137 - if (staticLambda != null) { 165 + List<OchSignal> lambda = findFirstAvailableOch(path);
138 - ochSignal = new OchSignal(Frequency.ofHz(Long.parseLong(staticLambda)), 166 + if (lambda.isEmpty()) {
139 - srcOchPort.lambda().channelSpacing(), 167 + continue;
140 - srcOchPort.lambda().slotGranularity()); 168 + }
141 - } else if (!srcOchPort.isTunable() || !dstOchPort.isTunable()) { 169 + List<Resource> lambdaResources = convertToResources(path.links(), lambda);
142 - // FIXME: also check destination OCh port 170 + if (!lambdaResources.stream().allMatch(resourceService::isAvailable)) {
143 - ochSignal = srcOchPort.lambda(); 171 + continue;
144 - } else {
145 - // Request and reserve lambda on path
146 - List<OchSignal> lambdas = assignWavelength(intent, path);
147 - if (lambdas.isEmpty()) {
148 - continue;
149 - }
150 - ochSignal = OchSignal.toFixedGrid(lambdas, ChannelSpacing.CHL_50GHZ);
151 } 172 }
173 + resources.addAll(lambdaResources);
152 174
153 - // Create installable optical path intent 175 + allocateResources(intent, resources);
154 - // Only support fixed grid for now
155 - OchSignalType signalType = OchSignalType.FIXED_GRID;
156 -
157 - Intent newIntent = OpticalPathIntent.builder()
158 - .appId(intent.appId())
159 - .src(intent.getSrc())
160 - .dst(intent.getDst())
161 - .path(path)
162 - .lambda(ochSignal)
163 - .signalType(signalType)
164 - .bidirectional(intent.isBidirectional())
165 - .build();
166 -
167 - return ImmutableList.of(newIntent);
168 - }
169 176
170 - // Release port allocations if unsuccessful 177 + OchSignal ochSignal = OchSignal.toFixedGrid(lambda, ChannelSpacing.CHL_50GHZ);
171 - resourceService.release(intent.id()); 178 + return ImmutableList.of(createIntent(intent, path, ochSignal));
179 + }
172 180
173 throw new IntentCompilationException("Unable to find suitable lightpath for intent " + intent); 181 throw new IntentCompilationException("Unable to find suitable lightpath for intent " + intent);
174 } 182 }
175 183
176 - /** 184 + private Intent createIntent(OpticalConnectivityIntent parentIntent, Path path, OchSignal lambda) {
177 - * Request and reserve first available wavelength across path. 185 + // Create installable optical path intent
178 - * 186 + // Only support fixed grid for now
179 - * @param path path in WDM topology 187 + OchSignalType signalType = OchSignalType.FIXED_GRID;
180 - * @return first available lambda allocated 188 +
181 - */ 189 + return OpticalPathIntent.builder()
182 - private List<OchSignal> assignWavelength(Intent intent, Path path) { 190 + .appId(parentIntent.appId())
191 + .src(parentIntent.getSrc())
192 + .dst(parentIntent.getDst())
193 + // calling paths.iterator().next() is safe because of non-empty set
194 + .path(path)
195 + .lambda(lambda)
196 + .signalType(signalType)
197 + .bidirectional(parentIntent.isBidirectional())
198 + .build();
199 + }
200 +
201 + private void allocateResources(Intent intent, List<Resource> resources) {
202 + // reserve all of required resources
203 + List<ResourceAllocation> allocations = resourceService.allocate(intent.id(), resources);
204 + if (allocations.isEmpty()) {
205 + log.info("Resource allocation for {} failed (resource request: {})", intent, resources);
206 + throw new IntentCompilationException("Unable to allocate resources: " + resources);
207 + }
208 + }
209 +
210 + private List<OchSignal> findFirstAvailableOch(Path path) {
183 Set<OchSignal> lambdas = findCommonLambdasOverLinks(path.links()); 211 Set<OchSignal> lambdas = findCommonLambdasOverLinks(path.links());
184 if (lambdas.isEmpty()) { 212 if (lambdas.isEmpty()) {
185 return Collections.emptyList(); 213 return Collections.emptyList();
186 } 214 }
187 215
188 - List<OchSignal> minLambda = findFirstLambda(lambdas, slotCount()); 216 + return findFirstLambda(lambdas, slotCount());
189 - List<Resource> lambdaResources = path.links().stream() 217 + }
218 +
219 + private List<Resource> convertToResources(List<Link> links, List<OchSignal> lambda) {
220 + return links.stream()
190 .flatMap(x -> Stream.of( 221 .flatMap(x -> Stream.of(
191 Resources.discrete(x.src().deviceId(), x.src().port()).resource(), 222 Resources.discrete(x.src().deviceId(), x.src().port()).resource(),
192 Resources.discrete(x.dst().deviceId(), x.dst().port()).resource() 223 Resources.discrete(x.dst().deviceId(), x.dst().port()).resource()
193 )) 224 ))
194 - .flatMap(x -> minLambda.stream().map(l -> x.child(l))) 225 + .flatMap(x -> lambda.stream().map(x::child))
195 .collect(Collectors.toList()); 226 .collect(Collectors.toList());
196 -
197 - List<ResourceAllocation> allocations = resourceService.allocate(intent.id(), lambdaResources);
198 - if (allocations.isEmpty()) {
199 - log.info("Resource allocation for {} failed (resource request: {})", intent, lambdaResources);
200 - return Collections.emptyList();
201 - }
202 -
203 - return minLambda;
204 } 227 }
205 228
206 /** 229 /**
......