blob: 7b12e9d8792672e1eb399e46789a441787a6190a [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
11
12from liberouterapi import auth
Radek Krejcid23f0df2017-08-31 16:34:49 +020013from flask import request
Radek Krejci0f793fe2018-02-14 08:33:45 +010014import yang
Radek Krejcid23f0df2017-08-31 16:34:49 +020015
Radek Krejci2b9bbc22017-09-21 13:20:48 +020016from .inventory import INVENTORY, inventory_check
Radek Krejcid23f0df2017-08-31 16:34:49 +020017from .error import NetopeerException
18
Radek Krejcid23f0df2017-08-31 16:34:49 +020019__SCHEMAS_EMPTY = '{"schemas":{"timestamp":0,"schema":[]}}'
20
Radek Krejci0f793fe2018-02-14 08:33:45 +010021
22def __schema_parse(path, format = yang.LYS_IN_UNKNOWN):
Radek Krejcid23f0df2017-08-31 16:34:49 +020023 try:
Radek Krejci0f793fe2018-02-14 08:33:45 +010024 ctx = yang.Context(os.path.dirname(path))
Radek Krejcid23f0df2017-08-31 16:34:49 +020025 except Exception as e:
26 raise NetopeerException(str(e))
27
Radek Krejci3cb753f2017-09-08 16:14:29 +020028 try:
Radek Krejci0f793fe2018-02-14 08:33:45 +010029 module = ctx.parse_path(path, yang.LYS_IN_YANG if format == yang.LYS_IN_UNKNOWN else format)
Radek Krejci3cb753f2017-09-08 16:14:29 +020030 except Exception as e:
Radek Krejci0f793fe2018-02-14 08:33:45 +010031 if format != yang.LYS_IN_UNKOWN:
Radek Krejci3cb753f2017-09-08 16:14:29 +020032 raise NetopeerException(str(e))
33 try:
34 module = ctx.parse_path(path, ly_LYS_IN_YIN)
35 except Exception as e:
36 raise NetopeerException(str(e))
37
38 return module
Radek Krejcid23f0df2017-08-31 16:34:49 +020039
40
41def __schemas_init():
42 schemas = json.loads(__SCHEMAS_EMPTY)
43 try:
Radek Krejci0f793fe2018-02-14 08:33:45 +010044 ctx = yang.Context()
Radek Krejcid23f0df2017-08-31 16:34:49 +020045 except Exception as e:
46 raise NetopeerException(str(e))
47
48 # initialize the list with libyang's internal modules
49 modules = ctx.get_module_iter()
50 for module in modules:
Radek Krejci6be087d2018-02-14 08:53:20 +010051 schemas['schemas']['schema'].append({'key':module.name() + '@' + module.rev().date(), 'name':module.name(), 'revision':module.rev().date()})
Radek Krejcid23f0df2017-08-31 16:34:49 +020052 return schemas
53
54
Radek Krejci3cb753f2017-09-08 16:14:29 +020055def __schemas_inv_load(path):
56 schemainv_path = os.path.join(path, 'schemas.json')
Radek Krejcid23f0df2017-08-31 16:34:49 +020057 try:
58 with open(schemainv_path, 'r') as schemas_file:
59 schemas = json.load(schemas_file)
60 except OSError as e:
61 if e.errno == errno.ENOENT:
62 schemas = __schemas_init()
63 else:
64 raise NetopeerException('Unable to use user\'s schemas inventory ' + schemainv_path + ' (' + str(e) + ').')
65 except ValueError:
66 schemas = __schemas_init()
Radek Krejci3cb753f2017-09-08 16:14:29 +020067
68 return schemas
69
70def __schemas_inv_save(path, schemas):
71 schemainv_path = os.path.join(path, 'schemas.json')
Radek Krejcid23f0df2017-08-31 16:34:49 +020072
73 # update the timestamp
74 schemas['schemas']['timestamp'] = time.time()
Radek Krejci3cb753f2017-09-08 16:14:29 +020075
Radek Krejcid23f0df2017-08-31 16:34:49 +020076 #store the list
77 try:
78 with open(schemainv_path, 'w') as schema_file:
79 json.dump(schemas, schema_file)
80 except Exception:
81 pass
Radek Krejci3cb753f2017-09-08 16:14:29 +020082
83 return schemas
84
85def __schemas_update(path):
86 # get schemas database
87 schemas = __schemas_inv_load(path)
88
89 # get the previous timestamp
90 timestamp = schemas['schemas']['timestamp']
91
92 # check the current content of the storage
93 for file in os.listdir(path):
94 if file[-5:] == '.yang':
Radek Krejci0f793fe2018-02-14 08:33:45 +010095 format = yang.LYS_IN_YANG
Radek Krejci3cb753f2017-09-08 16:14:29 +020096 elif file[-4:] == '.yin':
Radek Krejci0f793fe2018-02-14 08:33:45 +010097 format = yang.LYS_IN_YIN
Radek Krejci3cb753f2017-09-08 16:14:29 +020098 else:
99 continue
100
101 schemapath = os.path.join(path, file);
102 if os.path.getmtime(schemapath) > timestamp:
103 # update the list
Radek Krejci3cb753f2017-09-08 16:14:29 +0200104 try:
105 module = __schema_parse(schemapath, format)
106 if module.rev_size():
Radek Krejci6be087d2018-02-14 08:53:20 +0100107 schemas['schemas']['schema'].append({'key':module.name() + '@' + module.rev().date(),
108 'name':module.name(),
109 'revision':module.rev().date(),
110 'file':os.path.basename(schemapath)})
Radek Krejci3cb753f2017-09-08 16:14:29 +0200111 else:
Radek Krejci6be087d2018-02-14 08:53:20 +0100112 schemas['schemas']['schema'].append({'key':module.name() + '@',
113 'name':module.name(),
114 'file':os.path.basename(schemapath)})
Radek Krejci3cb753f2017-09-08 16:14:29 +0200115 except Exception as e:
116 continue
117
118 #store the list
119 __schemas_inv_save(path, schemas)
Radek Krejcid23f0df2017-08-31 16:34:49 +0200120
121 # return the up-to-date list
122 return schemas['schemas']['schema']
123
Radek Krejcid23f0df2017-08-31 16:34:49 +0200124@auth.required()
125def schemas_list():
126 session = auth.lookup(request.headers.get('Authorization', None))
127 user = session['user']
Radek Krejci2b9bbc22017-09-21 13:20:48 +0200128 path = os.path.join(INVENTORY, user.username)
Radek Krejcid23f0df2017-08-31 16:34:49 +0200129
Radek Krejcib2feaac2017-09-21 13:16:54 +0200130 inventory_check(path)
Radek Krejcid23f0df2017-08-31 16:34:49 +0200131 schemas = __schemas_update(path)
132
133 return(json.dumps(schemas))
134
Radek Krejci6be087d2018-02-14 08:53:20 +0100135
136@auth.required()
137def schema_get():
138 session = auth.lookup(request.headers.get('Authorization', None))
139 user = session['user']
140 req = request.args.to_dict()
141 path = os.path.join(INVENTORY, user.username)
142
143 if not 'key' in req:
144 return(json.dumps({'success': False, 'error-msg': 'Missing schema key.'}))
145 key = req['key']
146
147 schemas = __schemas_inv_load(path)
148 for i in range(len(schemas['schemas']['schema'])):
149 schema = schemas['schemas']['schema'][i]
150 if schema['key'] == key:
151 data = ""
152 if 'file' in schema:
153 with open(os.path.join(path, schema['file']), 'r') as schema_file:
154 data = schema_file.read()
155 else:
156 ctx = yang.Context()
157 data = ctx.get_module(schema['name']).print_mem(yang.LYS_OUT_YANG, 0)
158 return(json.dumps({'success': True, 'data': data}))
159
160 return(json.dumps({'success': False, 'error-msg':'Schema ' + key + ' not found.'}))
161
162
Radek Krejcid23f0df2017-08-31 16:34:49 +0200163@auth.required()
164def schemas_add():
165 if 'schema' not in request.files:
166 raise NetopeerException('Missing schema file in upload request.')
167
168 session = auth.lookup(request.headers.get('Authorization', None))
169 user = session['user']
170 file = request.files['schema']
171
172 # store the file
Radek Krejci2b9bbc22017-09-21 13:20:48 +0200173 path = os.path.join(INVENTORY, user.username, file.filename)
Radek Krejcid23f0df2017-08-31 16:34:49 +0200174 file.save(path)
175
176 # parse file
177 try:
Radek Krejci3cb753f2017-09-08 16:14:29 +0200178 if file.filename[-5:] == '.yang':
Radek Krejci0f793fe2018-02-14 08:33:45 +0100179 format = yang.LYS_IN_YANG
Radek Krejci3cb753f2017-09-08 16:14:29 +0200180 elif file.filename[-4:] == '.yin':
Radek Krejci0f793fe2018-02-14 08:33:45 +0100181 format = yang.LYS_IN_YIN
Radek Krejci3cb753f2017-09-08 16:14:29 +0200182 else:
Radek Krejci0f793fe2018-02-14 08:33:45 +0100183 format = yang.LYS_IN_UNKNOWN
Radek Krejci3cb753f2017-09-08 16:14:29 +0200184 module = __schema_parse(path, format)
185 # TODO: normalize file name to allow removing without remembering schema path
Radek Krejcid23f0df2017-08-31 16:34:49 +0200186 except Exception:
187 os.remove(path)
188 return(json.dumps({'success': False}))
189
190 return(json.dumps({'success': True}))
Radek Krejci3cb753f2017-09-08 16:14:29 +0200191
192@auth.required()
193def schemas_rm():
194 session = auth.lookup(request.headers.get('Authorization', None))
195 user = session['user']
Radek Krejci2b9bbc22017-09-21 13:20:48 +0200196 path = os.path.join(INVENTORY, user.username)
Radek Krejci3cb753f2017-09-08 16:14:29 +0200197
Radek Krejci6be087d2018-02-14 08:53:20 +0100198 key = request.get_json()
199 if not key:
Radek Krejci3cb753f2017-09-08 16:14:29 +0200200 raise NetopeerException('Invalid schema remove request.')
201
202 schemas = __schemas_inv_load(path)
203 for i in range(len(schemas['schemas']['schema'])):
204 schema = schemas['schemas']['schema'][i]
Radek Krejci6be087d2018-02-14 08:53:20 +0100205 if schema['key'] == key:
206 schemas['schemas']['schema'].pop(i)
207 break;
Radek Krejci3cb753f2017-09-08 16:14:29 +0200208 else:
Radek Krejci6be087d2018-02-14 08:53:20 +0100209 schema = None;
Radek Krejci3cb753f2017-09-08 16:14:29 +0200210
211 if not schema:
212 # schema not in inventory
213 return (json.dumps({'success': False}))
214
215 # update the inventory database
216 __schemas_inv_save(path, schemas)
217
218 # remove the schema file
219 if 'revision' in schema:
220 path = os.path.join(path, schema['name'] + '@' + schema['revision'] + '.yang')
221 else:
222 path = os.path.join(path, schema['name'] + '.yang')
223 os.remove(path)
224
225 # TODO: resolve dependencies
226
227 return(json.dumps({'success': True}))