blob: 89a685bf999cfc5fa0dc2e208f0a4fc6b3e56610 [file] [log] [blame]
Avantika-Huawei73862d42016-05-12 18:58:06 +05301/*
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 */
16package org.onosproject.pce.pceservice;
17
18import static com.google.common.base.Preconditions.checkNotNull;
Avantika-Huawei73862d42016-05-12 18:58:06 +053019
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053020import java.util.Collection;
Priyanka Bb6963582016-05-20 20:21:20 +053021import java.util.Collections;
Avantika-Huawei73862d42016-05-12 18:58:06 +053022import java.util.Iterator;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053023import java.util.LinkedList;
Avantika-Huawei73862d42016-05-12 18:58:06 +053024import java.util.List;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053025import java.util.Map;
26import java.util.Optional;
27import java.util.Map.Entry;
Priyanka Bb6963582016-05-20 20:21:20 +053028import java.util.Set;
Priyanka B9fa4ed32016-05-27 11:59:24 +053029import java.util.concurrent.Executors;
30import java.util.concurrent.ScheduledExecutorService;
31import java.util.concurrent.TimeUnit;
Avantika-Huawei73862d42016-05-12 18:58:06 +053032
Avantika-Huaweid1e36bd2016-05-26 12:47:16 +053033import org.onlab.packet.Ethernet;
34import org.onlab.packet.IPv4;
35
Avantika-Huawei73862d42016-05-12 18:58:06 +053036import org.apache.felix.scr.annotations.Activate;
37import org.apache.felix.scr.annotations.Component;
38import org.apache.felix.scr.annotations.Deactivate;
39import org.apache.felix.scr.annotations.Reference;
40import org.apache.felix.scr.annotations.ReferenceCardinality;
41import org.apache.felix.scr.annotations.Service;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053042import org.onlab.packet.IpAddress;
43import org.onlab.packet.IpPrefix;
Avantika-Huaweid1e36bd2016-05-26 12:47:16 +053044import org.onlab.packet.TCP;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053045import org.onlab.util.Bandwidth;
Avantika-Huawei73862d42016-05-12 18:58:06 +053046import org.onosproject.core.ApplicationId;
47import org.onosproject.core.CoreService;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053048import org.onosproject.incubator.net.resource.label.LabelResourceAdminService;
49import org.onosproject.incubator.net.resource.label.LabelResourceId;
50import org.onosproject.incubator.net.resource.label.LabelResourceService;
Avantika-Huawei73862d42016-05-12 18:58:06 +053051import org.onosproject.core.IdGenerator;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053052import org.onosproject.incubator.net.tunnel.DefaultTunnel;
53import org.onosproject.incubator.net.tunnel.IpTunnelEndPoint;
54import org.onosproject.incubator.net.tunnel.LabelStack;
Avantika-Huawei73862d42016-05-12 18:58:06 +053055import org.onosproject.incubator.net.tunnel.Tunnel;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053056import org.onosproject.incubator.net.tunnel.TunnelEndPoint;
57import org.onosproject.incubator.net.tunnel.TunnelEvent;
Avantika-Huawei73862d42016-05-12 18:58:06 +053058import org.onosproject.incubator.net.tunnel.TunnelId;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053059import org.onosproject.incubator.net.tunnel.TunnelListener;
60import org.onosproject.incubator.net.tunnel.TunnelName;
Avantika-Huawei73862d42016-05-12 18:58:06 +053061import org.onosproject.incubator.net.tunnel.TunnelService;
Priyanka B3f92c5a2016-05-27 10:14:16 +053062import org.onosproject.mastership.MastershipService;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053063import org.onosproject.net.DefaultAnnotations;
64import org.onosproject.net.DefaultAnnotations.Builder;
65import org.onosproject.net.Device;
Avantika-Huawei73862d42016-05-12 18:58:06 +053066import org.onosproject.net.DeviceId;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053067import org.onosproject.net.Link;
Priyanka Bb6963582016-05-20 20:21:20 +053068import org.onosproject.net.Path;
Priyanka Bb6963582016-05-20 20:21:20 +053069import org.onosproject.net.device.DeviceService;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053070import org.onosproject.net.flowobjective.FlowObjectiveService;
71import org.onosproject.net.flowobjective.Objective;
72import org.onosproject.net.intent.Constraint;
73import org.onosproject.net.intent.constraint.BandwidthConstraint;
Priyanka B3f92c5a2016-05-27 10:14:16 +053074import org.onosproject.net.link.LinkEvent;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053075import org.onosproject.pce.pceservice.constraint.CapabilityConstraint;
76import org.onosproject.pce.pceservice.constraint.CapabilityConstraint.CapabilityType;
77import org.onosproject.pce.pceservice.constraint.CostConstraint;
78import org.onosproject.pce.pceservice.constraint.SharedBandwidthConstraint;
79import org.onosproject.net.resource.Resource;
80import org.onosproject.net.resource.ResourceAllocation;
81import org.onosproject.net.resource.ResourceConsumer;
82import org.onosproject.net.resource.ResourceQueryService;
Priyanka Bb6963582016-05-20 20:21:20 +053083import org.onosproject.net.resource.ResourceService;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053084import org.onosproject.net.resource.Resources;
Priyanka Bb6963582016-05-20 20:21:20 +053085import org.onosproject.net.topology.LinkWeight;
86import org.onosproject.net.topology.PathService;
87import org.onosproject.net.topology.TopologyEdge;
Priyanka B3f92c5a2016-05-27 10:14:16 +053088import org.onosproject.net.topology.TopologyEvent;
89import org.onosproject.net.topology.TopologyListener;
90import org.onosproject.net.topology.TopologyService;
Avantika-Huawei73862d42016-05-12 18:58:06 +053091import org.onosproject.pce.pceservice.api.PceService;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +053092import org.onosproject.pce.pcestore.PcePathInfo;
93import org.onosproject.pce.pcestore.PceccTunnelInfo;
94import org.onosproject.pce.pcestore.api.PceStore;
Avantika-Huawei73862d42016-05-12 18:58:06 +053095import org.onosproject.store.serializers.KryoNamespaces;
96import org.onosproject.store.service.DistributedSet;
97import org.onosproject.store.service.Serializer;
98import org.onosproject.store.service.StorageService;
99import org.slf4j.Logger;
100import org.slf4j.LoggerFactory;
101
Priyanka Bb6963582016-05-20 20:21:20 +0530102import com.google.common.collect.ImmutableList;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530103import com.google.common.collect.ImmutableSet;
104
105import static org.onosproject.incubator.net.tunnel.Tunnel.Type.MPLS;
106import static org.onosproject.incubator.net.tunnel.Tunnel.State.INIT;
107import static org.onosproject.incubator.net.tunnel.Tunnel.State.ESTABLISHED;
108import static org.onosproject.incubator.net.tunnel.Tunnel.State.UNSTABLE;
109import static org.onosproject.pce.pceservice.LspType.WITH_SIGNALLING;
110import static org.onosproject.pce.pceservice.LspType.SR_WITHOUT_SIGNALLING;
111import static org.onosproject.pce.pceservice.LspType.WITHOUT_SIGNALLING_AND_WITHOUT_SR;
112
113import static org.onosproject.pce.pceservice.PcepAnnotationKeys.BANDWIDTH;
114import static org.onosproject.pce.pceservice.PcepAnnotationKeys.LOCAL_LSP_ID;
115import static org.onosproject.pce.pceservice.PcepAnnotationKeys.LSP_SIG_TYPE;
116import static org.onosproject.pce.pceservice.PcepAnnotationKeys.PCE_INIT;
117import static org.onosproject.pce.pceservice.PcepAnnotationKeys.PLSP_ID;
118import static org.onosproject.pce.pceservice.PcepAnnotationKeys.PCC_TUNNEL_ID;
119import static org.onosproject.pce.pceservice.PcepAnnotationKeys.DELEGATE;
120import static org.onosproject.pce.pceservice.PcepAnnotationKeys.COST_TYPE;
121
122import org.onosproject.net.packet.InboundPacket;
123import org.onosproject.net.packet.PacketContext;
124import org.onosproject.net.packet.PacketProcessor;
125import org.onosproject.net.packet.PacketService;
Priyanka Bb6963582016-05-20 20:21:20 +0530126
Avantika-Huawei73862d42016-05-12 18:58:06 +0530127/**
128 * Implementation of PCE service.
129 */
130@Component(immediate = true)
131@Service
132public class PceManager implements PceService {
133 private static final Logger log = LoggerFactory.getLogger(PceManager.class);
134
135 public static final String PCE_SERVICE_APP = "org.onosproject.pce";
Avantika-Huawei73862d42016-05-12 18:58:06 +0530136 private static final String LOCAL_LSP_ID_GEN_TOPIC = "pcep-local-lsp-id";
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530137 private static final int PREFIX_LENGTH = 32;
138
139 private static final String TUNNEL_CONSUMER_ID_GEN_TOPIC = "pcep-tunnel-consumer-id";
140 private IdGenerator tunnelConsumerIdGen;
141
142 private static final String LSRID = "lsrId";
143 private static final String TRUE = "true";
144 private static final String FALSE = "false";
145 private static final String END_OF_SYNC_IP_PREFIX = "0.0.0.0/32";
Avantika-Huaweid1e36bd2016-05-26 12:47:16 +0530146 public static final int PCEP_PORT = 4189;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530147
Avantika-Huawei73862d42016-05-12 18:58:06 +0530148 private IdGenerator localLspIdIdGen;
149 protected DistributedSet<Short> localLspIdFreeList;
150
151 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
152 protected CoreService coreService;
153
154 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530155 protected ResourceService resourceService;
156
157 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
158 protected ResourceQueryService resourceQueryService;
159
160 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
161 protected PathService pathService;
162
163 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
164 protected PceStore pceStore;
165
166 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Avantika-Huawei73862d42016-05-12 18:58:06 +0530167 protected TunnelService tunnelService;
168
169 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
170 protected StorageService storageService;
171
Priyanka Bb6963582016-05-20 20:21:20 +0530172 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530173 protected PacketService packetService;
Priyanka Bb6963582016-05-20 20:21:20 +0530174
175 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
176 protected DeviceService deviceService;
177
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530178 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
179 protected LabelResourceAdminService labelRsrcAdminService;
180
181 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
182 protected LabelResourceService labelRsrcService;
183
184 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
185 protected FlowObjectiveService flowObjectiveService;
186
Priyanka B3f92c5a2016-05-27 10:14:16 +0530187 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
188 protected MastershipService mastershipService;
189
190 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
191 protected TopologyService topologyService;
192
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530193 private TunnelListener listener = new InnerTunnelListener();
194 private BasicPceccHandler crHandler;
195 private PceccSrTeBeHandler srTeHandler;
Avantika-Huawei73862d42016-05-12 18:58:06 +0530196 private ApplicationId appId;
197
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530198 private final PcepPacketProcessor processor = new PcepPacketProcessor();
Priyanka B3f92c5a2016-05-27 10:14:16 +0530199 private final TopologyListener topologyListener = new InternalTopologyListener();
Priyanka B9fa4ed32016-05-27 11:59:24 +0530200 private ScheduledExecutorService executor;
201
202 public static final int INITIAL_DELAY = 30;
203 public static final int PERIODIC_DELAY = 30;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530204
Avantika-Huawei73862d42016-05-12 18:58:06 +0530205 /**
206 * Creates new instance of PceManager.
207 */
208 public PceManager() {
209 }
210
211 @Activate
212 protected void activate() {
213 appId = coreService.registerApplication(PCE_SERVICE_APP);
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530214 crHandler = BasicPceccHandler.getInstance();
215 crHandler.initialize(labelRsrcService, flowObjectiveService, appId, pceStore);
Avantika-Huawei73862d42016-05-12 18:58:06 +0530216
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530217 srTeHandler = PceccSrTeBeHandler.getInstance();
218 srTeHandler.initialize(labelRsrcAdminService, labelRsrcService, flowObjectiveService, appId, pceStore);
219
220 tunnelService.addListener(listener);
221
222 tunnelConsumerIdGen = coreService.getIdGenerator(TUNNEL_CONSUMER_ID_GEN_TOPIC);
Avantika-Huawei73862d42016-05-12 18:58:06 +0530223 localLspIdIdGen = coreService.getIdGenerator(LOCAL_LSP_ID_GEN_TOPIC);
224 localLspIdFreeList = storageService.<Short>setBuilder()
225 .withName("pcepLocalLspIdDeletedList")
226 .withSerializer(Serializer.using(KryoNamespaces.API))
227 .build()
228 .asDistributedSet();
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530229
230 packetService.addProcessor(processor, PacketProcessor.director(4));
Priyanka B3f92c5a2016-05-27 10:14:16 +0530231 topologyService.addListener(topologyListener);
Priyanka B9fa4ed32016-05-27 11:59:24 +0530232 executor = Executors.newSingleThreadScheduledExecutor();
233 //Start a timer when the component is up, with initial delay of 30min and periodic delays at 30min
234 executor.scheduleAtFixedRate(new GlobalOptimizationTimer(), INITIAL_DELAY, PERIODIC_DELAY, TimeUnit.MINUTES);
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530235 log.info("Started");
Avantika-Huawei73862d42016-05-12 18:58:06 +0530236 }
237
238 @Deactivate
239 protected void deactivate() {
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530240 tunnelService.removeListener(listener);
241 packetService.removeProcessor(processor);
Priyanka B3f92c5a2016-05-27 10:14:16 +0530242 topologyService.removeListener(topologyListener);
Priyanka B9fa4ed32016-05-27 11:59:24 +0530243 //Shutdown the thread when component is deactivated
244 executor.shutdown();
Avantika-Huawei73862d42016-05-12 18:58:06 +0530245 log.info("Stopped");
246 }
247
Priyanka Bb6963582016-05-20 20:21:20 +0530248 /**
249 * Returns an edge-weight capable of evaluating links on the basis of the
250 * specified constraints.
251 *
252 * @param constraints path constraints
253 * @return edge-weight function
254 */
255 private LinkWeight weight(List<Constraint> constraints) {
256 return new TeConstraintBasedLinkWeight(constraints);
257 }
258
259 /**
260 * Computes a path between two devices.
261 *
262 * @param src ingress device
263 * @param dst egress device
264 * @param constraints path constraints
265 * @return computed path based on constraints
266 */
267 protected Set<Path> computePath(DeviceId src, DeviceId dst, List<Constraint> constraints) {
268 if (pathService == null) {
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530269 return ImmutableSet.of();
Priyanka Bb6963582016-05-20 20:21:20 +0530270 }
271 Set<Path> paths = pathService.getPaths(src, dst, weight(constraints));
272 if (!paths.isEmpty()) {
273 return paths;
274 }
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530275 return ImmutableSet.of();
Priyanka Bb6963582016-05-20 20:21:20 +0530276 }
277
Avantika-Huawei73862d42016-05-12 18:58:06 +0530278 //[TODO:] handle requests in queue
279 @Override
280 public boolean setupPath(DeviceId src, DeviceId dst, String tunnelName, List<Constraint> constraints,
281 LspType lspType) {
282 checkNotNull(src);
283 checkNotNull(dst);
284 checkNotNull(tunnelName);
Avantika-Huawei73862d42016-05-12 18:58:06 +0530285 checkNotNull(lspType);
286
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530287 // Convert from DeviceId to TunnelEndPoint
288 Device srcDevice = deviceService.getDevice(src);
289 Device dstDevice = deviceService.getDevice(dst);
Avantika-Huawei73862d42016-05-12 18:58:06 +0530290
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530291 if (srcDevice == null || dstDevice == null) {
292 // Device is not known.
293 pceStore.addFailedPathInfo(new PcePathInfo(src, dst, tunnelName, constraints, lspType));
294 return false;
295 }
296
297 // In future projections instead of annotations will be used to fetch LSR ID.
298 String srcLsrId = srcDevice.annotations().value(LSRID);
299 String dstLsrId = dstDevice.annotations().value(LSRID);
300
301 if (srcLsrId == null || dstLsrId == null) {
302 // LSR id is not known.
303 pceStore.addFailedPathInfo(new PcePathInfo(src, dst, tunnelName, constraints, lspType));
304 return false;
305 }
306
307 TunnelEndPoint srcEndPoint = IpTunnelEndPoint.ipTunnelPoint(IpAddress.valueOf(srcLsrId));
308 TunnelEndPoint dstEndPoint = IpTunnelEndPoint.ipTunnelPoint(IpAddress.valueOf(dstLsrId));
309
310 double bwConstraintValue = 0;
311 CostConstraint costConstraint = null;
312 if (constraints != null) {
313 constraints.add(CapabilityConstraint.of(CapabilityType.valueOf(lspType.name())));
314 Iterator<Constraint> iterator = constraints.iterator();
315
316 while (iterator.hasNext()) {
317 Constraint constraint = iterator.next();
318 if (constraint instanceof BandwidthConstraint) {
319 bwConstraintValue = ((BandwidthConstraint) constraint).bandwidth().bps();
320 } else if (constraint instanceof CostConstraint) {
321 costConstraint = (CostConstraint) constraint;
322 }
323 }
324
325 /*
326 * Add cost at the end of the list of constraints. The path computation algorithm also computes cumulative
327 * cost. The function which checks the limiting/capability constraints also returns per link cost. This
328 * function can either return the result of limiting/capability constraint validation or the value of link
329 * cost, depending upon what is the last constraint in the loop.
330 */
331 if (costConstraint != null) {
332 constraints.remove(costConstraint);
333 constraints.add(costConstraint);
334 }
335 } else {
336 constraints = new LinkedList<>();
337 constraints.add(CapabilityConstraint.of(CapabilityType.valueOf(lspType.name())));
338 }
339
340 Set<Path> computedPathSet = computePath(src, dst, constraints);
341
342 // NO-PATH
343 if (computedPathSet.isEmpty()) {
344 pceStore.addFailedPathInfo(new PcePathInfo(src, dst, tunnelName, constraints, lspType));
345 return false;
346 }
347
348 Builder annotationBuilder = DefaultAnnotations.builder();
349 if (bwConstraintValue != 0) {
350 annotationBuilder.set(BANDWIDTH, String.valueOf(bwConstraintValue));
351 }
352 if (costConstraint != null) {
353 annotationBuilder.set(COST_TYPE, String.valueOf(costConstraint.type()));
354 }
355 annotationBuilder.set(LSP_SIG_TYPE, lspType.name());
356 annotationBuilder.set(PCE_INIT, TRUE);
357 annotationBuilder.set(DELEGATE, TRUE);
358
359 Path computedPath = computedPathSet.iterator().next();
360 LabelStack labelStack = null;
361
362 if (lspType == SR_WITHOUT_SIGNALLING) {
363 labelStack = srTeHandler.computeLabelStack(computedPath);
364 // Failed to form a label stack.
365 if (labelStack == null) {
366 pceStore.addFailedPathInfo(new PcePathInfo(src, dst, tunnelName, constraints, lspType));
367 return false;
368 }
369 }
370
371 if (lspType != WITH_SIGNALLING) {
372 /*
373 * Local LSP id which is assigned by RSVP for RSVP signalled LSPs, will be assigned by
374 * PCE for non-RSVP signalled LSPs.
375 */
376 annotationBuilder.set(LOCAL_LSP_ID, String.valueOf(getNextLocalLspId()));
377 }
378
379 // For SR-TE tunnels, call SR manager for label stack and put it inside tunnel.
380 Tunnel tunnel = new DefaultTunnel(null, srcEndPoint, dstEndPoint, MPLS, INIT, null, null,
381 TunnelName.tunnelName(tunnelName), computedPath,
382 labelStack, annotationBuilder.build());
383
384 // Allocate bandwidth.
385 TunnelConsumerId consumerId = null;
386 if (bwConstraintValue != 0) {
387 consumerId = reserveBandwidth(computedPath, bwConstraintValue, null);
388 if (consumerId == null) {
389 pceStore.addFailedPathInfo(new PcePathInfo(src, dst, tunnelName, constraints, lspType));
390 return false;
391 }
392 }
393
394 TunnelId tunnelId = tunnelService.setupTunnel(appId, src, tunnel, computedPath);
395 if (tunnelId == null) {
396 pceStore.addFailedPathInfo(new PcePathInfo(src, dst, tunnelName, constraints, lspType));
397 if (consumerId != null) {
398 resourceService.release(consumerId);
399 }
400 return false;
401 }
402
403 if (consumerId != null) {
404 // Store tunnel consumer id in LSP-Label store.
405 PceccTunnelInfo pceccTunnelInfo = new PceccTunnelInfo(null, consumerId);
406 pceStore.addTunnelInfo(tunnelId, pceccTunnelInfo);
407 }
408 return true;
409 }
Avantika-Huawei73862d42016-05-12 18:58:06 +0530410
411 @Override
412 public boolean updatePath(TunnelId tunnelId, List<Constraint> constraints) {
413 checkNotNull(tunnelId);
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530414 Set<Path> computedPathSet = null;
415 Tunnel tunnel = tunnelService.queryTunnel(tunnelId);
Avantika-Huawei73862d42016-05-12 18:58:06 +0530416
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530417 if (tunnel == null) {
418 return false;
419 }
Avantika-Huawei73862d42016-05-12 18:58:06 +0530420
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530421 if (tunnel.type() != MPLS || FALSE.equalsIgnoreCase(tunnel.annotations().value(DELEGATE))) {
422 // Only delegated LSPs can be updated.
423 return false;
424 }
425
426 List<Link> links = tunnel.path().links();
427 String lspSigType = tunnel.annotations().value(LSP_SIG_TYPE);
428 double bwConstraintValue = 0;
Priyanka B3f92c5a2016-05-27 10:14:16 +0530429 String costType = null;
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530430 SharedBandwidthConstraint shBwConstraint = null;
431 BandwidthConstraint bwConstraint = null;
432 CostConstraint costConstraint = null;
433
434 if (constraints != null) {
435 // Call path computation in shared bandwidth mode.
436 Iterator<Constraint> iterator = constraints.iterator();
437 while (iterator.hasNext()) {
438 Constraint constraint = iterator.next();
439 if (constraint instanceof BandwidthConstraint) {
440 bwConstraint = (BandwidthConstraint) constraint;
441 bwConstraintValue = bwConstraint.bandwidth().bps();
442 } else if (constraint instanceof CostConstraint) {
443 costConstraint = (CostConstraint) constraint;
Priyanka B3f92c5a2016-05-27 10:14:16 +0530444 costType = costConstraint.type().name();
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530445 }
446 }
447
448 // Remove and keep the cost constraint at the end of the list of constraints.
449 if (costConstraint != null) {
450 constraints.remove(costConstraint);
451 }
452
453 Bandwidth existingBwValue = null;
454 String existingBwAnnotation = tunnel.annotations().value(BANDWIDTH);
455 if (existingBwAnnotation != null) {
456 existingBwValue = Bandwidth.bps(Double.parseDouble(existingBwAnnotation));
457
458 /*
459 * The computation is a shared bandwidth constraint based, so need to remove bandwidth constraint which
460 * has been utilized to create shared bandwidth constraint.
461 */
462 if (bwConstraint != null) {
463 constraints.remove(bwConstraint);
464 }
465 }
466
467 if (existingBwValue != null) {
468 shBwConstraint = new SharedBandwidthConstraint(links, existingBwValue, bwConstraint.bandwidth());
469 constraints.add(shBwConstraint);
470 }
471 } else {
472 constraints = new LinkedList<>();
473 }
474
475 constraints.add(CapabilityConstraint.of(CapabilityType.valueOf(lspSigType)));
476 if (costConstraint != null) {
477 constraints.add(costConstraint);
478 }
479
480 computedPathSet = computePath(links.get(0).src().deviceId(), links.get(links.size() - 1).dst().deviceId(),
481 constraints);
482
483 // NO-PATH
484 if (computedPathSet.isEmpty()) {
485 return false;
486 }
487
488 Builder annotationBuilder = DefaultAnnotations.builder();
489 annotationBuilder.set(BANDWIDTH, String.valueOf(bwConstraintValue));
Priyanka B3f92c5a2016-05-27 10:14:16 +0530490 if (costType != null) {
491 annotationBuilder.set(COST_TYPE, costType);
492 }
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530493 annotationBuilder.set(LSP_SIG_TYPE, lspSigType);
494 annotationBuilder.set(PCE_INIT, TRUE);
495 annotationBuilder.set(DELEGATE, TRUE);
496 annotationBuilder.set(PLSP_ID, tunnel.annotations().value(PLSP_ID));
497 annotationBuilder.set(PCC_TUNNEL_ID, tunnel.annotations().value(PCC_TUNNEL_ID));
498
499 Path computedPath = computedPathSet.iterator().next();
500 LabelStack labelStack = null;
501 TunnelConsumerId consumerId = null;
502 LspType lspType = LspType.valueOf(lspSigType);
503 long localLspId = 0;
504 if (lspType != WITH_SIGNALLING) {
505 /*
506 * Local LSP id which is assigned by RSVP for RSVP signalled LSPs, will be assigned by
507 * PCE for non-RSVP signalled LSPs.
508 */
509 localLspId = getNextLocalLspId();
510 annotationBuilder.set(LOCAL_LSP_ID, String.valueOf(localLspId));
511
512 if (lspType == SR_WITHOUT_SIGNALLING) {
513 labelStack = srTeHandler.computeLabelStack(computedPath);
514 // Failed to form a label stack.
515 if (labelStack == null) {
516 return false;
517 }
518 }
519 }
520
521 Tunnel updatedTunnel = new DefaultTunnel(null, tunnel.src(), tunnel.dst(), MPLS, INIT, null, null,
522 tunnel.tunnelName(), computedPath,
523 labelStack, annotationBuilder.build());
524
525 // Allocate shared bandwidth.
526 if (bwConstraintValue != 0) {
527 consumerId = reserveBandwidth(computedPath, bwConstraintValue, shBwConstraint);
528 if (consumerId == null) {
529 return false;
530 }
531 }
532
533 TunnelId updatedTunnelId = tunnelService.setupTunnel(appId, links.get(0).src().deviceId(), updatedTunnel,
534 computedPath);
535
536 if (updatedTunnelId == null) {
537 if (consumerId != null) {
538 resourceService.release(consumerId);
539 }
540 return false;
541 }
542
543 if (consumerId != null) {
544 // Store tunnel consumer id in LSP-Label store.
545 PceccTunnelInfo pceccTunnelInfo = new PceccTunnelInfo(null, consumerId);
546 pceStore.addTunnelInfo(updatedTunnelId, pceccTunnelInfo);
547 }
Avantika-Huawei73862d42016-05-12 18:58:06 +0530548 return true;
549 }
550
551 @Override
552 public boolean releasePath(TunnelId tunnelId) {
553 checkNotNull(tunnelId);
554 // 1. Query Tunnel from Tunnel manager.
555 Tunnel tunnel = tunnelService.queryTunnel(tunnelId);
556
557 if (tunnel == null) {
558 return false;
559 }
560
561 // 2. Call tunnel service.
562 return tunnelService.downTunnel(appId, tunnel.tunnelId());
563 }
564
565 @Override
566 public Iterable<Tunnel> queryAllPath() {
567 return tunnelService.queryTunnel(MPLS);
568 }
569
570 @Override
571 public Tunnel queryPath(TunnelId tunnelId) {
572 return tunnelService.queryTunnel(tunnelId);
573 }
574
575 /**
576 * Returns the next local LSP identifier to be used either by getting from
577 * freed list if available otherwise generating a new one.
578 *
579 * @return value of local LSP identifier
580 */
581 private short getNextLocalLspId() {
582 // If there is any free id use it. Otherwise generate new id.
583 if (localLspIdFreeList.isEmpty()) {
584 return (short) localLspIdIdGen.getNewId();
585 }
586 Iterator<Short> it = localLspIdFreeList.iterator();
587 Short value = it.next();
588 localLspIdFreeList.remove(value);
589 return value;
590 }
591
Priyanka Bb6963582016-05-20 20:21:20 +0530592 protected class TeConstraintBasedLinkWeight implements LinkWeight {
593
594 private final List<Constraint> constraints;
595
596 /**
597 * Creates a new edge-weight function capable of evaluating links
598 * on the basis of the specified constraints.
599 *
600 * @param constraints path constraints
601 */
602 public TeConstraintBasedLinkWeight(List<Constraint> constraints) {
603 if (constraints == null) {
604 this.constraints = Collections.emptyList();
605 } else {
606 this.constraints = ImmutableList.copyOf(constraints);
607 }
608 }
609
610 @Override
611 public double weight(TopologyEdge edge) {
612 if (!constraints.iterator().hasNext()) {
613 //Takes default cost/hopcount as 1 if no constraints specified
614 return 1.0;
615 }
616
617 Iterator<Constraint> it = constraints.iterator();
618 double cost = 1;
619
620 //If any constraint fails return -1 also value of cost returned from cost constraint can't be negative
621 while (it.hasNext() && cost > 0) {
622 Constraint constraint = it.next();
623 if (constraint instanceof CapabilityConstraint) {
624 cost = ((CapabilityConstraint) constraint).isValidLink(edge.link(), deviceService) ? 1 : -1;
625 } else {
626 cost = constraint.cost(edge.link(), resourceService::isAvailable);
627 }
628 }
629 return cost;
630 }
631 }
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530632
Priyanka B3f92c5a2016-05-27 10:14:16 +0530633 //TODO: annotations used for temporarily later projection/network config will be used
634 private class InternalTopologyListener implements TopologyListener {
635 @Override
636 public void event(TopologyEvent event) {
637 event.reasons().forEach(e -> {
638 //If event type is link removed, get the impacted tunnel
639 if (e instanceof LinkEvent) {
640 LinkEvent linkEvent = (LinkEvent) e;
641 if (linkEvent.type() == LinkEvent.Type.LINK_REMOVED) {
642 tunnelService.queryTunnel(MPLS).forEach(t -> {
Avantika-Huaweid1e36bd2016-05-26 12:47:16 +0530643 if (t.path().links().contains((e.subject()))) {
Priyanka B3f92c5a2016-05-27 10:14:16 +0530644 // Check whether this ONOS instance is master for ingress device if yes,
645 // recompute and send update
646 checkForMasterAndUpdateTunnel(t.path().src().deviceId(), t);
647 }
648 });
649 }
650 }
651 });
652 }
653 }
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530654
Priyanka B3f92c5a2016-05-27 10:14:16 +0530655 private boolean checkForMasterAndUpdateTunnel(DeviceId src, Tunnel tunnel) {
656 /**
657 * Master of ingress node will recompute and also delegation flag must be set.
658 */
659 if (mastershipService.isLocalMaster(src)
660 && Boolean.valueOf(tunnel.annotations().value(DELEGATE)) != null) {
661 LinkedList<Constraint> constraintList = new LinkedList<>();
662
663 if (tunnel.annotations().value(BANDWIDTH) != null) {
664 //Requested bandwidth will be same as previous allocated bandwidth for the tunnel
665 BandwidthConstraint localConst = new BandwidthConstraint(Bandwidth.bps(Double.parseDouble(tunnel
666 .annotations().value(BANDWIDTH))));
667 constraintList.add(localConst);
668 }
669 if (tunnel.annotations().value(COST_TYPE) != null) {
670 constraintList.add(CostConstraint.of(CostConstraint.Type.valueOf(tunnel.annotations().value(
671 COST_TYPE))));
672 }
Priyanka B9fa4ed32016-05-27 11:59:24 +0530673
674 /*
675 * If tunnel was UP after recomputation failed then store failed path in PCE store send PCIntiate(remove)
676 * and If tunnel is failed and computation fails nothing to do because tunnel status will be same[Failed]
677 */
678 if (!updatePath(tunnel.tunnelId(), constraintList) && !tunnel.state().equals(Tunnel.State.FAILED)) {
Priyanka B3f92c5a2016-05-27 10:14:16 +0530679 // If updation fails store in PCE store as failed path
680 // then PCInitiate (Remove)
681 pceStore.addFailedPathInfo(new PcePathInfo(tunnel.path().src().deviceId(), tunnel
682 .path().dst().deviceId(), tunnel.tunnelName().value(), constraintList,
683 LspType.valueOf(tunnel.annotations().value(LSP_SIG_TYPE))));
684 //Release that tunnel calling PCInitiate
685 releasePath(tunnel.tunnelId());
686 }
687 }
688
689 return false;
690 }
691
692 // Allocates the bandwidth locally for PCECC tunnels.
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530693 private TunnelConsumerId reserveBandwidth(Path computedPath, double bandwidthConstraint,
694 SharedBandwidthConstraint shBwConstraint) {
695 checkNotNull(computedPath);
696 checkNotNull(bandwidthConstraint);
697 Resource resource = null;
698 double bwToAllocate = 0;
699
700 TunnelConsumerId consumer = TunnelConsumerId.valueOf(tunnelConsumerIdGen.getNewId());
701
702 /**
703 * Shared bandwidth sub-case : Lesser bandwidth required than original -
704 * No reservation required.
705 */
706 Double additionalBwValue = null;
707 if (shBwConstraint != null) {
708 additionalBwValue = ((bandwidthConstraint - shBwConstraint.sharedBwValue().bps()) <= 0) ? null
709 : (bandwidthConstraint - shBwConstraint.sharedBwValue().bps());
710 }
711
712 Optional<ResourceAllocation> resAlloc = null;
713 for (Link link : computedPath.links()) {
714 bwToAllocate = 0;
715 if ((shBwConstraint != null) && (shBwConstraint.links().contains(link))) {
716 if (additionalBwValue != null) {
717 bwToAllocate = bandwidthConstraint - additionalBwValue;
718 }
719 } else {
720 bwToAllocate = bandwidthConstraint;
721 }
722
723 /**
724 * In shared bandwidth cases, where new BW is lesser than old BW, it
725 * is not required to allocate anything.
726 */
727 if (bwToAllocate != 0) {
728 resource = Resources.continuous(link.src().deviceId(), link.src().port(), Bandwidth.class)
729 .resource(bwToAllocate);
730 resAlloc = resourceService.allocate(consumer, resource);
731
732 // If allocation for any link fails, then release the partially allocated bandwidth.
733 if (!resAlloc.isPresent()) {
734 resourceService.release(consumer);
735 return null;
736 }
737 }
738 }
739
740 /*
741 * Note: Storing of tunnel consumer id is done by caller of bandwidth reservation function. So deleting tunnel
742 * consumer id should be done by caller of bandwidth releasing function. This will prevent ambiguities related
743 * to who is supposed to store/delete.
744 */
745 return consumer;
746 }
747
748 /*
749 * Deallocates the bandwidth which is reserved locally for PCECC tunnels.
750 */
751 private void releaseBandwidth(Tunnel tunnel) {
752 // Between same source and destination, search the tunnel with same symbolic path name.
753 Collection<Tunnel> tunnelQueryResult = tunnelService.queryTunnel(tunnel.src(), tunnel.dst());
754 Tunnel newTunnel = null;
755 for (Tunnel tunnelObj : tunnelQueryResult) {
756 if (tunnel.tunnelName().value().equals(tunnelObj.tunnelName().value())) {
757 newTunnel = tunnelObj;
758 break;
759 }
760 }
761
762 // Even if one link is shared, the bandwidth release should happen based on shared mechanism.
763 boolean isLinkShared = false;
764 if (newTunnel != null) {
765 for (Link link : tunnel.path().links()) {
766 if (newTunnel.path().links().contains(link)) {
767 isLinkShared = true;
768 break;
769 }
770 }
771 }
772
773 if (isLinkShared) {
774 releaseSharedBandwidth(newTunnel, tunnel);
775 return;
776 }
777
778 resourceService.release(pceStore.getTunnelInfo(tunnel.tunnelId()).tunnelConsumerId());
779 return;
780
781 /*
782 * Note: Storing of tunnel consumer id is done by caller of bandwidth reservation function. So deleting tunnel
783 * consumer id should be done by caller of bandwidth releasing function. This will prevent ambiguities related
784 * to who is supposed to store/delete.
785 */
786 }
787
788 /**
789 * Re-allocates the bandwidth for the tunnel for which the bandwidth was
790 * allocated in shared mode initially.
791 */
792 private synchronized void releaseSharedBandwidth(Tunnel newTunnel, Tunnel oldTunnel) {
793 // 1. Release old tunnel's bandwidth.
794 resourceService.release(pceStore.getTunnelInfo(oldTunnel.tunnelId()).tunnelConsumerId());
795
796 // 2. Release new tunnel's bandwidth
797 ResourceConsumer consumer = pceStore.getTunnelInfo(newTunnel.tunnelId()).tunnelConsumerId();
798 resourceService.release(consumer);
799
800 // 3. Allocate new tunnel's complete bandwidth.
801 double bandwidth = Double.parseDouble(newTunnel.annotations().value(BANDWIDTH));
802 Resource resource;
803
804 for (Link link : newTunnel.path().links()) {
805 resource = Resources.continuous(link.src().deviceId(), link.src().port(), Bandwidth.class)
806 .resource(bandwidth);
807 resourceService.allocate(consumer, resource); // Reusing new tunnel's TunnelConsumerId intentionally.
808 }
809 }
810
811 // Listens on tunnel events.
812 private class InnerTunnelListener implements TunnelListener {
813 @Override
814 public void event(TunnelEvent event) {
815 // Event gets generated with old tunnel object.
816 Tunnel tunnel = event.subject();
817 if (tunnel.type() != MPLS) {
818 return;
819 }
820
821 LspType lspType = LspType.valueOf(tunnel.annotations().value(LSP_SIG_TYPE));
822 String tunnelBandwidth = tunnel.annotations().value(BANDWIDTH);
823 double bwConstraintValue = 0;
824 if (tunnelBandwidth != null) {
825 bwConstraintValue = Double.parseDouble(tunnelBandwidth);
826 }
827
828 switch (event.type()) {
829 case TUNNEL_ADDED:
830 // Allocate bandwidth for non-initiated, delegated LSPs with non-zero bandwidth (learned LSPs).
831 String pceInit = tunnel.annotations().value(PCE_INIT);
832 if (FALSE.equalsIgnoreCase(pceInit)
833 && bwConstraintValue != 0) {
834 reserveBandwidth(tunnel.path(), bwConstraintValue, null);
835 }
836 break;
837
838 case TUNNEL_UPDATED:
839 // Allocate/send labels for basic PCECC tunnels.
840 if ((tunnel.state() == ESTABLISHED) && (lspType == WITHOUT_SIGNALLING_AND_WITHOUT_SR)) {
841 crHandler.allocateLabel(tunnel);
842 }
843
844 if (tunnel.state() == UNSTABLE) {
845 /*
846 * During LSP DB sync if PCC doesn't report LSP which was PCE initiated, it's state is turned into
847 * unstable so that it can be setup again. Add into failed path store so that it can be recomputed
848 * and setup while global reoptimization.
849 */
850
851 List<Constraint> constraints = new LinkedList<>();
852 String bandwidth = tunnel.annotations().value(BANDWIDTH);
853 if (bandwidth != null) {
854 constraints.add(new BandwidthConstraint(Bandwidth
855 .bps(Double.parseDouble(bandwidth))));
856 }
857
858 String costType = tunnel.annotations().value(COST_TYPE);
859 if (costType != null) {
860 CostConstraint costConstraint = new CostConstraint(CostConstraint.Type.valueOf(costType));
861 constraints.add(costConstraint);
862 }
863
864 constraints.add(CapabilityConstraint
865 .of(CapabilityType.valueOf(tunnel.annotations().value(LSP_SIG_TYPE))));
866
867 List<Link> links = tunnel.path().links();
868 pceStore.addFailedPathInfo(new PcePathInfo(links.get(0).src().deviceId(),
869 links.get(links.size() - 1).dst().deviceId(),
870 tunnel.tunnelName().value(), constraints, lspType));
871 }
872 break;
873
874 case TUNNEL_REMOVED:
875 if (lspType != WITH_SIGNALLING) {
876 localLspIdFreeList.add(Short.valueOf(tunnel.annotations().value(LOCAL_LSP_ID)));
877 }
878
879 // If not zero bandwidth, and delegated (initiated LSPs will also be delegated).
880 if (bwConstraintValue != 0) {
881 releaseBandwidth(event.subject());
882
883 // Release basic PCECC labels.
884 if (lspType == WITHOUT_SIGNALLING_AND_WITHOUT_SR) {
885 // Delete stored tunnel consumer id from PCE store (while still retaining label list.)
886 PceccTunnelInfo pceccTunnelInfo = pceStore.getTunnelInfo(tunnel.tunnelId());
887 pceccTunnelInfo.tunnelConsumerId(null);
888 crHandler.releaseLabel(tunnel);
889 } else {
890 pceStore.removeTunnelInfo(tunnel.tunnelId());
891 }
892 }
893 break;
894
895 default:
896 break;
897
898 }
899 return;
900 }
901 }
902
903 private boolean syncLabelDb(DeviceId deviceId) {
904 checkNotNull(deviceId);
905 Map<DeviceId, LabelResourceId> globalNodeLabelMap = pceStore.getGlobalNodeLabels();
906
907 for (Entry<DeviceId, LabelResourceId> entry : globalNodeLabelMap.entrySet()) {
908
909 // Convert from DeviceId to TunnelEndPoint
910 Device srcDevice = deviceService.getDevice(entry.getKey());
911
912 /*
913 * If there is a slight difference in timing such that if device subsystem has removed the device but PCE
914 * store still has it, just ignore such devices.
915 */
916 if (srcDevice == null) {
917 continue;
918 }
919
920 String srcLsrId = srcDevice.annotations().value(LSRID);
921 if (srcLsrId == null) {
922 continue;
923 }
924
925 srTeHandler.advertiseNodeLabelRule(deviceId,
926 entry.getValue(),
927 IpPrefix.valueOf(IpAddress.valueOf(srcLsrId), PREFIX_LENGTH),
928 Objective.Operation.ADD, false);
929 }
930
931 Map<Link, LabelResourceId> adjLabelMap = pceStore.getAdjLabels();
932 for (Entry<Link, LabelResourceId> entry : adjLabelMap.entrySet()) {
933 if (entry.getKey().src().deviceId().equals(deviceId)) {
934 srTeHandler.installAdjLabelRule(deviceId,
935 entry.getValue(),
936 entry.getKey().src().port(),
937 entry.getKey().dst().port(),
938 Objective.Operation.ADD);
939 }
940 }
941
942 srTeHandler.advertiseNodeLabelRule(deviceId,
943 LabelResourceId.labelResourceId(0),
944 IpPrefix.valueOf(END_OF_SYNC_IP_PREFIX),
945 Objective.Operation.ADD, true);
946
947 return true;
948 }
949
950 // Process the packet received.
951 private class PcepPacketProcessor implements PacketProcessor {
952 // Process the packet received and in our case initiates the label DB sync.
953 @Override
954 public void process(PacketContext context) {
955 // Stop processing if the packet has been handled, since we
956 // can't do any more to it.
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530957 if (context.isHandled()) {
958 return;
959 }
960
961 InboundPacket pkt = context.inPacket();
Avantika-Huaweid1e36bd2016-05-26 12:47:16 +0530962 if (pkt == null) {
963 return;
964 }
965
966 Ethernet ethernet = pkt.parsed();
967 if (ethernet == null || ethernet.getEtherType() != Ethernet.TYPE_IPV4) {
968 return;
969 }
970
971 IPv4 ipPacket = (IPv4) ethernet.getPayload();
972 if (ipPacket == null || ipPacket.getProtocol() != IPv4.PROTOCOL_TCP) {
973 return;
974 }
975
976 TCP tcp = (TCP) ipPacket.getPayload();
977 if (tcp == null || tcp.getDestinationPort() != PCEP_PORT) {
978 return;
979 }
980
Avantika-Huaweidbdf7722016-05-21 14:20:31 +0530981 syncLabelDb(pkt.receivedFrom().deviceId());
982 }
983 }
984
Priyanka B9fa4ed32016-05-27 11:59:24 +0530985 //Computes path from tunnel store and also path failed to setup.
986 private void callForOptimization() {
987 //Recompute the LSPs which it was delegated [LSPs stored in PCE store (failed paths)]
988 for (PcePathInfo failedPathInfo : pceStore.getFailedPathInfos()) {
989 checkForMasterAndSetupPath(failedPathInfo);
990 }
991
992 //Recompute the LSPs for which it was delegated [LSPs stored in tunnel store]
993 tunnelService.queryTunnel(MPLS).forEach(t -> {
994 checkForMasterAndUpdateTunnel(t.path().src().deviceId(), t);
995 });
996 }
997
998 private boolean checkForMasterAndSetupPath(PcePathInfo failedPathInfo) {
999 /**
1000 * Master of ingress node will setup the path failed stored in PCE store.
1001 */
1002 if (mastershipService.isLocalMaster(failedPathInfo.src())) {
1003 if (setupPath(failedPathInfo.src(), failedPathInfo.dst(), failedPathInfo.name(),
1004 failedPathInfo.constraints(), failedPathInfo.lspType())) {
1005 // If computation is success remove that path
1006 pceStore.removeFailedPathInfo(failedPathInfo);
1007 return true;
1008 }
1009 }
1010
1011 return false;
1012 }
1013
1014 //Timer to call global optimization
1015 private class GlobalOptimizationTimer implements Runnable {
1016
1017 public GlobalOptimizationTimer() {
1018 }
1019
1020 @Override
1021 public void run() {
1022 callForOptimization();
1023 }
1024 }
Avantika-Huawei73862d42016-05-12 18:58:06 +05301025}