Added support for annotations in generated APIs.
[controller.git] / opendaylight / protocol_plugins / openflow / src / main / java / org / opendaylight / controller / protocol_plugin / openflow / internal / FlowProgrammerService.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.controller.protocol_plugin.openflow.internal;
10
11 import java.nio.ByteBuffer;
12 import java.util.ArrayList;
13 import java.util.HashMap;
14 import java.util.HashSet;
15 import java.util.Map;
16 import java.util.Set;
17 import java.util.concurrent.ConcurrentHashMap;
18 import java.util.concurrent.ConcurrentMap;
19
20 import org.eclipse.osgi.framework.console.CommandInterpreter;
21 import org.eclipse.osgi.framework.console.CommandProvider;
22 import org.opendaylight.controller.protocol_plugin.openflow.IFlowProgrammerNotifier;
23 import org.opendaylight.controller.protocol_plugin.openflow.IInventoryShimExternalListener;
24 import org.opendaylight.controller.protocol_plugin.openflow.core.IController;
25 import org.opendaylight.controller.protocol_plugin.openflow.core.IMessageListener;
26 import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitch;
27 import org.opendaylight.controller.protocol_plugin.openflow.vendorextension.v6extension.V6Error;
28 import org.openflow.protocol.OFError;
29 import org.openflow.protocol.OFFlowMod;
30 import org.openflow.protocol.OFFlowRemoved;
31 import org.openflow.protocol.OFMessage;
32 import org.openflow.protocol.OFPort;
33 import org.openflow.protocol.OFType;
34 import org.openflow.protocol.action.OFAction;
35
36 import org.opendaylight.controller.sal.core.ContainerFlow;
37 import org.opendaylight.controller.sal.core.IContainerListener;
38 import org.opendaylight.controller.sal.core.Node;
39 import org.opendaylight.controller.sal.core.Node.NodeIDType;
40 import org.opendaylight.controller.sal.core.NodeConnector;
41 import org.opendaylight.controller.sal.core.Property;
42 import org.opendaylight.controller.sal.core.UpdateType;
43 import org.opendaylight.controller.sal.flowprogrammer.Flow;
44 import org.opendaylight.controller.sal.flowprogrammer.IPluginInFlowProgrammerService;
45 import org.opendaylight.controller.sal.match.Match;
46 import org.opendaylight.controller.sal.match.MatchType;
47 import org.opendaylight.controller.sal.utils.GlobalConstants;
48 import org.opendaylight.controller.sal.utils.HexEncode;
49 import org.opendaylight.controller.sal.utils.NodeCreator;
50 import org.opendaylight.controller.sal.utils.StatusCode;
51 import org.opendaylight.controller.sal.utils.Status;
52 import org.osgi.framework.BundleContext;
53 import org.osgi.framework.FrameworkUtil;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 /**
58  * Represents the openflow plugin component in charge of programming the flows
59  * the flow programming and relay them to functional modules above SAL.
60  */
61 public class FlowProgrammerService implements IPluginInFlowProgrammerService,
62         IMessageListener, IContainerListener, IInventoryShimExternalListener,
63         CommandProvider {
64     private static final Logger log = LoggerFactory
65             .getLogger(FlowProgrammerService.class);
66     private IController controller;
67     private ConcurrentMap<String, IFlowProgrammerNotifier> flowProgrammerNotifiers;
68     private Map<String, Set<NodeConnector>> containerToNc;
69     private ConcurrentMap<Long, Map<Integer, Long>> xid2rid;
70     private int barrierMessagePriorCount = getBarrierMessagePriorCount();
71
72     public FlowProgrammerService() {
73         controller = null;
74         flowProgrammerNotifiers = new ConcurrentHashMap<String, IFlowProgrammerNotifier>();
75         containerToNc = new HashMap<String, Set<NodeConnector>>();
76         xid2rid = new ConcurrentHashMap<Long, Map<Integer, Long>>();
77     }
78
79     public void setController(IController core) {
80         this.controller = core;
81     }
82
83     public void unsetController(IController core) {
84         if (this.controller == core) {
85             this.controller = null;
86         }
87     }
88
89     public void setFlowProgrammerNotifier(Map<String, ?> props,
90             IFlowProgrammerNotifier s) {
91         if (props == null || props.get("containerName") == null) {
92             log.error("Didn't receive the service correct properties");
93             return;
94         }
95         String containerName = (String) props.get("containerName");
96         this.flowProgrammerNotifiers.put(containerName, s);
97     }
98
99     public void unsetFlowProgrammerNotifier(Map<String, ?> props,
100             IFlowProgrammerNotifier s) {
101         if (props == null || props.get("containerName") == null) {
102             log.error("Didn't receive the service correct properties");
103             return;
104         }
105         String containerName = (String) props.get("containerName");
106         if (this.flowProgrammerNotifiers != null
107                 && this.flowProgrammerNotifiers.containsKey(containerName)
108                 && this.flowProgrammerNotifiers.get(containerName) == s) {
109             this.flowProgrammerNotifiers.remove(containerName);
110         }
111     }
112
113     /**
114      * Function called by the dependency manager when all the required
115      * dependencies are satisfied
116      * 
117      */
118     void init() {
119         this.controller.addMessageListener(OFType.FLOW_REMOVED, this);
120         this.controller.addMessageListener(OFType.ERROR, this);
121         registerWithOSGIConsole();
122     }
123
124     /**
125      * Function called by the dependency manager when at least one dependency
126      * become unsatisfied or when the component is shutting down because for
127      * example bundle is being stopped.
128      * 
129      */
130     void destroy() {
131     }
132
133     /**
134      * Function called by dependency manager after "init ()" is called and after
135      * the services provided by the class are registered in the service registry
136      * 
137      */
138     void start() {
139     }
140
141     /**
142      * Function called by the dependency manager before the services exported by
143      * the component are unregistered, this will be followed by a "destroy ()"
144      * calls
145      * 
146      */
147     void stop() {
148     }
149
150     @Override
151     public Status addFlow(Node node, Flow flow) {
152         return addFlowInternal(node, flow, 0);
153     }
154
155     @Override
156     public Status modifyFlow(Node node, Flow oldFlow, Flow newFlow) {
157         return modifyFlowInternal(node, oldFlow, newFlow, 0);
158     }
159
160     @Override
161     public Status removeFlow(Node node, Flow flow) {
162         return removeFlowInternal(node, flow, 0);
163     }
164
165     @Override
166     public Status addFlowAsync(Node node, Flow flow, long rid) {
167         return addFlowInternal(node, flow, rid);
168     }
169
170     @Override
171     public Status modifyFlowAsync(Node node, Flow oldFlow, Flow newFlow,
172             long rid) {
173         return modifyFlowInternal(node, oldFlow, newFlow, rid);
174     }
175
176     @Override
177     public Status removeFlowAsync(Node node, Flow flow, long rid) {
178         return removeFlowInternal(node, flow, rid);
179     }
180
181     private Status addFlowInternal(Node node, Flow flow, long rid) {
182         String action = "add";
183         if (!node.getType().equals(NodeIDType.OPENFLOW)) {
184             return new Status(StatusCode.NOTACCEPTABLE, errorString("send",
185                     action, "Invalid node type"));
186         }
187
188         if (controller != null) {
189             ISwitch sw = controller.getSwitch((Long) node.getID());
190             if (sw != null) {
191                 FlowConverter x = new FlowConverter(flow);
192                 OFMessage msg = x.getOFFlowMod(OFFlowMod.OFPFC_ADD, null);
193
194                 Object result;
195                 if (rid == 0) {
196                     /*
197                      * Synchronous message send. Each message is followed by a
198                      * Barrier message.
199                      */
200                     result = sw.syncSend(msg);
201                 } else {
202                     /*
203                      * Message will be sent asynchronously. A Barrier message
204                      * will be inserted automatically to synchronize the
205                      * progression.
206                      */
207                     result = asyncMsgSend(node, sw, msg, rid);  
208                 }
209                 if (result instanceof Boolean) {
210                     return ((Boolean) result == Boolean.TRUE) ? new Status(
211                             StatusCode.SUCCESS, null) : new Status(
212                             StatusCode.TIMEOUT, errorString(null, action,
213                                     "Request Timed Out"));
214                 } else if (result instanceof OFError) {
215                     OFError res = (OFError) result;
216                     if (res.getErrorType() == V6Error.NICIRA_VENDOR_ERRORTYPE) {
217                         V6Error er = new V6Error(res);
218                         byte[] b = res.getError();
219                         ByteBuffer bb = ByteBuffer.allocate(b.length);
220                         bb.put(b);
221                         bb.rewind();
222                         er.readFrom(bb);
223                         return new Status(StatusCode.INTERNALERROR,
224                                 errorString("program", action,
225                                         "Vendor Extension Internal Error"));
226                     }
227                     return new Status(StatusCode.INTERNALERROR, errorString(
228                             "program", action, Utils.getOFErrorString(res)));
229                 } else {
230                     return new Status(StatusCode.INTERNALERROR, errorString(
231                             "send", action, "Internal Error"));
232                 }
233             } else {
234                 return new Status(StatusCode.GONE, errorString("send", action,
235                         "Switch is not available"));
236             }
237         }
238         return new Status(StatusCode.INTERNALERROR, errorString("send", action,
239                 "Internal plugin error"));
240     }
241
242     private Status modifyFlowInternal(Node node, Flow oldFlow, Flow newFlow, long rid) {
243         String action = "modify";
244         if (!node.getType().equals(NodeIDType.OPENFLOW)) {
245             return new Status(StatusCode.NOTACCEPTABLE, errorString("send",
246                     action, "Invalid node type"));
247         }
248         if (controller != null) {
249             ISwitch sw = controller.getSwitch((Long) node.getID());
250             if (sw != null) {
251                 OFMessage msg1 = null, msg2 = null;
252
253                 // If priority and match portion are the same, send a
254                 // modification message
255                 if (oldFlow.getPriority() != newFlow.getPriority()
256                         || !oldFlow.getMatch().equals(newFlow.getMatch())) {
257                     msg1 = new FlowConverter(oldFlow).getOFFlowMod(
258                             OFFlowMod.OFPFC_DELETE_STRICT, OFPort.OFPP_NONE);
259                     msg2 = new FlowConverter(newFlow).getOFFlowMod(
260                             OFFlowMod.OFPFC_ADD, null);
261                 } else {
262                     msg1 = new FlowConverter(newFlow).getOFFlowMod(
263                             OFFlowMod.OFPFC_MODIFY_STRICT, null);
264                 }
265                 /*
266                  * Synchronous message send
267                  */
268                 action = (msg2 == null) ? "modify" : "delete";
269                 Object result;
270                 if (rid == 0) {
271                     /*
272                      * Synchronous message send. Each message is followed by a
273                      * Barrier message.
274                      */
275                     result = sw.syncSend(msg1);
276                 } else {
277                     /*
278                      * Message will be sent asynchronously. A Barrier message
279                      * will be inserted automatically to synchronize the
280                      * progression.
281                      */
282                     result = asyncMsgSend(node, sw, msg1, rid);
283                 }
284                 if (result instanceof Boolean) {
285                     if ((Boolean) result == Boolean.FALSE) {
286                         return new Status(StatusCode.TIMEOUT, errorString(null,
287                                 action, "Request Timed Out"));
288                     } else if (msg2 == null) {
289                         return new Status(StatusCode.SUCCESS, null);
290                     }
291                 } else if (result instanceof OFError) {
292                     return new Status(StatusCode.INTERNALERROR, errorString(
293                             "program", action,
294                             Utils.getOFErrorString((OFError) result)));
295                 } else {
296                     return new Status(StatusCode.INTERNALERROR, errorString(
297                             "send", action, "Internal Error"));
298                 }
299
300                 if (msg2 != null) {
301                     action = "add";
302                     if (rid == 0) {
303                         /*
304                          * Synchronous message send. Each message is followed by a
305                          * Barrier message.
306                          */
307                         result = sw.syncSend(msg2);
308                     } else {
309                         /*
310                          * Message will be sent asynchronously. A Barrier message
311                          * will be inserted automatically to synchronize the
312                          * progression.
313                          */
314                         result = asyncMsgSend(node, sw, msg2, rid);
315                     }
316                     if (result instanceof Boolean) {
317                         return ((Boolean) result == Boolean.TRUE) ? new Status(
318                                 StatusCode.SUCCESS, null) : new Status(
319                                 StatusCode.TIMEOUT, errorString(null, action,
320                                         "Request Timed Out"));
321                     } else if (result instanceof OFError) {
322                         return new Status(StatusCode.INTERNALERROR,
323                                 errorString("program", action, Utils
324                                         .getOFErrorString((OFError) result)));
325                     } else {
326                         return new Status(StatusCode.INTERNALERROR,
327                                 errorString("send", action, "Internal Error"));
328                     }
329                 }
330             } else {
331                 return new Status(StatusCode.GONE, errorString("send", action,
332                         "Switch is not available"));
333             }
334         }
335         return new Status(StatusCode.INTERNALERROR, errorString("send", action,
336                 "Internal plugin error"));
337     }
338
339     private Status removeFlowInternal(Node node, Flow flow, long rid) {
340         String action = "remove";
341         if (!node.getType().equals(NodeIDType.OPENFLOW)) {
342             return new Status(StatusCode.NOTACCEPTABLE, errorString("send",
343                     action, "Invalid node type"));
344         }
345         if (controller != null) {
346             ISwitch sw = controller.getSwitch((Long) node.getID());
347             if (sw != null) {
348                 OFMessage msg = new FlowConverter(flow).getOFFlowMod(
349                         OFFlowMod.OFPFC_DELETE_STRICT, OFPort.OFPP_NONE);
350                 Object result;
351                 if (rid == 0) {
352                     /*
353                      * Synchronous message send. Each message is followed by a
354                      * Barrier message.
355                      */
356                     result = sw.syncSend(msg);
357                 } else {
358                     /*
359                      * Message will be sent asynchronously. A Barrier message
360                      * will be inserted automatically to synchronize the
361                      * progression.
362                      */
363                     result = asyncMsgSend(node, sw, msg, rid);
364                 }
365                 if (result instanceof Boolean) {
366                     return ((Boolean) result == Boolean.TRUE) ? new Status(
367                             StatusCode.SUCCESS, null) : new Status(
368                             StatusCode.TIMEOUT, errorString(null, action,
369                                     "Request Timed Out"));
370                 } else if (result instanceof OFError) {
371                     return new Status(StatusCode.INTERNALERROR, errorString(
372                             "program", action,
373                             Utils.getOFErrorString((OFError) result)));
374                 } else {
375                     return new Status(StatusCode.INTERNALERROR, errorString(
376                             "send", action, "Internal Error"));
377                 }
378             } else {
379                 return new Status(StatusCode.GONE, errorString("send", action,
380                         "Switch is not available"));
381             }
382         }
383         return new Status(StatusCode.INTERNALERROR, errorString("send", action,
384                 "Internal plugin error"));
385     }
386
387     @Override
388     public Status removeAllFlows(Node node) {
389         return new Status(StatusCode.SUCCESS, null);
390     }
391
392     private String errorString(String phase, String action, String cause) {
393         return "Failed to "
394                 + ((phase != null) ? phase + " the " + action
395                         + " flow message: " : action + " the flow: ") + cause;
396     }
397
398     @Override
399     public void receive(ISwitch sw, OFMessage msg) {
400         if (msg instanceof OFFlowRemoved) {
401             handleFlowRemovedMessage(sw, (OFFlowRemoved) msg);
402         } else if (msg instanceof OFError) {
403             handleErrorMessage(sw, (OFError) msg);
404         }
405     }
406
407     private void handleFlowRemovedMessage(ISwitch sw, OFFlowRemoved msg) {
408         Node node = NodeCreator.createOFNode(sw.getId());
409         Flow flow = new FlowConverter(msg.getMatch(),
410                 new ArrayList<OFAction>(0)).getFlow(node);
411         flow.setPriority(msg.getPriority());
412         flow.setIdleTimeout(msg.getIdleTimeout());
413         flow.setId(msg.getCookie());
414
415         Match match = flow.getMatch();
416         NodeConnector inPort = match.isPresent(MatchType.IN_PORT) ? (NodeConnector) match
417                 .getField(MatchType.IN_PORT).getValue() : null;
418
419         for (Map.Entry<String, IFlowProgrammerNotifier> containerNotifier : flowProgrammerNotifiers
420                 .entrySet()) {
421             String container = containerNotifier.getKey();
422             IFlowProgrammerNotifier notifier = containerNotifier.getValue();
423             /*
424              * Switch only provide us with the match information. For now let's
425              * try to identify the container membership only from the input port
426              * match field. In any case, upper layer consumers can derive
427              * whether the notification was not for them. More sophisticated
428              * filtering can be added later on.
429              */
430             if (inPort == null
431                     || container.equals(GlobalConstants.DEFAULT.toString())
432                     || this.containerToNc.get(container).contains(inPort)) {
433                 notifier.flowRemoved(node, flow);
434             }
435         }
436     }
437
438     private void handleErrorMessage(ISwitch sw, OFError errorMsg) {
439         Node node = NodeCreator.createOFNode(sw.getId());
440         OFMessage offendingMsg = errorMsg.getOffendingMsg();
441         Integer xid;
442         if (offendingMsg != null) {
443             xid = offendingMsg.getXid();
444         } else {
445             xid = errorMsg.getXid();
446         }
447
448         long rid = getMessageRid(sw.getId(), xid);
449         for (Map.Entry<String, IFlowProgrammerNotifier> containerNotifier : flowProgrammerNotifiers
450                 .entrySet()) {
451             IFlowProgrammerNotifier notifier = containerNotifier.getValue();
452             notifier.flowErrorReported(node, rid, errorMsg);
453         }
454     }
455
456     @Override
457     public void tagUpdated(String containerName, Node n, short oldTag,
458             short newTag, UpdateType t) {
459
460     }
461
462     @Override
463     public void containerFlowUpdated(String containerName,
464             ContainerFlow previousFlow, ContainerFlow currentFlow, UpdateType t) {
465     }
466
467     @Override
468     public void nodeConnectorUpdated(String containerName, NodeConnector p,
469             UpdateType type) {
470         Set<NodeConnector> target = null;
471
472         switch (type) {
473         case ADDED:
474             if (!containerToNc.containsKey(containerName)) {
475                 containerToNc.put(containerName, new HashSet<NodeConnector>());
476             }
477             containerToNc.get(containerName).add(p);
478             break;
479         case CHANGED:
480             break;
481         case REMOVED:
482             target = containerToNc.get(containerName);
483             if (target != null) {
484                 target.remove(p);
485             }
486             break;
487         default:
488         }
489     }
490
491     @Override
492     public void containerModeUpdated(UpdateType t) {
493
494     }
495
496     @Override
497     public Status sendBarrierMessage(Node node) {
498         if (!node.getType().equals(NodeIDType.OPENFLOW)) {
499             return new Status(StatusCode.NOTACCEPTABLE,
500                     "The node does not support Barrier message.");
501         }
502
503         if (controller != null) {
504             long swid = (Long) node.getID();
505             ISwitch sw = controller.getSwitch(swid);
506             if (sw != null) {
507                 sw.sendBarrierMessage();
508                 clearXid2Rid(swid);
509                 return (new Status(StatusCode.SUCCESS, null));
510             } else {
511                 return new Status(StatusCode.GONE,
512                         "The node does not have a valid Switch reference.");
513             }
514         }
515         return new Status(StatusCode.INTERNALERROR,
516                 "Failed to send Barrier message.");
517     }
518     
519     /**
520      * This method sends the message asynchronously until the number of messages
521      * sent reaches a threshold. Then a Barrier message is sent automatically
522      * for sync purpose. An unique Request ID associated with the message is
523      * passed down by the caller. The Request ID will be returned to the caller
524      * when an error message is received from the switch.
525      * 
526      * @param node
527      *            The node
528      * @param msg
529      *            The switch
530      * @param msg
531      *            The OF message to be sent
532      * @param rid
533      *            The Request Id
534      * @return result
535      */
536     private Object asyncMsgSend(Node node, ISwitch sw, OFMessage msg, long rid) {
537         Object result = Boolean.TRUE;
538         long swid = (Long) node.getID();
539         int xid;
540
541         xid = sw.asyncSend(msg);
542         addXid2Rid(swid, xid, rid);
543         
544         Map<Integer, Long> swxid2rid = this.xid2rid.get(swid);
545         if (swxid2rid == null) {
546             return result;
547         }
548         
549         int size = swxid2rid.size();
550         if (size % barrierMessagePriorCount == 0) {
551             result = sendBarrierMessage(node);
552         }
553         
554         return result;
555     }
556     
557     /**
558      * A number of async messages are sent followed by a synchronous Barrier
559      * message. This method returns the maximum async messages that can be sent
560      * before the Barrier message.
561      * 
562      * @return The max count of async messages sent prior to Barrier message
563      */
564     private int getBarrierMessagePriorCount() {
565         String count = System.getProperty("of.barrierMessagePriorCount");
566         int rv = 100;
567
568         if (count != null) {
569             try {
570                 rv = Integer.parseInt(count);
571             } catch (Exception e) {
572             }
573         }
574
575         return rv;
576     }
577     
578     /**
579      * This method returns the message Request ID previously assigned by the
580      * caller for a given OF message xid
581      * 
582      * @param swid
583      *            The switch id
584      * @param xid
585      *            The OF message xid
586      * @return The Request ID
587      */
588     public long getMessageRid(long swid, int xid) {
589         Map<Integer, Long> swxid2rid = this.xid2rid.get(swid);
590         long rid = 0;
591         
592         if (swxid2rid != null) {
593             rid = swxid2rid.get(xid);
594         }
595         return rid;
596     }
597
598     /**
599      * This method returns a copy of outstanding xid to rid mappings.for a given
600      * switch
601      * 
602      * @param swid
603      *            The switch id
604      * @return a copy of xid2rid mappings
605      */
606     public Map<Integer, Long> getSwXid2Rid(long swid) {
607         Map<Integer, Long> swxid2rid = this.xid2rid.get(swid);
608         
609         if (swxid2rid != null) {
610             return new HashMap<Integer, Long>(swxid2rid);
611         } else {
612             return new HashMap<Integer, Long>();
613         }
614     }
615
616     /**
617      * Adds xid to rid mapping to the local DB
618      * 
619      * @param swid
620      *            The switch id
621      * @param xid
622      *            The OF message xid
623      * @param rid
624      *            The message Request ID
625      */
626     private void addXid2Rid(long swid, int xid, long rid) {
627         Map<Integer, Long> swxid2rid = this.xid2rid.get(swid);
628         if (swxid2rid != null) {
629             swxid2rid.put(xid, rid);
630         }
631     }
632
633     /**
634      * When an Error message is received, this method will be invoked to remove
635      * the offending xid from the local DB.
636      * 
637      * @param swid
638      *            The switch id
639      * @param xid
640      *            The OF message xid
641      */
642     private void removeXid2Rid(long swid, int xid) {
643         Map<Integer, Long> swxid2rid = this.xid2rid.get(swid);
644         if (swxid2rid != null) {
645             swxid2rid.remove(xid);
646         }
647     }
648
649     /**
650      * When a Barrier reply is received, this method will be invoked to clear
651      * the local DB
652      * 
653      * @param swid
654      *            The switch id
655      */
656     private void clearXid2Rid(long swid) {
657         Map<Integer, Long> swxid2rid = this.xid2rid.get(swid);
658         if (swxid2rid != null) {
659             swxid2rid.clear();
660         }
661     }
662
663     @Override
664     public void updateNode(Node node, UpdateType type, Set<Property> props) {
665         long swid = (Long)node.getID();
666         
667         switch (type) {
668         case ADDED:
669             Map<Integer, Long> swxid2rid = new HashMap<Integer, Long>();
670             this.xid2rid.put(swid, swxid2rid);
671             break;
672         case CHANGED:
673             break;
674         case REMOVED:
675             this.xid2rid.remove(swid);
676             break;
677         default:
678         }
679     }
680
681     @Override
682     public void updateNodeConnector(NodeConnector nodeConnector,
683             UpdateType type, Set<Property> props) {
684     }
685
686     private void registerWithOSGIConsole() {
687         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
688                 .getBundleContext();
689         bundleContext.registerService(CommandProvider.class.getName(), this,
690                 null);
691     }
692
693     @Override
694     public String getHelp() {
695         StringBuffer help = new StringBuffer();
696         help.append("-- Flow Programmer Service --\n");
697         help.append("\t px2r <node id>          - Print outstanding xid2rid mappings for a given node id\n");
698         help.append("\t px2rc                   - Print max num of async msgs prior to the Barrier\n");
699         return help.toString();
700     }
701
702     public void _px2r(CommandInterpreter ci) {
703         String st = ci.nextArgument();
704         if (st == null) {
705             ci.println("Please enter a valid node id");
706             return;
707         }
708         
709         long sid;
710         try {
711             sid = HexEncode.stringToLong(st);
712         } catch (NumberFormatException e) {
713             ci.println("Please enter a valid node id");
714             return;
715         }
716         
717         Map<Integer, Long> swxid2rid = this.xid2rid.get(sid);
718         if (swxid2rid == null) {
719             ci.println("The node id entered does not exist");
720             return;
721         }
722
723         ci.println("xid             rid");
724         
725         Set<Integer> xidSet = swxid2rid.keySet();
726         if (xidSet == null) {
727             return;
728         }
729
730         for (Integer xid : xidSet) {
731             ci.println(xid + "       " + swxid2rid.get(xid));
732         }
733     }
734
735     public void _px2rc(CommandInterpreter ci) {
736         ci.println("Max num of async messages sent prior to the Barrier message is "
737                 + barrierMessagePriorCount);
738     }
739 }