Tunnel loadbalancing policy: phase2 support
diff --git a/cli/c_actions.py b/cli/c_actions.py
index b2ab1f8..debece3 100755
--- a/cli/c_actions.py
+++ b/cli/c_actions.py
@@ -95,8 +95,9 @@
 
 tunnelset_id=None
 tunnelset_dict=[]
+tunnelset_remove_tunnels=[]
 def tunnelset_create(data=None):
-    global tunnelset_id,tunnelset_dict
+    global tunnelset_id,tunnelset_dict,tunnelset_remove_tunnels
     if sdnsh.description:   # description debugging
         print "tunnelset_create:" , data
     if data.has_key('tunnelset-id'):
@@ -105,22 +106,27 @@
                 print "tunnelset_create: previous data is not cleaned up"
             tunnelset_id=None
             tunnelset_dict=[]
+            tunnelset_remove_dict=[]
         tunnelset_id=data['tunnelset-id']
         tunnelset_dict=[]
+        tunnelset_remove_dict=[]
     if sdnsh.description:   # description debugging
         print "tunnelset_create:" , tunnelset_id
 
 def tunnelset_config_exit():
-    global tunnelset_id,tunnelset_dict
+    global tunnelset_id,tunnelset_dict,tunnelset_remove_tunnels
     if sdnsh.description:   # description debugging
         print "tunnelset_config_exit entered", tunnelset_dict
-    if tunnelset_dict:
+    if (len(tunnelset_dict) > 0) or (len(tunnelset_remove_tunnels)>0):
         url_str = ""
         entries = tunnelset_dict
         url_str = "http://%s/rest/v1/tunnelset/" % (sdnsh.controller)
         obj_data = {}
         obj_data['tunnelset_id']=tunnelset_id
-        obj_data['tunnel_params']=entries
+        if (len(entries) > 0):
+            obj_data['tunnel_params']=entries
+        if (len(tunnelset_remove_tunnels) > 0):
+            obj_data['remove_tunnel_params']=tunnelset_remove_tunnels
         result = "fail"
         try:
             result = sdnsh.store.rest_post_request(url_str,obj_data)
@@ -129,6 +135,7 @@
             print sdnsh.rest_error_dict_to_message(errors)
         # LOOK! successful stuff should be returned in json too.
         tunnelset_dict = []
+        tunnelset_remove_tunnels = []
         tunnelset_id = None
         curr_tunnel_id = None
         if result != "success":
@@ -141,7 +148,7 @@
     if sdnsh.description:   # description debugging
         print "tunnelset_remove:" , data
     tunnelset_id=data['tunnelset-id']
-    url_str = "http://%s/rest/v1/tunnel/" % (sdnsh.controller)
+    url_str = "http://%s/rest/v1/tunnelset/" % (sdnsh.controller)
     obj_data = {}
     obj_data['tunnelset_id']=data['tunnelset-id']
     result = "fail"
@@ -175,7 +182,7 @@
         print "tunnel_create:" , tunnel_id, tunnel_dict
 
 def tunnel_config_exit():
-    global tunnel_id,tunnel_dict
+    global tunnel_id,tunnel_dict,tunnelset_dict
     if sdnsh.description:   # description debugging
         print "tunnel_config_exit entered", tunnel_dict
         
@@ -206,20 +213,24 @@
     #Clear the transit information    
             
 def tunnel_remove(data=None):
+    global tunnelset_remove_tunnels
     if sdnsh.description:   # description debugging
         print "tunnel_remove:" , data
     tunnel_id=data['tunnel-id']
-    url_str = "http://%s/rest/v1/tunnel/" % (sdnsh.controller)
-    obj_data = {}
-    obj_data['tunnel_id']=data['tunnel-id']
-    result = "fail"
-    try:
-        result = sdnsh.store.rest_post_request(url_str,obj_data,'DELETE')
-    except Exception, e:
-        errors = sdnsh.rest_error_to_dict(e)
-        print sdnsh.rest_error_dict_to_message(errors)
-    if not result.startswith("SUCCESS"):
-        print result
+    if tunnelset_id:
+        tunnelset_remove_tunnels.append(tunnel_id)
+    else:
+        url_str = "http://%s/rest/v1/tunnel/" % (sdnsh.controller)
+        obj_data = {}
+        obj_data['tunnel_id']=data['tunnel-id']
+        result = "fail"
+        try:
+            result = sdnsh.store.rest_post_request(url_str,obj_data,'DELETE')
+        except Exception, e:
+            errors = sdnsh.rest_error_to_dict(e)
+            print sdnsh.rest_error_dict_to_message(errors)
+        if not result.startswith("SUCCESS"):
+            print result
 
 
 policy_obj_data = {}
@@ -821,6 +832,16 @@
     if (mode_name == 'config-tunnel'):
         if (current_mode == 'config-tunnelset'):
             mode_name = 'config-tunnelset-tunnel'
+    if (mode_name == 'config-policy'):
+        if (data.has_key('policy-type')):
+            if (data['policy-type'] == 'tunnel-flow'):
+                mode_name = 'config-policy-tunnel'
+            if (data['policy-type'] == 'loadbalance'):
+                mode_name = 'config-policy-loadbalance'
+            if (data['policy-type'] == 'avoid'):
+                mode_name = 'config-policy-avoid'
+            if sdnsh.description:   # description debugging
+                print "Changing config-policy sub mode to ", mode_name
 
     if sdnsh.description:   # description debugging
         print "push_mode: ", mode_name, obj_type, data, parent_field, parent_id
@@ -936,7 +957,7 @@
         exitCallback = tunnelset_config_exit
     if ((mode_name == 'config-tunnel') or (mode_name == 'config-tunnelset-tunnel')):
         exitCallback = tunnel_config_exit
-    if (mode_name == 'config-policy'):
+    if (mode_name.startswith('config-policy')):
         exitCallback = policy_config_exit
     sdnsh.push_mode(mode_name, obj_type, key, exitCallback)
 
diff --git a/cli/command.py b/cli/command.py
index d99ed41..b710e0b 100755
--- a/cli/command.py
+++ b/cli/command.py
@@ -1352,6 +1352,28 @@
                 matching_commands = self.get_matching_commands(command_word,
                                                                self.is_no_command,
                                                                command_registry)
+                # There may be multiple results. We need to figure out it there's
+                # an unambiguous match. The heuristic we use is to iterate over
+                # the command words and try to find a single result which has an
+                # exact match on the word while all the others are prefix matches.
+                # If there are multiple results that are exact matches, then we
+                # discard the other results that were prefix matches and continue
+                # with the next command word. If at an iteration all of the
+                # results are prefix matches for the current word, then we check
+                # all of the full tokens that were prefix matched by the command
+                # word. If all of the results have the same full token, then we
+                # continue. If there are multiple full tokens, then that's
+                # indicative of an ambiguous command, so we break out of the loop
+                # and raise an ambiguous command exception.
+                exact_match_commands = []
+                for matching_command in matching_commands:
+                    command, prefix_match = matching_command
+                    if prefix_match == False:
+                        #exact match found
+                        exact_match_commands.append(matching_command)
+                if len(exact_match_commands) > 0:
+                    matching_commands = exact_match_commands
+                    
                 for matching_command in matching_commands:
                     command, prefix_match = matching_command
                     self.handle_one_command(command, prefix_match, words)
@@ -1795,7 +1817,7 @@
                     }
                     arg_scopes.insert(0, invocation_scope)
                     if sdnsh.description: # for deubugging, print command name
-                        print command['self'], completion_proc
+                        print "command and completion_proc", command['self'], completion_proc
                     try:
                         _result = _call_proc(completion_proc, completion_registry,
                                             arg_scopes, command)
diff --git a/cli/desc/version200/policy.py b/cli/desc/version200/policy.py
index ad99363..733f2e6 100644
--- a/cli/desc/version200/policy.py
+++ b/cli/desc/version200/policy.py
@@ -17,7 +17,7 @@
     'mode'          : 'config',
     'command-type'  : 'config-submode',
     'obj-type'      : 'policy-config',
-    'submode-name'  : 'config-policy',
+    'submode-name'  : 'config-policy', #config-policy-tunnel or config-policy-loadbalance or config-policy-avoid
     'parent-field'  : None,
     'doc'           : 'policy|policy',
     'doc-example'   : 'policy|policy-example',
@@ -56,11 +56,10 @@
                     'field'        : 'policy-type',
                     'optional-for-no' : True,
                     'type'         : 'enum',
-                    'values'       : ('tunnel-flow','loadbalanced','avoid','deny'), 
+                    'values'       : ('tunnel-flow','loadbalance','avoid','deny'), 
                     'completion'   : 'complete-object-field',
                     'syntax-help'  : 'Enter a policy type',
                     'doc'          : 'policy|policy',
-                    #'doc-include'  : [ 'type-doc' ],
                 },
             ),
         ),
@@ -179,7 +178,7 @@
 
 POLICY_FLOW_ENTRY_COMMAND_DESCRIPTION = {
     'name'            : 'flow-entry',
-    'mode'            : 'config-policy',
+    'mode'            : 'config-policy*',
     'command-type'    : 'config',
     'short-help'      : 'Configure flow entry',
     'doc'             : 'flow-entry|flow-entry',
@@ -241,7 +240,7 @@
 }
 POLICY_TUNNEL_ID_COMMAND_DESCRIPTION = {
     'name'            : 'tunnel',
-    'mode'            : 'config-policy',
+    'mode'            : 'config-policy-tunnel',
     #'obj-type'        : 'policy-config',
     'command-type'    : 'config',
     'short-help'      : 'Configure tunnel id',
@@ -249,13 +248,13 @@
     #'doc-example'     : 'policy|policy-tunnel-example',
     'parent-field'    : 'policy',
     'args' : {
+        'field'        : 'tunnel-id',
+        'completion'   : 'tunnel-id-completion',
         'action'       : (
                             {
                                 'proc' : 'create-policy',
                             },
                          ),
-        'completion'   : 'tunnel-id-completion',
-        'field'        : 'tunnel-id',
         'type'         : 'identifier',
         'syntax-help'  : 'Enter tunnel id',
         'doc'          : 'policy|tunnel-id',
@@ -265,7 +264,7 @@
 
 POLICY_TUNNELSET_ID_COMMAND_DESCRIPTION = {
     'name'            : 'tunnelset',
-    'mode'            : 'config-policy',
+    'mode'            : 'config-policy-loadbalance',
     #'obj-type'        : 'policy-config',
     'command-type'    : 'config',
     'short-help'      : 'Configure tunnelset id',
@@ -273,13 +272,13 @@
     #'doc-example'     : 'policy|policy-tunnel-example',
     'parent-field'    : 'policy',
     'args' : {
+        'field'        : 'tunnelset-id',
+        'completion'   : 'tunnelset-id-completion',
         'action'       : (
                             {
                                 'proc' : 'create-policy',
                             },
                          ),
-        'completion'   : 'tunnelset-id-completion',
-        'field'        : 'tunnelset-id',
         'type'         : 'identifier',
         'syntax-help'  : 'Enter tunnelset id',
         'doc'          : 'policy|tunnelset-id',
@@ -289,7 +288,7 @@
 
 POLICY_PRIORITY_COMMAND_DESCRIPTION = {
     'name'            : 'priority',
-    'mode'            : 'config-policy',
+    'mode'            : 'config-policy*',
     'command-type'    : 'config',
     'short-help'      : 'Configure policy priority',
     'doc'             : 'policy|priority',
@@ -331,7 +330,6 @@
     )
 }
 
-
 def tunnel_id_completion(prefix, completions):
     query_url = "http://127.0.0.1:8000/rest/v1/showtunnel"
     result = command.sdnsh.store.rest_simple_request(query_url)
@@ -372,4 +370,4 @@
 command.add_completion('policy-id-completion', policy_id_completion,
                        {'kwargs': { 'prefix'       : '$text',
                                     'completions'  : '$completions',
-                                    }})
\ No newline at end of file
+                                    }})