Radek Krejci | a133960 | 2017-11-02 13:52:38 +0100 | [diff] [blame] | 1 | """ |
| 2 | NETCONF data helper functions |
| 3 | File: data.py |
| 4 | Author: Radek Krejci <rkrejci@cesnet.cz> |
| 5 | """ |
| 6 | |
| 7 | import json |
| 8 | import os |
| 9 | |
Radek Krejci | 0f793fe | 2018-02-14 08:33:45 +0100 | [diff] [blame] | 10 | import yang |
Radek Krejci | a133960 | 2017-11-02 13:52:38 +0100 | [diff] [blame] | 11 | import netconf2 as nc |
Radek Krejci | 38b0a0b | 2018-08-03 10:17:03 +0200 | [diff] [blame] | 12 | from .schemas import make_schema_key |
| 13 | |
Radek Krejci | a133960 | 2017-11-02 13:52:38 +0100 | [diff] [blame] | 14 | |
Radek Krejci | 4d3896c | 2018-01-08 17:10:43 +0100 | [diff] [blame] | 15 | def infoBuiltInType(base): |
| 16 | return { |
Radek Krejci | 6e772b2 | 2018-01-25 13:28:57 +0100 | [diff] [blame] | 17 | - 1 : 'error', |
| 18 | 0 : 'derived', |
| 19 | 1 : 'binary', |
| 20 | 2 : 'bits', |
| 21 | 3 : 'boolean', |
| 22 | 4 : 'decimal64', |
| 23 | 5 : 'empty', |
| 24 | 6 : 'enumeration', |
| 25 | 7 : 'identityref', |
| 26 | 8 : 'instance-identifier', |
| 27 | 9 : 'leafref', |
Radek Krejci | 4d3896c | 2018-01-08 17:10:43 +0100 | [diff] [blame] | 28 | 10: 'string', |
| 29 | 11: 'union', |
| 30 | 12: 'int8', |
| 31 | 13: 'uint8', |
| 32 | 14: 'int16', |
| 33 | 15: 'uint16', |
| 34 | 16: 'int32', |
| 35 | 17: 'uint32', |
| 36 | 18: 'int64', |
| 37 | 19: 'uint64', |
| 38 | }[base] |
| 39 | |
Radek Krejci | 6e772b2 | 2018-01-25 13:28:57 +0100 | [diff] [blame] | 40 | |
| 41 | def schemaInfoType(schema, info): |
| 42 | info["datatype"] = schema.type().der().name() |
| 43 | info["datatypebase"] = infoBuiltInType(schema.type().base()) |
| 44 | |
| 45 | |
Radek Krejci | d0ce4cf | 2018-02-09 14:44:34 +0100 | [diff] [blame] | 46 | def typeValues(type, result): |
| 47 | while type.der(): |
| 48 | if type.base() == 2: |
| 49 | # bits |
| 50 | if type.info().bits().count(): |
| 51 | for bit in type.info().bits().bit(): |
| 52 | result.append(bit.name()) |
| 53 | elif type.base() == 6: |
| 54 | # enumeration |
| 55 | if type.info().enums().count(): |
| 56 | for enm in type.info().enums().enm(): |
| 57 | result.append(enm.name()) |
| 58 | else: |
| 59 | return result |
| 60 | type = type.der().type() |
| 61 | |
| 62 | return result |
| 63 | |
Radek Krejci | 6e772b2 | 2018-01-25 13:28:57 +0100 | [diff] [blame] | 64 | def schemaInfoNode(schema): |
| 65 | info = {} |
| 66 | |
| 67 | info["type"] = schema.nodetype() |
Radek Krejci | ad45e57 | 2018-02-21 10:54:54 +0100 | [diff] [blame] | 68 | if schema.module().rev_size(): |
| 69 | info["module"] = schema.module().name() + '@' + schema.module().rev().date() |
| 70 | else: |
| 71 | info["module"] = schema.module().name() |
Radek Krejci | 6e772b2 | 2018-01-25 13:28:57 +0100 | [diff] [blame] | 72 | info["name"] = schema.name() |
| 73 | info["dsc"] = schema.dsc() |
Radek Krejci | 0f793fe | 2018-02-14 08:33:45 +0100 | [diff] [blame] | 74 | info["config"] = True if schema.flags() & yang.LYS_CONFIG_W else False |
Radek Krejci | 6e772b2 | 2018-01-25 13:28:57 +0100 | [diff] [blame] | 75 | if info["type"] == 1: |
| 76 | info["presence"] = schema.subtype().presence() |
| 77 | info["path"] = schema.path() |
| 78 | |
Radek Krejci | 0f793fe | 2018-02-14 08:33:45 +0100 | [diff] [blame] | 79 | if info["type"] == yang.LYS_LEAF: |
Radek Krejci | 6e772b2 | 2018-01-25 13:28:57 +0100 | [diff] [blame] | 80 | schemaInfoType(schema.subtype(), info) |
Radek Krejci | 6c18e39 | 2018-08-23 13:21:29 +0200 | [diff] [blame] | 81 | info["key"] = False if schema.subtype().is_key() == None else True |
Radek Krejci | 6e772b2 | 2018-01-25 13:28:57 +0100 | [diff] [blame] | 82 | dflt = schema.subtype().dflt() |
| 83 | if dflt: |
| 84 | info["default"] = dflt |
| 85 | else: |
| 86 | tpdf = schema.subtype().type().der() |
| 87 | while tpdf and not tpdf.dflt(): |
| 88 | tpdf = tpdf.type().der() |
| 89 | if tpdf: |
| 90 | info["default"] = tpdf.dflt() |
Radek Krejci | 0f793fe | 2018-02-14 08:33:45 +0100 | [diff] [blame] | 91 | elif info["type"] == yang.LYS_LEAFLIST: |
Radek Krejci | 6e772b2 | 2018-01-25 13:28:57 +0100 | [diff] [blame] | 92 | schemaInfoType(schema.subtype(), info) |
Radek Krejci | 010475d | 2018-03-08 13:14:19 +0100 | [diff] [blame] | 93 | if schema.flags() & yang.LYS_USERORDERED: |
| 94 | info["ordered"] = True; |
Radek Krejci | 0f793fe | 2018-02-14 08:33:45 +0100 | [diff] [blame] | 95 | elif info["type"] == yang.LYS_LIST: |
Radek Krejci | 010475d | 2018-03-08 13:14:19 +0100 | [diff] [blame] | 96 | if schema.flags() & yang.LYS_USERORDERED: |
| 97 | info["ordered"] = True; |
Radek Krejci | 6e772b2 | 2018-01-25 13:28:57 +0100 | [diff] [blame] | 98 | info["keys"] = [] |
| 99 | for key in schema.subtype().keys(): |
| 100 | info["keys"].append(key.name()) |
| 101 | |
| 102 | return info |
| 103 | |
Radek Krejci | 4d3896c | 2018-01-08 17:10:43 +0100 | [diff] [blame] | 104 | |
Radek Krejci | 30ce159 | 2018-03-01 14:44:14 +0100 | [diff] [blame] | 105 | def _sortChildren(node): |
| 106 | sorted = [] |
| 107 | for index, item in enumerate(node["children"]): |
| 108 | sorted.append(item) |
| 109 | if item["info"]["type"] == yang.LYS_LIST: |
| 110 | removed = 0 |
Radek Krejci | 010475d | 2018-03-08 13:14:19 +0100 | [diff] [blame] | 111 | if "ordered" in item["info"]: |
| 112 | item["order"] = removed |
Radek Krejci | 30ce159 | 2018-03-01 14:44:14 +0100 | [diff] [blame] | 113 | for i, instance in enumerate(node["children"][index + 1:]): |
| 114 | if item["info"]["name"] == instance["info"]["name"] and item["info"]["module"] == instance["info"]["module"]: |
| 115 | sorted.append(node["children"].pop(index + 1 + i - removed)) |
| 116 | removed += 1; |
Radek Krejci | 010475d | 2018-03-08 13:14:19 +0100 | [diff] [blame] | 117 | if "ordered" in item["info"]: |
| 118 | instance["order"] = removed |
Radek Krejci | 30ce159 | 2018-03-01 14:44:14 +0100 | [diff] [blame] | 119 | if item["info"]["type"] == yang.LYS_LEAFLIST: |
| 120 | lastLeafList = len(sorted) - 1 |
| 121 | item["first"] = True |
| 122 | removed = 0 |
Radek Krejci | 010475d | 2018-03-08 13:14:19 +0100 | [diff] [blame] | 123 | if "ordered" in item["info"]: |
| 124 | item["order"] = removed |
Radek Krejci | 30ce159 | 2018-03-01 14:44:14 +0100 | [diff] [blame] | 125 | for i, instance in enumerate(node["children"][index + 1:]): |
| 126 | if item["info"]["name"] == instance["info"]["name"] and item["info"]["module"] == instance["info"]["module"]: |
| 127 | instance["first"] = False |
| 128 | sorted.append(node["children"].pop(index + 1 + i - removed)) |
| 129 | removed += 1; |
Radek Krejci | 010475d | 2018-03-08 13:14:19 +0100 | [diff] [blame] | 130 | if "ordered" in item["info"]: |
| 131 | instance["order"] = removed |
Radek Krejci | 30ce159 | 2018-03-01 14:44:14 +0100 | [diff] [blame] | 132 | node["children"] = sorted |
| 133 | last = node["children"][len(node["children"]) - 1] |
| 134 | if last["info"]["type"] == yang.LYS_LEAFLIST: |
| 135 | node["children"][lastLeafList]["last"] = True |
| 136 | for item in node["children"][lastLeafList + 1:]: |
| 137 | item["lastLeafList"] = True; |
| 138 | else: |
| 139 | last["last"] = True |
| 140 | |
| 141 | |
Radek Krejci | 2713498 | 2017-11-10 15:42:00 +0100 | [diff] [blame] | 142 | def dataInfoNode(node, parent=None, recursion=False): |
Radek Krejci | a133960 | 2017-11-02 13:52:38 +0100 | [diff] [blame] | 143 | schema = node.schema() |
| 144 | casted = node.subtype() |
| 145 | |
| 146 | if node.dflt(): |
| 147 | return None |
| 148 | |
Radek Krejci | 6e772b2 | 2018-01-25 13:28:57 +0100 | [diff] [blame] | 149 | info = schemaInfoNode(schema); |
Radek Krejci | a133960 | 2017-11-02 13:52:38 +0100 | [diff] [blame] | 150 | |
| 151 | result = {} |
Radek Krejci | 30ce159 | 2018-03-01 14:44:14 +0100 | [diff] [blame] | 152 | if info["type"] == yang.LYS_LEAF or info["type"] == yang.LYS_LEAFLIST: |
Radek Krejci | a133960 | 2017-11-02 13:52:38 +0100 | [diff] [blame] | 153 | result["value"] = casted.value_str() |
Radek Krejci | 38b0a0b | 2018-08-03 10:17:03 +0200 | [diff] [blame] | 154 | if info["datatypebase"] == "identityref": |
| 155 | info["refmodule"] = make_schema_key(casted.value().ident().module()) |
Radek Krejci | a133960 | 2017-11-02 13:52:38 +0100 | [diff] [blame] | 156 | elif recursion: |
| 157 | result["children"] = [] |
| 158 | if node.child(): |
| 159 | for child in node.child().tree_for(): |
Radek Krejci | 2713498 | 2017-11-10 15:42:00 +0100 | [diff] [blame] | 160 | childNode = dataInfoNode(child, result, True) |
Radek Krejci | a133960 | 2017-11-02 13:52:38 +0100 | [diff] [blame] | 161 | if not childNode: |
| 162 | continue |
Radek Krejci | a133960 | 2017-11-02 13:52:38 +0100 | [diff] [blame] | 163 | result["children"].append(childNode) |
Radek Krejci | 30ce159 | 2018-03-01 14:44:14 +0100 | [diff] [blame] | 164 | # sort list instances |
| 165 | _sortChildren(result) |
Radek Krejci | 0f793fe | 2018-02-14 08:33:45 +0100 | [diff] [blame] | 166 | if info["type"] == yang.LYS_LIST: |
Radek Krejci | d82dfe2 | 2018-01-05 13:20:31 +0100 | [diff] [blame] | 167 | result["keys"] = [] |
| 168 | index = 0 |
| 169 | for key in schema.subtype().keys(): |
| 170 | if len(result["children"]) <= index: |
| 171 | break |
| 172 | if key.subtype().name() == result["children"][index]["info"]["name"]: |
| 173 | result["keys"].append(result["children"][index]["value"]) |
Radek Krejci | d82dfe2 | 2018-01-05 13:20:31 +0100 | [diff] [blame] | 174 | index = index + 1 |
Radek Krejci | a133960 | 2017-11-02 13:52:38 +0100 | [diff] [blame] | 175 | result["info"] = info |
| 176 | result["path"] = node.path() |
| 177 | |
| 178 | return result |
| 179 | |
| 180 | def dataInfoSubtree(data, path, recursion=False): |
| 181 | try: |
| 182 | node = data.find_path(path).data()[0] |
| 183 | except: |
| 184 | return(json.dumps({'success': False, 'error-msg': 'Invalid data path.'})) |
| 185 | |
| 186 | result = dataInfoNode(node) |
| 187 | if not result: |
| 188 | return(json.dumps({'success': False, 'error-msg': 'Path refers to a default node.'})) |
| 189 | |
| 190 | result["children"] = [] |
| 191 | if node.child(): |
| 192 | for child in node.child().tree_for(): |
Radek Krejci | 2713498 | 2017-11-10 15:42:00 +0100 | [diff] [blame] | 193 | childNode = dataInfoNode(child, result, recursion) |
Radek Krejci | a133960 | 2017-11-02 13:52:38 +0100 | [diff] [blame] | 194 | if not childNode: |
| 195 | continue |
Radek Krejci | a133960 | 2017-11-02 13:52:38 +0100 | [diff] [blame] | 196 | result["children"].append(childNode) |
Radek Krejci | 30ce159 | 2018-03-01 14:44:14 +0100 | [diff] [blame] | 197 | _sortChildren(result) |
Radek Krejci | a133960 | 2017-11-02 13:52:38 +0100 | [diff] [blame] | 198 | |
| 199 | return(json.dumps({'success': True, 'data': result})) |
| 200 | |
| 201 | |
| 202 | def dataInfoRoots(data, recursion=False): |
Radek Krejci | 2713498 | 2017-11-10 15:42:00 +0100 | [diff] [blame] | 203 | top = {} |
| 204 | top["children"] = [] |
Radek Krejci | a133960 | 2017-11-02 13:52:38 +0100 | [diff] [blame] | 205 | for root in data.tree_for(): |
Radek Krejci | 2713498 | 2017-11-10 15:42:00 +0100 | [diff] [blame] | 206 | rootNode = dataInfoNode(root, top, recursion) |
Radek Krejci | a133960 | 2017-11-02 13:52:38 +0100 | [diff] [blame] | 207 | if not rootNode: |
| 208 | continue |
Radek Krejci | d0fac3b | 2018-03-13 16:55:47 +0100 | [diff] [blame] | 209 | if not recursion: |
| 210 | rootNode['subtreeRoot'] = True |
Radek Krejci | 2713498 | 2017-11-10 15:42:00 +0100 | [diff] [blame] | 211 | top["children"].append(rootNode) |
Radek Krejci | 30ce159 | 2018-03-01 14:44:14 +0100 | [diff] [blame] | 212 | _sortChildren(top) |
Radek Krejci | 4d3896c | 2018-01-08 17:10:43 +0100 | [diff] [blame] | 213 | return(json.dumps({'success': True, 'data': top["children"]})) |