Use RFC8040 URL for JSON-RPC tests
[integration/test.git] / csit / libraries / ALTO / AltoParser.py
1 """
2 Library for ALTO project robot system test framework.
3 Author: linxiao9292@outlook.com
4 """
5
6 import json
7 import re
8
9 content_key_set = {"meta", "resources"}
10 resource_key_set = {"uri", "media-type", "accepts", "capabilities", "uses"}
11 cost_type_key_set = {"cost-mode", "cost-metric", "description"}
12 media_type_set = {
13     "application/alto-directory+json",
14     "application/alto-networkmap+json",
15     "application/alto-networkmapfilter+json",
16     "application/alto-costmap+json",
17     "application/alto-costmapfilter+json",
18     "application/alto-endpointprop+json",
19     "application/alto-endpointpropparams+json",
20     "application/alto-endpointcost+json",
21     "application/alto-endpointcostparams+json",
22     "application/alto-error+json",
23 }
24
25
26 def get_basic_info(response):
27     """Get basic information of the simple IRD.
28
29     Args:
30         :param response: response from restconf/operational/alto-simple-ird:information
31             contains context-id and base-url for alto-simple-ird.
32     Returns:
33         :returns tuple: context-id - Identifier of different implementations of one service.
34             The formation of it is xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
35             For example, we have three implementations of IRD service, and we could use
36             different context-id to identify them.
37
38                         base-url - ALTO northbound URL of simple IRD.
39     """
40     resp = json.loads(response)
41     return resp["information"]["context-id"], resp["information"]["base-url"]
42
43
44 def check_ird_configuration_entry(response, ird_resource_id, context_id, resource_id):
45     """Check whether resources we added are in the resource pool.
46
47     Args:
48         :param response: response from restconf/operational/alto-resourcepool:context/context-id
49             context-id is from get_basic_info(response).
50
51         :param ird_resource_id: ID of the IRD.
52
53         :param context_id: See above.
54
55         :param resource_id: ID of the resource.
56             context-id and resource-id together could determine one implementation of one service.
57     Returns:
58         :return bool: False if we do not get the resource we added before
59     """
60     resp = json.loads(response)
61     resources = resp["context"][0]["resource"]
62     for resource in resources:
63         if resource["resource-id"] == ird_resource_id:
64             context_tags = resource["context-tag"]
65             for tag in context_tags:
66                 if "dependency" in tag:
67                     for one_dependency in tag["dependency"]:
68                         _context_id = re.findall(
69                             r"\d{8}-\d{4}-\d{4}-\d{4}-\d{12}", one_dependency
70                         )[0]
71                         if _context_id == context_id:
72                             long_resource_id = re.findall(
73                                 r"resource-id='[a-zA-Z\-]*'", one_dependency
74                             )[0]
75                             short_resource_id = re.findall("'.*'", long_resource_id)[0]
76                             _resource_id = short_resource_id.replace("'", "")
77                             if _resource_id == resource_id:
78                                 return True
79     return False
80
81
82 def verify_ird(response):
83     """Semantic check of IRD response, more information in RFC 7285 9.2.
84
85     Args:
86         :param response: response from ALTO northbound URL of IRD.
87     Returns:
88         :return: bool: False if there are some semantic errors.
89             One semantic error is that we only define routing-cost in cost-type, but we find that one capability of a
90             resource is bandwidth.
91     """
92     if response.headers["content-type"] != "application/alto-directory+json":
93         return False
94     try:
95         resp = json.loads(response.content)
96     except ValueError:
97         return False
98     if "meta" not in resp:
99         return False
100     meta = resp["meta"]
101     if "cost-types" in meta:
102         cost_types = meta["cost-types"]
103         for cost_type in cost_types:
104             if set(cost_type).issubset(cost_type_key_set):
105                 if "cost-mode" in cost_type and "cost-metric" in cost_type:
106                     continue
107                 else:
108                     return False
109             else:
110                 return False
111
112     resources = resp["resources"]
113     for resource in resources.keys():
114         if set(resources[resource].keys()).issubset(resource_key_set):
115             if (
116                 "uri" not in resources[resource]
117                 or "media-type" not in resources[resource]
118             ):
119                 return False
120             else:
121                 _resource = resources[resource]
122                 media_type = _resource["media-type"]
123                 if media_type not in media_type_set:
124                     return False
125                 if "capabilities" in _resource:
126                     capabilities = _resource["capabilities"]
127                     if "cost-type-names" in capabilities:
128                         cost_type_names = capabilities["cost-type-names"]
129                         for cost_type_name in cost_type_names:
130                             if cost_type_name not in cost_types:
131                                 return False
132         else:
133             return False
134     return True