blob: 7a3d43e1ed64506a5b88699e657085ddafbdaa87 [file] [log] [blame]
HIGUCHI Yuta2d011582013-06-15 01:47:11 -07001package net.onrc.onos.ofcontroller.core.internal;
Umesh Krishnaswamyb676ca22013-01-11 12:39:25 -08002
Pankaj Berde6debb042013-01-16 18:04:32 -08003import java.util.ArrayList;
Jonathan Hartb7e3d2c2013-01-15 18:45:19 -08004import java.util.List;
Umesh Krishnaswamyb676ca22013-01-11 12:39:25 -08005
Jonathan Hartb7e3d2c2013-01-15 18:45:19 -08006import net.floodlightcontroller.routing.Link;
Pankaj Berde38646d62013-06-21 11:34:04 -07007import net.onrc.onos.graph.GraphDBOperation;
HIGUCHI Yuta2d011582013-06-15 01:47:11 -07008import net.onrc.onos.ofcontroller.core.ILinkStorage;
HIGUCHI Yuta20514902013-06-12 11:24:16 -07009import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IPortObject;
10import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.ISwitchObject;
HIGUCHI Yutaa56fbde2013-06-17 14:26:05 -070011import net.onrc.onos.ofcontroller.linkdiscovery.LinkInfo;
Jonathan Hartb7e3d2c2013-01-15 18:45:19 -080012
Umesh Krishnaswamyb676ca22013-01-11 12:39:25 -080013import org.openflow.util.HexString;
14import org.slf4j.Logger;
15import org.slf4j.LoggerFactory;
16
Naoki Shiotac57efb82013-06-18 13:40:28 -070017import com.tinkerpop.blueprints.Vertex;
Naoki Shiotac57efb82013-06-18 13:40:28 -070018import com.tinkerpop.pipes.PipeFunction;
19import com.tinkerpop.pipes.transform.PathPipe;
Umesh Krishnaswamyb676ca22013-01-11 12:39:25 -080020
Naoki Shiota1c393ce2013-06-28 22:55:14 -070021/**
Naoki Shiotab2d17e82013-10-18 18:08:16 -070022 * This is the class for storing the information of links into GraphDB
Naoki Shiota1c393ce2013-06-28 22:55:14 -070023 */
Umesh Krishnaswamyb676ca22013-01-11 12:39:25 -080024public class LinkStorageImpl implements ILinkStorage {
Pankaj Berde62016142013-04-09 15:35:50 -070025
Yuta HIGUCHI6ac8d182013-10-22 15:24:56 -070026 protected final static Logger log = LoggerFactory.getLogger(LinkStorageImpl.class);
Naoki Shiota987a5722013-10-23 11:59:36 -070027 protected GraphDBOperation op;
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -080028
Naoki Shiotab2d17e82013-10-18 18:08:16 -070029
30 /**
31 * Initialize the object. Open LinkStorage using given configuration file.
32 * @param conf Path (absolute path for now) to configuration file.
33 */
34 @Override
35 public void init(String conf) {
Naoki Shiota987a5722013-10-23 11:59:36 -070036 this.op = new GraphDBOperation(conf);
Naoki Shiotab2d17e82013-10-18 18:08:16 -070037 }
38
Naoki Shiota987a5722013-10-23 11:59:36 -070039 // Method designing policy:
40 // op.commit() and op.rollback() MUST called in public (first-class) methods.
41 // A first-class method MUST NOT call other first-class method.
42 // Routine process should be implemented in private method.
43 // A private method MUST NOT call commit or rollback.
44
45
Naoki Shiota11516712013-06-05 22:36:01 -070046 /**
Naoki Shiota987a5722013-10-23 11:59:36 -070047 * Update a record in the LinkStorage in a way provided by dmop.
Naoki Shiota11516712013-06-05 22:36:01 -070048 * @param link Record of a link to be updated.
Naoki Shiota987a5722013-10-23 11:59:36 -070049 * @param linkinfo Meta-information of a link to be updated.
50 * @param dmop Operation to be done.
Naoki Shiota11516712013-06-05 22:36:01 -070051 */
Umesh Krishnaswamyb676ca22013-01-11 12:39:25 -080052 @Override
Naoki Shiota987a5722013-10-23 11:59:36 -070053 public boolean update(Link link, LinkInfo linkinfo, DM_OPERATION dmop) {
54 boolean success = false;
55
56 switch (dmop) {
Naoki Shiotab2d17e82013-10-18 18:08:16 -070057 case CREATE:
58 case INSERT:
Naoki Shiota987a5722013-10-23 11:59:36 -070059 if (link != null) {
60 try {
61 if (addLinkImpl(link)) {
62 op.commit();
63 success = true;
64 }
65 } catch (Exception e) {
66 op.rollback();
67 e.printStackTrace();
68 log.error("LinkStorageImpl:update {} link:{} failed", dmop, link);
69 }
70 }
Naoki Shiotab2d17e82013-10-18 18:08:16 -070071 break;
72 case UPDATE:
Naoki Shiota987a5722013-10-23 11:59:36 -070073 if (link != null && linkinfo != null) {
74 try {
75 if (setLinkInfoImpl(link, linkinfo)) {
76 op.commit();
77 success = true;
78 }
79 } catch (Exception e) {
80 op.rollback();
81 e.printStackTrace();
82 log.error("LinkStorageImpl:update {} link:{} failed", dmop, link);
83 }
Naoki Shiotab2d17e82013-10-18 18:08:16 -070084 }
85 break;
86 case DELETE:
Naoki Shiota987a5722013-10-23 11:59:36 -070087 if (link != null) {
88 try {
89 if (deleteLinkImpl(link)) {
90 op.commit();
91 success = true;
92 log.debug("LinkStorageImpl:update {} link:{} succeeded", dmop, link);
93 } else {
94 op.rollback();
95 log.debug("LinkStorageImpl:update {} link:{} failed", dmop, link);
96 }
97 } catch (Exception e) {
98 op.rollback();
99 e.printStackTrace();
100 log.error("LinkStorageImpl:update {} link:{} failed", dmop, link);
101 }
102 }
Naoki Shiotab2d17e82013-10-18 18:08:16 -0700103 break;
104 }
Naoki Shiota987a5722013-10-23 11:59:36 -0700105
106 return success;
Umesh Krishnaswamyb676ca22013-01-11 12:39:25 -0800107 }
108
Naoki Shiotab2d17e82013-10-18 18:08:16 -0700109 @Override
Naoki Shiota987a5722013-10-23 11:59:36 -0700110 public boolean addLink(Link link) {
111 return addLink(link, null);
112 }
113
114 @Override
115 public boolean addLink(Link link, LinkInfo linfo) {
116 boolean success = false;
117
118 try {
119 if (addLinkImpl(link)) {
120 // Set LinkInfo only if linfo is non-null.
121 if (linfo != null && (! setLinkInfoImpl(link, linfo))) {
Jonathan Hart13ccdca2013-10-30 15:23:28 -0700122 log.debug("Adding linkinfo failed: {}", link);
Naoki Shiota987a5722013-10-23 11:59:36 -0700123 op.rollback();
124 }
125 op.commit();
126 success = true;
127 } else {
Jonathan Hart13ccdca2013-10-30 15:23:28 -0700128 // If we fail here that's because the ports aren't added
129 // before we try to add the link
130 log.debug("Adding link failed: {}", link);
Naoki Shiota987a5722013-10-23 11:59:36 -0700131 op.rollback();
132 }
133 } catch (Exception e) {
Pavlin Radoslavov0aad60c2013-11-05 08:33:57 -0800134 op.rollback();
Naoki Shiota987a5722013-10-23 11:59:36 -0700135 e.printStackTrace();
136 log.error("LinkStorageImpl:addLink link:{} linfo:{} failed", link, linfo);
137 }
138
139 return success;
Naoki Shiotab2d17e82013-10-18 18:08:16 -0700140 }
141
Naoki Shiota11516712013-06-05 22:36:01 -0700142 /**
143 * Update multiple records in the LinkStorage in a way provided by op.
144 * @param links List of records to be updated.
145 * @param op Operation to be done.
146 */
Umesh Krishnaswamyb676ca22013-01-11 12:39:25 -0800147 @Override
Naoki Shiota987a5722013-10-23 11:59:36 -0700148 public boolean addLinks(List<Link> links) {
149 boolean success = false;
150
Umesh Krishnaswamyb676ca22013-01-11 12:39:25 -0800151 for (Link lt: links) {
Naoki Shiota987a5722013-10-23 11:59:36 -0700152 if (! addLinkImpl(lt)) {
153 return false;
154 }
Naoki Shiotab2d17e82013-10-18 18:08:16 -0700155 }
Naoki Shiota987a5722013-10-23 11:59:36 -0700156
157 try {
158 op.commit();
159 success = true;
160 } catch (Exception e) {
Pavlin Radoslavov0aad60c2013-11-05 08:33:57 -0800161 op.rollback();
Naoki Shiota987a5722013-10-23 11:59:36 -0700162 e.printStackTrace();
163 log.error("LinkStorageImpl:addLinks link:s{} failed", links);
164 }
165
166 return success;
Naoki Shiotab2d17e82013-10-18 18:08:16 -0700167 }
168
169 /**
Naoki Shiota987a5722013-10-23 11:59:36 -0700170 * Delete a record in the LinkStorage.
171 * @param lt Record to be deleted.
172 */
173 @Override
174 public boolean deleteLink(Link lt) {
175 boolean success = false;
176
177 log.debug("LinkStorageImpl:deleteLink(): {}", lt);
178
179 try {
180 if (deleteLinkImpl(lt)) {
181 op.commit();
182 success = true;
183 log.debug("LinkStorageImpl:deleteLink(): deleted edges {}", lt);
184 } else {
185 op.rollback();
186 log.error("LinkStorageImpl:deleteLink(): failed invalid vertices {}", lt);
187 }
188 } catch (Exception e) {
189 op.rollback();
190 log.error("LinkStorageImpl:deleteLink(): failed {} {}",
191 new Object[]{lt, e.toString()});
192 e.printStackTrace();
193 }
194
195 return success;
196 }
Naoki Shiotab2d17e82013-10-18 18:08:16 -0700197
198 /**
199 * Delete multiple records in LinkStorage.
200 * @param links List of records to be deleted.
201 */
202 @Override
Naoki Shiota987a5722013-10-23 11:59:36 -0700203 public boolean deleteLinks(List<Link> links) {
204 boolean success = false;
205
206 try {
207 for (Link lt : links) {
208 if (! deleteLinkImpl(lt)) {
209 op.rollback();
210 return false;
211 }
212 }
213 op.commit();
214 success = true;
215 } catch (Exception e) {
216 op.rollback();
217 e.printStackTrace();
218 log.error("LinkStorageImpl:deleteLinks failed invalid vertices {}", links);
Umesh Krishnaswamyb676ca22013-01-11 12:39:25 -0800219 }
Naoki Shiotab2d17e82013-10-18 18:08:16 -0700220
Naoki Shiota987a5722013-10-23 11:59:36 -0700221 return success;
Naoki Shiotab2d17e82013-10-18 18:08:16 -0700222 }
223
Naoki Shiota11516712013-06-05 22:36:01 -0700224 /**
Naoki Shiota11516712013-06-05 22:36:01 -0700225 * Get list of all links connected to the port specified by given DPID and port number.
226 * @param dpid DPID of desired port.
227 * @param port Port number of desired port.
228 * @return List of links. Empty list if no port was found.
229 */
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800230 @Override
231 public List<Link> getLinks(Long dpid, short port) {
Pavlin Radoslavovd8226792013-11-04 20:28:07 -0800232 List<Link> links = new ArrayList<Link>();
233
234 IPortObject srcPort = op.searchPort(HexString.toHexString(dpid), port);
235 if (srcPort == null)
236 return links;
237 ISwitchObject srcSw = srcPort.getSwitch();
238 if (srcSw == null)
239 return links;
Naoki Shiota93ec1712013-06-13 15:49:36 -0700240
Pavlin Radoslavovd8226792013-11-04 20:28:07 -0800241 for(IPortObject dstPort : srcPort.getLinkedPorts()) {
242 ISwitchObject dstSw = dstPort.getSwitch();
243 if (dstSw != null) {
Pavlin Radoslavov0aad60c2013-11-05 08:33:57 -0800244 Link link = new Link(dpid, port,
Pavlin Radoslavovd8226792013-11-04 20:28:07 -0800245 HexString.toLong(dstSw.getDPID()),
246 dstPort.getNumber());
247 links.add(link);
248 }
249 }
250 return links;
Pavlin Radoslavov43781cd2013-11-04 18:19:10 -0800251 }
252
253 /**
254 * Get list of all reverse links connected to the port specified by given DPID and port number.
255 * @param dpid DPID of desired port.
256 * @param port Port number of desired port.
257 * @return List of reverse links. Empty list if no port was found.
258 */
259 @Override
260 public List<Link> getReverseLinks(Long dpid, short port) {
Pavlin Radoslavovd8226792013-11-04 20:28:07 -0800261 List<Link> links = new ArrayList<Link>();
Pavlin Radoslavov43781cd2013-11-04 18:19:10 -0800262
Pavlin Radoslavovd8226792013-11-04 20:28:07 -0800263 IPortObject srcPort = op.searchPort(HexString.toHexString(dpid), port);
264 if (srcPort == null)
265 return links;
266 ISwitchObject srcSw = srcPort.getSwitch();
267 if (srcSw == null)
268 return links;
Pavlin Radoslavov43781cd2013-11-04 18:19:10 -0800269
Pavlin Radoslavovd8226792013-11-04 20:28:07 -0800270 for(IPortObject dstPort : srcPort.getReverseLinkedPorts()) {
271 ISwitchObject dstSw = dstPort.getSwitch();
272 if (dstSw != null) {
273 Link link = new Link(HexString.toLong(dstSw.getDPID()),
274 dstPort.getNumber(),
Pavlin Radoslavov0aad60c2013-11-05 08:33:57 -0800275 dpid, port);
Pavlin Radoslavovd8226792013-11-04 20:28:07 -0800276 links.add(link);
277 }
278 }
279 return links;
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800280 }
281
Naoki Shiota11516712013-06-05 22:36:01 -0700282 /**
Naoki Shiota11516712013-06-05 22:36:01 -0700283 * Delete records of the links connected to the port specified by given DPID and port number.
284 * @param dpid DPID of desired port.
285 * @param port Port number of desired port.
286 */
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800287 @Override
Naoki Shiota987a5722013-10-23 11:59:36 -0700288 public boolean deleteLinksOnPort(Long dpid, short port) {
289 boolean success = false;
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800290
Naoki Shiota987a5722013-10-23 11:59:36 -0700291 List<Link> linksToDelete = getLinks(dpid, port);
292 try {
293 for(Link l : linksToDelete) {
294 if (! deleteLinkImpl(l)) {
295 op.rollback();
296 log.error("LinkStorageImpl:deleteLinksOnPort dpid:{} port:{} failed", dpid, port);
297 return false;
298 }
299 }
300 op.commit();
301 success = true;
302 } catch (Exception e) {
303 op.rollback();
304 e.printStackTrace();
305 log.error("LinkStorageImpl:deleteLinksOnPort dpid:{} port:{} failed", dpid, port);
mininet403d5892013-06-05 03:48:17 -0700306 }
Naoki Shiota987a5722013-10-23 11:59:36 -0700307
308 return success;
Umesh Krishnaswamyf962d642013-01-23 19:04:23 -0800309 }
310
Naoki Shiota11516712013-06-05 22:36:01 -0700311 /**
312 * Get list of all links connected to the switch specified by given DPID.
313 * @param dpid DPID of desired switch.
314 * @return List of links. Empty list if no port was found.
315 */
Pankaj Berdeff421802013-01-29 20:28:52 -0800316 @Override
317 public List<Link> getLinks(String dpid) {
mininet403d5892013-06-05 03:48:17 -0700318 List<Link> links = new ArrayList<Link>();
319
Naoki Shiota987a5722013-10-23 11:59:36 -0700320 ISwitchObject srcSw = op.searchSwitch(dpid);
Naoki Shiota93ec1712013-06-13 15:49:36 -0700321
322 if(srcSw != null) {
323 for(IPortObject srcPort : srcSw.getPorts()) {
324 for(IPortObject dstPort : srcPort.getLinkedPorts()) {
325 ISwitchObject dstSw = dstPort.getSwitch();
326 if(dstSw != null) {
Pavlin Radoslavov0aad60c2013-11-05 08:33:57 -0800327 Link link = new Link(HexString.toLong(dpid),
Naoki Shiota93ec1712013-06-13 15:49:36 -0700328 srcPort.getNumber(),
329 HexString.toLong(dstSw.getDPID()),
Pavlin Radoslavov0aad60c2013-11-05 08:33:57 -0800330 dstPort.getNumber());
Naoki Shiota93ec1712013-06-13 15:49:36 -0700331 links.add(link);
332 }
333 }
334 }
335 }
336
mininet403d5892013-06-05 03:48:17 -0700337 return links;
Pankaj Berdeff421802013-01-29 20:28:52 -0800338 }
339
Naoki Shiota11516712013-06-05 22:36:01 -0700340 /**
Pavlin Radoslavovc934b4a2013-11-02 14:53:52 -0700341 * Get list of all reverse links connected to the switch specified by
342 * given DPID.
343 * @param dpid DPID of desired switch.
344 * @return List of reverse links. Empty list if no port was found.
345 */
346 @Override
347 public List<Link> getReverseLinks(String dpid) {
348 List<Link> links = new ArrayList<Link>();
349
350 ISwitchObject srcSw = op.searchSwitch(dpid);
351
352 if(srcSw != null) {
353 for(IPortObject srcPort : srcSw.getPorts()) {
354 for(IPortObject dstPort : srcPort.getReverseLinkedPorts()) {
355 ISwitchObject dstSw = dstPort.getSwitch();
356 if(dstSw != null) {
357 Link link = new Link(
358 HexString.toLong(dstSw.getDPID()),
359 dstPort.getNumber(),
360
Pavlin Radoslavov0aad60c2013-11-05 08:33:57 -0800361 HexString.toLong(dpid),
Pavlin Radoslavovc934b4a2013-11-02 14:53:52 -0700362 srcPort.getNumber());
363 links.add(link);
364 }
365 }
366 }
367 }
368
369 return links;
370 }
371
372 /**
Naoki Shiota11516712013-06-05 22:36:01 -0700373 * Get list of all links whose state is ACTIVE.
374 * @return List of active links. Empty list if no port was found.
375 */
Pankaj Berdeff421802013-01-29 20:28:52 -0800376 public List<Link> getActiveLinks() {
Naoki Shiota987a5722013-10-23 11:59:36 -0700377 Iterable<ISwitchObject> switches = op.getActiveSwitches();
Pankaj Berde5024ec12013-01-31 17:07:29 -0800378
379 List<Link> links = new ArrayList<Link>();
Naoki Shiota826e84a2013-06-18 13:13:25 -0700380
381 for (ISwitchObject srcSw : switches) {
382 for(IPortObject srcPort : srcSw.getPorts()) {
383 for(IPortObject dstPort : srcPort.getLinkedPorts()) {
384 ISwitchObject dstSw = dstPort.getSwitch();
Pankaj Berde5024ec12013-01-31 17:07:29 -0800385
Naoki Shiota826e84a2013-06-18 13:13:25 -0700386 if(dstSw != null && dstSw.getState().equals("ACTIVE")) {
387 links.add(new Link(HexString.toLong(srcSw.getDPID()),
388 srcPort.getNumber(),
389 HexString.toLong(dstSw.getDPID()),
390 dstPort.getNumber()));
391 }
392 }
Pankaj Berde5024ec12013-01-31 17:07:29 -0800393 }
Pankaj Berde5024ec12013-01-31 17:07:29 -0800394 }
Naoki Shiota826e84a2013-06-18 13:13:25 -0700395
Pankaj Berde5024ec12013-01-31 17:07:29 -0800396 return links;
Pankaj Berdeff421802013-01-29 20:28:52 -0800397 }
Pankaj Berde5024ec12013-01-31 17:07:29 -0800398
Naoki Shiotab2d17e82013-10-18 18:08:16 -0700399 @Override
400 public LinkInfo getLinkInfo(Link link) {
401 // TODO implement this
402 return null;
403 }
404
405 /**
406 * Finalize the object.
407 */
408 public void finalize() {
409 close();
410 }
411
412 /**
413 * Close LinkStorage.
414 */
415 @Override
416 public void close() {
417 // TODO Auto-generated method stub
418// graph.shutdown();
419 }
420
Naoki Shiota987a5722013-10-23 11:59:36 -0700421 /**
422 * Update a record of link with meta-information in the LinkStorage.
423 * @param link Record of a link to update.
424 * @param linkinfo Meta-information of a link to be updated.
425 */
426 private boolean setLinkInfoImpl(Link link, LinkInfo linkinfo) {
427 // TODO implement this
428
429 return false;
430 }
431
432 private boolean addLinkImpl(Link lt) {
433 boolean success = false;
434
435 IPortObject vportSrc = null, vportDst = null;
436
437 // get source port vertex
438 String dpid = HexString.toHexString(lt.getSrc());
439 short port = lt.getSrcPort();
440 vportSrc = op.searchPort(dpid, port);
441
442 // get dest port vertex
443 dpid = HexString.toHexString(lt.getDst());
444 port = lt.getDstPort();
445 vportDst = op.searchPort(dpid, port);
446
447 if (vportSrc != null && vportDst != null) {
448 IPortObject portExist = null;
449 // check if the link exists
450 for (IPortObject V : vportSrc.getLinkedPorts()) {
451 if (V.equals(vportDst)) {
452 portExist = V;
453 break;
454 }
455 }
456
457 if (portExist == null) {
458 vportSrc.setLinkPort(vportDst);
459 success = true;
460 } else {
461 log.debug("LinkStorageImpl:addLinkImpl failed link exists {} {} src {} dst {}",
462 new Object[]{op, lt, vportSrc, vportDst});
463 }
464 }
465
466 return success;
467 }
468
469 private boolean deleteLinkImpl(Link lt) {
470 boolean success = false;
471 IPortObject vportSrc = null, vportDst = null;
472
473 // get source port vertex
474 String dpid = HexString.toHexString(lt.getSrc());
475 short port = lt.getSrcPort();
476 vportSrc = op.searchPort(dpid, port);
477
478 // get dst port vertex
479 dpid = HexString.toHexString(lt.getDst());
480 port = lt.getDstPort();
481 vportDst = op.searchPort(dpid, port);
482
483 // FIXME: This needs to remove all edges
484 if (vportSrc != null && vportDst != null) {
485 vportSrc.removeLink(vportDst);
486 log.debug("deleteLinkImpl(): deleted edges src {} dst {}", new Object[]{
487 lt, vportSrc, vportDst});
488 success = true;
489 }
490
491 return success;
492 }
493
Naoki Shiotab2d17e82013-10-18 18:08:16 -0700494 // TODO should be moved to TopoLinkServiceImpl (never used in this class)
HIGUCHI Yutaf05c4802013-06-17 11:15:50 -0700495 static class ExtractLink implements PipeFunction<PathPipe<Vertex>, Link> {
Naoki Shiotac57efb82013-06-18 13:40:28 -0700496
Yuta HIGUCHI57e67f22013-10-14 10:21:05 -0700497 @SuppressWarnings("unchecked")
Naoki Shiotac57efb82013-06-18 13:40:28 -0700498 @Override
499 public Link compute(PathPipe<Vertex> pipe ) {
Naoki Shiotac57efb82013-06-18 13:40:28 -0700500 long s_dpid = 0;
501 long d_dpid = 0;
502 short s_port = 0;
503 short d_port = 0;
504 List<Vertex> V = new ArrayList<Vertex>();
Yuta HIGUCHI57e67f22013-10-14 10:21:05 -0700505 V = (List<Vertex>)pipe.next();
Naoki Shiotac57efb82013-06-18 13:40:28 -0700506 Vertex src_sw = V.get(0);
507 Vertex dest_sw = V.get(3);
508 Vertex src_port = V.get(1);
509 Vertex dest_port = V.get(2);
510 s_dpid = HexString.toLong((String) src_sw.getProperty("dpid"));
511 d_dpid = HexString.toLong((String) dest_sw.getProperty("dpid"));
512 s_port = (Short) src_port.getProperty("number");
513 d_port = (Short) dest_port.getProperty("number");
514
515 Link l = new Link(s_dpid,s_port,d_dpid,d_port);
516
517 return l;
518 }
519 }
Pankaj Berde5024ec12013-01-31 17:07:29 -0800520
Pankaj Berdeff421802013-01-29 20:28:52 -0800521
Umesh Krishnaswamyb676ca22013-01-11 12:39:25 -0800522}