blob: 1ac552344c6d15b54fb557bdd1389512cdde19f8 [file] [log] [blame]
Radek Krejcid23f0df2017-08-31 16:34:49 +02001"""
2Manipulation with the YANG schemas.
3File: schemas.py
4Author: Radek Krejci <rkrejci@cesnet.cz>
5"""
6
Radek Krejci67c922d2017-09-21 13:56:41 +02007import json
8import os
9import errno
10import time
Radek Krejci5a1571a2018-02-16 13:45:53 +010011from subprocess import check_output
12from shutil import copy
Radek Krejci67c922d2017-09-21 13:56:41 +020013
14from liberouterapi import auth
Radek Krejcid23f0df2017-08-31 16:34:49 +020015from flask import request
Radek Krejci0f793fe2018-02-14 08:33:45 +010016import yang
Radek Krejcid23f0df2017-08-31 16:34:49 +020017
Radek Krejci2b9bbc22017-09-21 13:20:48 +020018from .inventory import INVENTORY, inventory_check
Radek Krejcid23f0df2017-08-31 16:34:49 +020019from .error import NetopeerException
20
Radek Krejci5a1571a2018-02-16 13:45:53 +010021__SCHEMAS_EMPTY = '{"timestamp":0, "schemas":{}}'
Radek Krejcid23f0df2017-08-31 16:34:49 +020022
Radek Krejci0f793fe2018-02-14 08:33:45 +010023
24def __schema_parse(path, format = yang.LYS_IN_UNKNOWN):
Radek Krejcid23f0df2017-08-31 16:34:49 +020025 try:
Radek Krejci0f793fe2018-02-14 08:33:45 +010026 ctx = yang.Context(os.path.dirname(path))
Radek Krejcid23f0df2017-08-31 16:34:49 +020027 except Exception as e:
28 raise NetopeerException(str(e))
29
Radek Krejci3cb753f2017-09-08 16:14:29 +020030 try:
Radek Krejci0f793fe2018-02-14 08:33:45 +010031 module = ctx.parse_path(path, yang.LYS_IN_YANG if format == yang.LYS_IN_UNKNOWN else format)
Radek Krejci3cb753f2017-09-08 16:14:29 +020032 except Exception as e:
Radek Krejci0f793fe2018-02-14 08:33:45 +010033 if format != yang.LYS_IN_UNKOWN:
Radek Krejci3cb753f2017-09-08 16:14:29 +020034 raise NetopeerException(str(e))
35 try:
36 module = ctx.parse_path(path, ly_LYS_IN_YIN)
37 except Exception as e:
38 raise NetopeerException(str(e))
39
40 return module
Radek Krejcid23f0df2017-08-31 16:34:49 +020041
Radek Krejci5a1571a2018-02-16 13:45:53 +010042
43def __schemas_init(path):
Radek Krejcid23f0df2017-08-31 16:34:49 +020044 schemas = json.loads(__SCHEMAS_EMPTY)
45 try:
Radek Krejci0f793fe2018-02-14 08:33:45 +010046 ctx = yang.Context()
Radek Krejcid23f0df2017-08-31 16:34:49 +020047 except Exception as e:
48 raise NetopeerException(str(e))
49
50 # initialize the list with libyang's internal modules
51 modules = ctx.get_module_iter()
52 for module in modules:
Radek Krejci5a1571a2018-02-16 13:45:53 +010053 name_norm = module.name() + '@' + module.rev().date() + '.yang'
54 schemas['schemas'][name_norm] = {'name':module.name(), 'revision':module.rev().date()}
55 try:
56 with open(os.path.join(path, name_norm), 'w') as schema_file:
57 schema_file.write(module.print_mem(yang.LYS_OUT_YANG, 0))
58 except:
59 pass
60 try:
61 nc_schemas_dir = check_output("pkg-config --variable=LNC2_SCHEMAS_DIR libnetconf2", shell = True).decode()
62 nc_schemas_dir = nc_schemas_dir[:len(nc_schemas_dir) - 1]
63 for file in os.listdir(nc_schemas_dir):
64 if file[-5:] == '.yang' or file[-4:] == '.yin':
65 try:
66 copy(os.path.join(nc_schemas_dir, file), path)
67 except:
68 pass
69 else:
70 continue
71 except:
72 pass
73
Radek Krejcid23f0df2017-08-31 16:34:49 +020074 return schemas
75
76
Radek Krejci3cb753f2017-09-08 16:14:29 +020077def __schemas_inv_load(path):
78 schemainv_path = os.path.join(path, 'schemas.json')
Radek Krejcid23f0df2017-08-31 16:34:49 +020079 try:
80 with open(schemainv_path, 'r') as schemas_file:
81 schemas = json.load(schemas_file)
82 except OSError as e:
83 if e.errno == errno.ENOENT:
Radek Krejci5a1571a2018-02-16 13:45:53 +010084 schemas = __schemas_init(path)
Radek Krejcid23f0df2017-08-31 16:34:49 +020085 else:
86 raise NetopeerException('Unable to use user\'s schemas inventory ' + schemainv_path + ' (' + str(e) + ').')
87 except ValueError:
Radek Krejci5a1571a2018-02-16 13:45:53 +010088 schemas = __schemas_init(path)
Radek Krejci3cb753f2017-09-08 16:14:29 +020089
90 return schemas
91
Radek Krejci5a1571a2018-02-16 13:45:53 +010092
Radek Krejci3cb753f2017-09-08 16:14:29 +020093def __schemas_inv_save(path, schemas):
94 schemainv_path = os.path.join(path, 'schemas.json')
Radek Krejcid23f0df2017-08-31 16:34:49 +020095
96 # update the timestamp
Radek Krejci5a1571a2018-02-16 13:45:53 +010097 schemas['timestamp'] = time.time()
Radek Krejci3cb753f2017-09-08 16:14:29 +020098
Radek Krejcid23f0df2017-08-31 16:34:49 +020099 #store the list
100 try:
101 with open(schemainv_path, 'w') as schema_file:
Radek Krejci5a1571a2018-02-16 13:45:53 +0100102 json.dump(schemas, schema_file, sort_keys = True)
Radek Krejcid23f0df2017-08-31 16:34:49 +0200103 except Exception:
104 pass
Radek Krejci3cb753f2017-09-08 16:14:29 +0200105
106 return schemas
107
Radek Krejci5a1571a2018-02-16 13:45:53 +0100108
Radek Krejci3cb753f2017-09-08 16:14:29 +0200109def __schemas_update(path):
110 # get schemas database
111 schemas = __schemas_inv_load(path)
112
113 # get the previous timestamp
Radek Krejci5a1571a2018-02-16 13:45:53 +0100114 timestamp = schemas['timestamp']
Radek Krejci3cb753f2017-09-08 16:14:29 +0200115
116 # check the current content of the storage
117 for file in os.listdir(path):
118 if file[-5:] == '.yang':
Radek Krejci0f793fe2018-02-14 08:33:45 +0100119 format = yang.LYS_IN_YANG
Radek Krejci3cb753f2017-09-08 16:14:29 +0200120 elif file[-4:] == '.yin':
Radek Krejci0f793fe2018-02-14 08:33:45 +0100121 format = yang.LYS_IN_YIN
Radek Krejci3cb753f2017-09-08 16:14:29 +0200122 else:
123 continue
124
125 schemapath = os.path.join(path, file);
126 if os.path.getmtime(schemapath) > timestamp:
127 # update the list
Radek Krejci3cb753f2017-09-08 16:14:29 +0200128 try:
129 module = __schema_parse(schemapath, format)
130 if module.rev_size():
Radek Krejci5a1571a2018-02-16 13:45:53 +0100131 name_norm = module.name() + '@' + module.rev().date() + '.yang'
132 schemas['schemas'][name_norm] = {'name': module.name(), 'revision': module.rev().date()}
Radek Krejci3cb753f2017-09-08 16:14:29 +0200133 else:
Radek Krejci5a1571a2018-02-16 13:45:53 +0100134 name_norm = module.name() + '.yang'
135 schemas['schemas'][name_norm] = {'name': module.name()}
136 if file != name_norm:
137 try:
138 with open(os.path.join(path, name_norm), 'w') as schema_file:
139 schema_file.write(module.print_mem(yang.LYS_OUT_YANG, 0))
140 except:
141 pass
142
143 try:
144 os.remove(schemapath)
145 except:
146 pass
147 except:
Radek Krejci3cb753f2017-09-08 16:14:29 +0200148 continue
149
150 #store the list
151 __schemas_inv_save(path, schemas)
Radek Krejcid23f0df2017-08-31 16:34:49 +0200152
153 # return the up-to-date list
Radek Krejci5a1571a2018-02-16 13:45:53 +0100154 return schemas['schemas']
155
Radek Krejcid23f0df2017-08-31 16:34:49 +0200156
Radek Krejcid23f0df2017-08-31 16:34:49 +0200157@auth.required()
158def schemas_list():
159 session = auth.lookup(request.headers.get('Authorization', None))
160 user = session['user']
Radek Krejci2b9bbc22017-09-21 13:20:48 +0200161 path = os.path.join(INVENTORY, user.username)
Radek Krejcid23f0df2017-08-31 16:34:49 +0200162
Radek Krejcib2feaac2017-09-21 13:16:54 +0200163 inventory_check(path)
Radek Krejcid23f0df2017-08-31 16:34:49 +0200164 schemas = __schemas_update(path)
165
Radek Krejci5a1571a2018-02-16 13:45:53 +0100166 return(json.dumps(schemas, sort_keys = True))
Radek Krejcid23f0df2017-08-31 16:34:49 +0200167
Radek Krejci6be087d2018-02-14 08:53:20 +0100168
169@auth.required()
170def schema_get():
171 session = auth.lookup(request.headers.get('Authorization', None))
172 user = session['user']
173 req = request.args.to_dict()
174 path = os.path.join(INVENTORY, user.username)
175
176 if not 'key' in req:
177 return(json.dumps({'success': False, 'error-msg': 'Missing schema key.'}))
178 key = req['key']
179
180 schemas = __schemas_inv_load(path)
Radek Krejci5a1571a2018-02-16 13:45:53 +0100181 if key in schemas['schemas']:
182 try:
183 with open(os.path.join(path, key), 'r') as schema_file:
184 data = schema_file.read()
Radek Krejci6be087d2018-02-14 08:53:20 +0100185 return(json.dumps({'success': True, 'data': data}))
Radek Krejci5a1571a2018-02-16 13:45:53 +0100186 except:
187 pass;
Radek Krejci6be087d2018-02-14 08:53:20 +0100188 return(json.dumps({'success': False, 'error-msg':'Schema ' + key + ' not found.'}))
189
190
Radek Krejcid23f0df2017-08-31 16:34:49 +0200191@auth.required()
192def schemas_add():
193 if 'schema' not in request.files:
194 raise NetopeerException('Missing schema file in upload request.')
195
196 session = auth.lookup(request.headers.get('Authorization', None))
197 user = session['user']
198 file = request.files['schema']
199
200 # store the file
Radek Krejci2b9bbc22017-09-21 13:20:48 +0200201 path = os.path.join(INVENTORY, user.username, file.filename)
Radek Krejcid23f0df2017-08-31 16:34:49 +0200202 file.save(path)
203
204 # parse file
205 try:
Radek Krejci3cb753f2017-09-08 16:14:29 +0200206 if file.filename[-5:] == '.yang':
Radek Krejci0f793fe2018-02-14 08:33:45 +0100207 format = yang.LYS_IN_YANG
Radek Krejci3cb753f2017-09-08 16:14:29 +0200208 elif file.filename[-4:] == '.yin':
Radek Krejci0f793fe2018-02-14 08:33:45 +0100209 format = yang.LYS_IN_YIN
Radek Krejci3cb753f2017-09-08 16:14:29 +0200210 else:
Radek Krejci0f793fe2018-02-14 08:33:45 +0100211 format = yang.LYS_IN_UNKNOWN
Radek Krejci3cb753f2017-09-08 16:14:29 +0200212 module = __schema_parse(path, format)
Radek Krejci5a1571a2018-02-16 13:45:53 +0100213
214 # normalize file name to allow removing without remembering schema path
215 if module.rev_size():
216 name_norm = module.name() + '@' + module.rev().date() + '.yang'
217 else:
218 name_norm = module.name() + '.yang'
219 if file.filename != name_norm:
220 with open(os.path.join(INVENTORY, user.username, name_norm), 'w') as schema_file:
221 schema_file.write(module.print_mem(yang.LYS_OUT_YANG, 0))
222 try:
223 os.remove(path)
224 except:
225 pass
Radek Krejcid23f0df2017-08-31 16:34:49 +0200226 except Exception:
Radek Krejci5a1571a2018-02-16 13:45:53 +0100227 try:
228 os.remove(path)
229 except:
230 pass
Radek Krejcid23f0df2017-08-31 16:34:49 +0200231 return(json.dumps({'success': False}))
232
233 return(json.dumps({'success': True}))
Radek Krejci3cb753f2017-09-08 16:14:29 +0200234
Radek Krejci5a1571a2018-02-16 13:45:53 +0100235
Radek Krejci3cb753f2017-09-08 16:14:29 +0200236@auth.required()
237def schemas_rm():
238 session = auth.lookup(request.headers.get('Authorization', None))
239 user = session['user']
Radek Krejci2b9bbc22017-09-21 13:20:48 +0200240 path = os.path.join(INVENTORY, user.username)
Radek Krejci3cb753f2017-09-08 16:14:29 +0200241
Radek Krejci6be087d2018-02-14 08:53:20 +0100242 key = request.get_json()
243 if not key:
Radek Krejci3cb753f2017-09-08 16:14:29 +0200244 raise NetopeerException('Invalid schema remove request.')
245
246 schemas = __schemas_inv_load(path)
Radek Krejci5a1571a2018-02-16 13:45:53 +0100247 try:
Radek Krejcia70c9802018-02-20 15:48:33 +0100248 schemas['schemas'].pop(key)
Radek Krejci5a1571a2018-02-16 13:45:53 +0100249 except KeyError:
Radek Krejci3cb753f2017-09-08 16:14:29 +0200250 # schema not in inventory
251 return (json.dumps({'success': False}))
252
253 # update the inventory database
254 __schemas_inv_save(path, schemas)
255
256 # remove the schema file
Radek Krejci5a1571a2018-02-16 13:45:53 +0100257 try:
258 os.remove(os.path.join(path, key))
259 except Exception as e:
260 print(e)
Radek Krejci3cb753f2017-09-08 16:14:29 +0200261
Radek Krejci5a1571a2018-02-16 13:45:53 +0100262 # TODO: resolve dependencies ?
Radek Krejci3cb753f2017-09-08 16:14:29 +0200263
264 return(json.dumps({'success': True}))