[ONOS-7883] modify tapiHelper.py to enable line-side service random creation and client-side service ordered creation on TX<->OLS<->TX transimission system

Change-Id: I2b2d6e88dd2484c6af59767ff0f6d62c6b5fe71a
diff --git a/tools/test/scenarios/bin/tapiHelper.py b/tools/test/scenarios/bin/tapiHelper.py
index f5bfcc8..036e27b 100755
--- a/tools/test/scenarios/bin/tapiHelper.py
+++ b/tools/test/scenarios/bin/tapiHelper.py
@@ -2,6 +2,8 @@
 
 import requests
 import json
+import random
+from sets import Set
 
 
 #
@@ -16,14 +18,13 @@
                     "service-interface-point": {
                         "service-interface-point-uuid" : sip_uuids[0]
                     }
-                  }
-                ,
+                  },
                   {
                     "local-id": sip_uuids[1],
                     "service-interface-point": {
                         "service-interface-point-uuid" : sip_uuids[1]
                     }
-                 }
+                  }
                 ]
               }
             }
@@ -36,28 +37,28 @@
 def tapi_line_input(sip_uuids):
     create_input = {
       "tapi-connectivity:input" : {
-         "end-point" : [
+         "end-point": [
             {
-               "layer-protocol-qualifier" : "tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC",
-               "role" : "UNKNOWN",
-               "local-id" : "Src_end_point",
-               "direction" : "BIDIRECTIONAL",
-               "service-interface-point" : {
+               "layer-protocol-qualifier": "tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC",
+               "role": "UNKNOWN",
+               "local-id": "Src_end_point",
+               "direction": "BIDIRECTIONAL",
+               "service-interface-point": {
                   "service-interface-point-uuid" : sip_uuids[0]
                },
-               "protection-role" : "WORK",
-               "layer-protocol-name" : "PHOTONIC_MEDIA"
+               "protection-role": "WORK",
+               "layer-protocol-name": "PHOTONIC_MEDIA"
             },
             {
-               "direction" : "BIDIRECTIONAL",
-               "service-interface-point" : {
-                  "service-interface-point-uuid" : sip_uuids[1]
+               "direction": "BIDIRECTIONAL",
+               "service-interface-point": {
+                  "service-interface-point-uuid": sip_uuids[1]
                },
-               "protection-role" : "WORK",
-               "layer-protocol-name" : "PHOTONIC_MEDIA",
-               "layer-protocol-qualifier" : "tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC",
-               "role" : "UNKNOWN",
-               "local-id" : "Dst_end_point"
+               "protection-role": "WORK",
+               "layer-protocol-name": "PHOTONIC_MEDIA",
+               "layer-protocol-qualifier": "tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC",
+               "role": "UNKNOWN",
+               "local-id": "Dst_end_point"
             }
          ]
       }
@@ -76,6 +77,75 @@
 
 
 #
+# Check if the node is transponder.
+# True  - transponder
+# False - OLS
+#
+def is_transponder_node(node):
+    if len(node["owned-node-edge-point"]) > 0 and "mapped-service-interface-point" in node["owned-node-edge-point"][0]:
+        return True
+    else:
+        return False
+
+
+#
+# Parse src and dst sip-uuids of specific link from topo.
+#
+def parse_src_dst(topo, link_index=-1):
+    if link_index == -1:
+        # select a link randomly from all links of topo
+        link_index = random.randint(0, len(topo["link"]) - 1)
+    nep_pair = topo["link"][link_index]["node-edge-point"]
+    assert topo["uuid"] == nep_pair[0]["topology-uuid"]
+    assert topo["uuid"] == nep_pair[1]["topology-uuid"]
+    src_onep, dst_onep = (find_line_onep(nep_pair[0], topo["node"]),
+                          find_line_onep(nep_pair[1], topo["node"]))
+    if src_onep is not None and dst_onep is not None:
+        # If the link is between two transponders directly
+        pass
+    elif src_onep is None and dst_onep is None:
+        raise AssertionError("Impossible for that both two ports are OLS port")
+    else:
+        # If one of src_onep and dst_onep is None, then make src_onep not None,
+        # and find a new dst_onep with same connection id.
+        if src_onep is None:
+            src_onep = dst_onep
+            dst_onep = None
+        conn_id = parse_value(src_onep["name"])["odtn-connection-id"]
+        for node in topo["node"]:
+            cep = src_onep["tapi-connectivity:cep-list"]["connection-end-point"]
+            assert len(cep) == 1
+            if cep[0]["parent-node-edge-point"]["node-uuid"] != node["uuid"] and is_transponder_node(node):
+                # If this node is not the node that includes src_onep, and not a OLS node
+                for onep in node["owned-node-edge-point"]:
+                    if parse_value(onep["name"])["odtn-connection-id"] == conn_id:
+                        dst_onep = onep
+                        break
+            if dst_onep is not None:
+                break
+
+    src_sip_uuid, dst_sip_uuid = \
+        (src_onep["mapped-service-interface-point"][0]["service-interface-point-uuid"],
+         dst_onep["mapped-service-interface-point"][0]["service-interface-point-uuid"])
+
+    return src_onep, dst_onep, src_sip_uuid, dst_sip_uuid
+
+
+#
+# Check whether the sip uuid is used in other existed services.
+#
+def is_port_used(sip_uuid, conn_context):
+    try:
+        for service in conn_context["connectivity-service"]:
+            for id in [0, 1]:
+                if service["end-point"][id]["service-interface-point"]["service-interface-point-uuid"] == sip_uuid:
+                    return True
+    except KeyError:
+        print "There is no line-side service in ONOS now."
+    return False
+
+
+#
 # Requests a connectivity service
 #
 def request_connection(url_connectivity, context):
@@ -127,37 +197,104 @@
 
 
 #
+# Find mapped client-side sip_uuid according to a line-side sip_uuid.
+# connection-ids of these two owned-node-edge-point should be the same.
+#
+def find_mapped_client_sip_uuid(line_sip_uuid, nodes):
+    line_node = None
+    line_onep = None
+    for node in nodes:
+        if is_transponder_node(node):
+            for onep in node["owned-node-edge-point"]:
+                if onep["mapped-service-interface-point"][0]["service-interface-point-uuid"] == line_sip_uuid:
+                    line_node = node
+                    line_onep = onep
+                    break
+    if line_node is None:
+        raise AssertionError("Cannot match line-side sip uuid in topology.")
+    conn_id = parse_value(line_onep["name"])["odtn-connection-id"]
+    for onep in line_node["owned-node-edge-point"]:
+        vals = parse_value(onep["name"])
+        if vals["odtn-connection-id"] == conn_id and vals["odtn-port-type"] == "client":
+            return onep["mapped-service-interface-point"][0]["service-interface-point-uuid"], vals
+    return None
+
+
+#
 # Create a client-side connection. Firstly, get the context, parsing for SIPs that connect
 # with each other in line-side; Secondly, issue the request
 #
 def create_client_connection(url_context, url_connectivity):
+    headers = {'Content-type': 'application/json'}
     context = get_context(url_context)
     # select the first topo from all topologies
     topo = context["tapi-common:context"]["tapi-topology:topology-context"]["topology"][0]
+    # Gather all current used sip_uuids
+    used_sip_uuids = Set()
+    try:
+        services = context["tapi-common:context"]["tapi-connectivity:connectivity-context"]["connectivity-service"]
+        for service in services:
+            used_sip_uuids.add(service["end-point"][0]["service-interface-point"]["service-interface-point-uuid"])
+            used_sip_uuids.add(service["end-point"][1]["service-interface-point"]["service-interface-point-uuid"])
+    except KeyError:
+        print "There is no existed connectivity service inside ONOS."
 
-    # select the first link from all links of topo
-    nep_pair = topo["link"][0]["node-edge-point"]
-    assert topo["uuid"] == nep_pair[0]["topology-uuid"]
-    assert topo["uuid"] == nep_pair[1]["topology-uuid"]
-    (src_onep, dst_onep) = (find_client_onep(nep_pair[0], topo["node"]),
-                            find_client_onep(nep_pair[1], topo["node"]))
-    print "client side neps", (src_onep, dst_onep)
+    # select the first available line-side service as bridge. If there is no available line-side service,
+    # then only create a client-to-client service for src and dst node.
+    empty_client_src_sip_uuid, empty_client_dst_sip_uuid = None, None
+    empty_src_name, empty_dst_name, empty_client_src_name, empty_client_dst_name = None, None, None, None
+    for link_index in range(0, len(topo["link"])):
+        src_onep, dst_onep, src_sip_uuid, dst_sip_uuid = parse_src_dst(topo, link_index)
+        client_src_sip_uuid, client_src_name = find_mapped_client_sip_uuid(src_sip_uuid, topo["node"])
+        client_dst_sip_uuid, client_dst_name = find_mapped_client_sip_uuid(dst_sip_uuid, topo["node"])
+        # firstly, check if line-side service exists
+        # If line-side service exists
+        if src_sip_uuid in used_sip_uuids and dst_sip_uuid in used_sip_uuids:
+            # secondly, check if mapped client-side service exists
+            if (client_src_sip_uuid not in used_sip_uuids) and (client_dst_sip_uuid not in used_sip_uuids):
+                # If there is no such client-side connection exists
+                # Create new client-side connection directly
+                print "Create client-side connection between %s and %s." % \
+                      (client_src_name["onos-cp"], client_dst_name["onos-cp"])
+                create_input_json = json.dumps(tapi_client_input((client_src_sip_uuid, client_dst_sip_uuid)))
+                resp = requests.post(url_connectivity, data=create_input_json, headers=headers,
+                                     auth=('onos', 'rocks'))
+                if resp.status_code != 200:
+                    raise Exception('POST {}'.format(resp.status_code))
+                return resp
+            else:
+                # If there exists such client-side connection
+                # Do nothing, just continue
+                pass
+        else:
+            # If line-side service doesn't exist
+            # save 4 sip uuids, and continue
+            empty_client_src_sip_uuid = client_src_sip_uuid
+            empty_client_dst_sip_uuid = client_dst_sip_uuid
+            empty_client_src_name = client_src_name
+            empty_client_dst_name = client_dst_name
+            empty_src_name = parse_value(src_onep["name"])
+            empty_dst_name = parse_value(dst_onep["name"])
+            pass
 
-    src_sip_uuid, dst_sip_uuid = \
-        (src_onep["mapped-service-interface-point"][0]["service-interface-point-uuid"],
-         dst_onep["mapped-service-interface-point"][0]["service-interface-point-uuid"])
-    print "\nBuild client-side connectivity:\n|Item|SRC|DST|\n|:--|:--|:--|\n|onos-cp|%s|%s|\n|connection id|%s|%s|\n|sip uuid|%s|%s|" % \
-          (src_onep["name"][2]["value"], dst_onep["name"][2]["value"],
-           src_onep["name"][1]["value"], dst_onep["name"][1]["value"],
-           src_sip_uuid, dst_sip_uuid)
-    create_input_json = json.dumps(tapi_client_input((src_sip_uuid, dst_sip_uuid)))
-    print "\nThe json content of creation operation for client-side connectivity service is \n\t\t%s." % \
-          create_input_json
-    headers = {'Content-type': 'application/json'}
-    resp = requests.post(url_connectivity, data=create_input_json, headers=headers, auth=('onos', 'rocks'))
-    if resp.status_code != 200:
-        raise Exception('POST {}'.format(resp.status_code))
-    return resp
+    # After FOR loop, if this method doesn't return, there is no available line-side
+    # service for mapped client-side service creation.
+    # So, we need to create two client-side services.
+    if empty_client_src_sip_uuid is None:
+        # None case means all client-side services exist.
+        raise AssertionError("There is no available client-side service could be created.")
+    else:
+        print "Create client-side services:"
+        print "\t- from %s to %s." % (empty_client_src_name["onos-cp"], empty_client_dst_name["onos-cp"])
+        print "This service should go through:"
+        print "\t- %s and %s." % (empty_src_name["onos-cp"], empty_dst_name["onos-cp"])
+        create_input_json = json.dumps(tapi_client_input((empty_client_src_sip_uuid, empty_client_dst_sip_uuid)))
+        resp = requests.post(url_connectivity, data=create_input_json, headers=headers,
+                             auth=('onos', 'rocks'))
+        if resp.status_code != 200:
+            raise Exception('POST {}'.format(resp.status_code))
+        return resp
+
 
 #
 # Parse array structure "name" under structure "owned node edge point"
@@ -168,6 +305,7 @@
         rtn[item["value-name"]] = item["value"]
     return rtn
 
+
 #
 # Find node edge point of node structure in topology with client-side port, by using nep with line-side port.
 # The odtn-connection-id should be the same in both line-side nep and client-side nep
@@ -201,16 +339,12 @@
     context = get_context(url_context)
     # select the first topo from all topologies
     topo = context["tapi-common:context"]["tapi-topology:topology-context"]["topology"][0]
+    # select randomly the src_sip_uuid and dst_sip_uuid with same connection id.
+    src_onep, dst_onep, src_sip_uuid, dst_sip_uuid = parse_src_dst(topo)
+    while is_port_used(src_sip_uuid, context["tapi-common:context"]["tapi-connectivity:connectivity-context"]):
+        print "Conflict occurs between randomly selected line-side link and existed ones."
+        src_onep, dst_onep, src_sip_uuid, dst_sip_uuid = parse_src_dst(topo)
 
-    # select the first link from all links of topo
-    nep_pair = topo["link"][0]["node-edge-point"]
-    assert topo["uuid"] == nep_pair[0]["topology-uuid"]
-    assert topo["uuid"] == nep_pair[1]["topology-uuid"]
-    src_onep, dst_onep = (find_line_onep(nep_pair[0], topo["node"]),
-                          find_line_onep(nep_pair[1], topo["node"]))
-    src_sip_uuid, dst_sip_uuid = \
-        (src_onep["mapped-service-interface-point"][0]["service-interface-point-uuid"],
-         dst_onep["mapped-service-interface-point"][0]["service-interface-point-uuid"])
     print "\nBuild line-side connectivity:\n|Item|SRC|DST|\n|:--|:--|:--|\n|onos-cp|%s|%s|\n|connection id|%s|%s|\n|sip uuid|%s|%s|" % \
           (src_onep["name"][2]["value"], dst_onep["name"][2]["value"],
            src_onep["name"][1]["value"], dst_onep["name"][1]["value"],
@@ -225,14 +359,20 @@
     return resp
 
 
+#
+# find owned-node-edge-point from all nodes according to line_nep_in_links
+#
 def find_line_onep(line_nep_in_link, nodes):
     for node in nodes:
         if node["uuid"] == line_nep_in_link["node-uuid"]:
+            if not is_transponder_node(node):
+                break
             for onep in node["owned-node-edge-point"]:
                 if onep["uuid"] == line_nep_in_link["node-edge-point-uuid"]:
                     # check the length equals 1 to verify the 1-to-1 mapping relationship
                     assert len(onep["mapped-service-interface-point"]) == 1
                     return onep
+    # When node is OLS, this method will return None
     return None