blob: e4d969e81ea7a233030d669f876b0c8beab829d6 [file] [log] [blame]
Radek Krejcicbbb1972017-09-21 13:25:19 +02001"""
2NETCONF connections
3File: connections.py
4Author: Radek Krejci <rkrejci@cesnet.cz>
5"""
6
Radek Krejci67c922d2017-09-21 13:56:41 +02007import json
8import os
Radek Krejci14b7aac2018-07-02 14:33:26 +02009import logging
Radek Krejci67c922d2017-09-21 13:56:41 +020010
Radek Krejci39cce672018-06-15 16:15:18 +020011from liberouterapi import socketio, auth
Radek Krejcicbbb1972017-09-21 13:25:19 +020012from flask import request
Radek Krejci39cce672018-06-15 16:15:18 +020013from flask_socketio import emit
14from eventlet import event
15from eventlet.timeout import Timeout
Radek Krejci0f793fe2018-02-14 08:33:45 +010016import yang
Radek Krejcicbbb1972017-09-21 13:25:19 +020017import netconf2 as nc
Radek Krejcicbbb1972017-09-21 13:25:19 +020018
19from .inventory import INVENTORY
Radek Krejcia7478322018-06-19 13:09:52 +020020from .devices import devices_get, devices_replace
Radek Krejcicbbb1972017-09-21 13:25:19 +020021from .error import NetopeerException
Radek Krejcice4d8382018-08-23 13:03:40 +020022from .schemas import schemas_update
Radek Krejcia1339602017-11-02 13:52:38 +010023from .data import *
Radek Krejcicbbb1972017-09-21 13:25:19 +020024
Radek Krejci14b7aac2018-07-02 14:33:26 +020025log = logging.getLogger(__name__)
26
Radek Krejcicbbb1972017-09-21 13:25:19 +020027sessions = {}
Radek Krejci14b7aac2018-07-02 14:33:26 +020028connect_sio_data = {}
Radek Krejci39cce672018-06-15 16:15:18 +020029
30
Radek Krejci14b7aac2018-07-02 14:33:26 +020031def connect_sio_send(data):
Radek Krejci39cce672018-06-15 16:15:18 +020032 try:
Radek Krejci14b7aac2018-07-02 14:33:26 +020033 e = connect_sio_data[data['id']]
34 e.send(data)
Radek Krejci39cce672018-06-15 16:15:18 +020035 except KeyError:
36 pass
37
38
Radek Krejci14b7aac2018-07-02 14:33:26 +020039@socketio.on('device_auth_password')
Radek Krejci39cce672018-06-15 16:15:18 +020040@socketio.on('hostcheck_result')
41def hostkey_check_answer(data):
Radek Krejci14b7aac2018-07-02 14:33:26 +020042 connect_sio_send(data)
Radek Krejci39cce672018-06-15 16:15:18 +020043
Radek Krejcicbbb1972017-09-21 13:25:19 +020044
Radek Krejcie20e4d82017-11-08 14:18:05 +010045def hostkey_check(hostname, state, keytype, hexa, priv):
Radek Krejcia7478322018-06-19 13:09:52 +020046 if 'fingerprint' in priv['device']:
47 # check according to the stored fingerprint from previous connection
48 if hexa == priv['device']['fingerprint']:
49 return True
50 elif state != 2:
Radek Krejci14b7aac2018-07-02 14:33:26 +020051 log.error("Incorrect host key state")
Radek Krejcia7478322018-06-19 13:09:52 +020052 state = 2
53
54 # ask frontend/user for hostkey check
55 params = {'id': priv['session']['session_id'], 'hostname' : hostname, 'state' : state, 'keytype' : keytype, 'hexa' : hexa}
Radek Krejci14b7aac2018-07-02 14:33:26 +020056 socketio.emit('hostcheck', params, callback = connect_sio_send)
Radek Krejci39cce672018-06-15 16:15:18 +020057
Radek Krejci14b7aac2018-07-02 14:33:26 +020058 result = False
Radek Krejci39cce672018-06-15 16:15:18 +020059 timeout = Timeout(30)
60 try:
Radek Krejcia7478322018-06-19 13:09:52 +020061 # wait for response from the frontend
Radek Krejci14b7aac2018-07-02 14:33:26 +020062 e = connect_sio_data[priv['session']['session_id']] = event.Event()
63 data = e.wait()
64 result = data['result']
Radek Krejci39cce672018-06-15 16:15:18 +020065 except Timeout:
Radek Krejcia7478322018-06-19 13:09:52 +020066 # no response received within the timeout
Radek Krejci14b7aac2018-07-02 14:33:26 +020067 log.info("socketio: hostcheck timeout.")
68 except KeyError:
69 # invalid response
70 log.error("socketio: invalid hostcheck_result received.")
Radek Krejci39cce672018-06-15 16:15:18 +020071 finally:
Radek Krejcia7478322018-06-19 13:09:52 +020072 # we have the response
Radek Krejci14b7aac2018-07-02 14:33:26 +020073 connect_sio_data.pop(priv['session']['session_id'], None)
Radek Krejci39cce672018-06-15 16:15:18 +020074 timeout.cancel()
75
Radek Krejcia7478322018-06-19 13:09:52 +020076 if result:
77 # store confirmed fingerprint for future connections
78 priv['device']['fingerprint'] = hexa;
79 devices_replace(priv['device']['id'], priv['session']['user'].username, priv['device'])
80
Radek Krejci39cce672018-06-15 16:15:18 +020081 return result
82
Radek Krejcie20e4d82017-11-08 14:18:05 +010083
Radek Krejci14b7aac2018-07-02 14:33:26 +020084def auth_common(session_id):
85 result = None
86 timeout = Timeout(60)
87 try:
88 # wait for response from the frontend
89 e = connect_sio_data[session_id] = event.Event()
90 data = e.wait()
91 print(data)
92 result = data['password']
93 except Timeout:
94 # no response received within the timeout
95 log.info("socketio: auth request timeout.")
96 except KeyError:
97 # no password
98 log.info("socketio: invalid credential data received.")
99 finally:
100 # we have the response
101 connect_sio_data.pop(session_id, None)
102 timeout.cancel()
103
104 return result
105
106
107def auth_password(username, hostname, priv):
108 print("auth_password callback")
109 socketio.emit('device_auth', {'id': priv, 'type': 'Password Authentication', 'msg': username + '@' + hostname}, callback = connect_sio_send)
110 return auth_common(priv)
111
112
113def auth_interactive(name, instruction, prompt, priv):
114 print("auth_interactive callback")
115 socketio.emit('device_auth', {'id': priv, 'type': name, 'msg': instruction, 'prompt': prompt}, callback = connect_sio_send)
116 return auth_common(priv)
117
118
Radek Krejcicbbb1972017-09-21 13:25:19 +0200119@auth.required()
120def connect():
Jakub Man47834ec2018-06-21 15:04:35 +0200121 session = auth.lookup(request.headers.get('lgui-Authorization', None))
Radek Krejcicbbb1972017-09-21 13:25:19 +0200122 user = session['user']
123 path = os.path.join(INVENTORY, user.username)
124
Radek Krejcia6c8b412017-10-17 16:59:38 +0200125 data = request.get_json()
126 if 'id' in data:
127 # stored device
128 device = devices_get(data['id'], user.username)
129 elif 'device' in data:
130 # one-time connect, the device is specified in request
131 device = data['device']
132 else:
133 raise NetopeerException('Invalid connection request.')
134
Radek Krejcicbbb1972017-09-21 13:25:19 +0200135 if not device:
136 raise NetopeerException('Unknown device to connect to request.')
137
138 nc.setSearchpath(path)
139
Radek Krejci14b7aac2018-07-02 14:33:26 +0200140 if 'password' in device:
141 ssh = nc.SSH(device['username'], password = device['password'])
142 else:
143 ssh = nc.SSH(device['username'])
144 ssh.setAuthPasswordClb(auth_password, session['session_id'])
145 ssh.setAuthInteractiveClb(auth_interactive, session['session_id'])
146
Radek Krejcia7478322018-06-19 13:09:52 +0200147 ssh.setAuthHostkeyCheckClb(hostkey_check, {'session': session, 'device' : device})
Radek Krejcicbbb1972017-09-21 13:25:19 +0200148 try:
Radek Krejci39cce672018-06-15 16:15:18 +0200149 ncs = nc.Session(device['hostname'], device['port'], ssh)
Radek Krejcicbbb1972017-09-21 13:25:19 +0200150 except Exception as e:
151 return(json.dumps({'success': False, 'error-msg': str(e)}))
152
153 if not user.username in sessions:
154 sessions[user.username] = {}
155
156 # use key (as hostname:port:session-id) to store the created NETCONF session
Radek Krejci39cce672018-06-15 16:15:18 +0200157 key = ncs.host + ":" + str(ncs.port) + ":" + ncs.id
Radek Krejci4d3896c2018-01-08 17:10:43 +0100158 sessions[user.username][key] = {}
Radek Krejci39cce672018-06-15 16:15:18 +0200159 sessions[user.username][key]['session'] = ncs
Radek Krejcicbbb1972017-09-21 13:25:19 +0200160
Radek Krejcice4d8382018-08-23 13:03:40 +0200161 # update inventory's list of schemas
162 schemas_update(path)
163
Radek Krejcicbbb1972017-09-21 13:25:19 +0200164 return(json.dumps({'success': True, 'session-key': key}))
165
166
167@auth.required()
168def session_get_capabilities():
Jakub Man47834ec2018-06-21 15:04:35 +0200169 session = auth.lookup(request.headers.get('lgui-Authorization', None))
Radek Krejcicbbb1972017-09-21 13:25:19 +0200170 user = session['user']
171 req = request.args.to_dict()
172
173 if not 'key' in req:
174 return(json.dumps({'success': False, 'error-msg': 'Missing session key.'}))
175
176 if not user.username in sessions:
177 sessions[user.username] = {}
Jakub Man47834ec2018-06-21 15:04:35 +0200178
Radek Krejcicbbb1972017-09-21 13:25:19 +0200179 key = req['key']
180 if not key in sessions[user.username]:
181 return(json.dumps({'success': False, 'error-msg': 'Invalid session key.'}))
182
183 cpblts = []
Radek Krejci4d3896c2018-01-08 17:10:43 +0100184 for c in sessions[user.username][key]['session'].capabilities:
Radek Krejcicbbb1972017-09-21 13:25:19 +0200185 cpblts.append(c)
186
187 return(json.dumps({'success': True, 'capabilities': cpblts}))
188
189@auth.required()
Radek Krejci2e578562017-10-17 11:11:13 +0200190def session_get():
Jakub Man47834ec2018-06-21 15:04:35 +0200191 session = auth.lookup(request.headers.get('lgui-Authorization', None))
Radek Krejci2e578562017-10-17 11:11:13 +0200192 user = session['user']
193 req = request.args.to_dict()
194
195 if not 'key' in req:
196 return(json.dumps({'success': False, 'error-msg': 'Missing session key.'}))
Radek Krejcia1339602017-11-02 13:52:38 +0100197 if not 'recursive' in req:
198 return(json.dumps({'success': False, 'error-msg': 'Missing recursive flag.'}))
Radek Krejci2e578562017-10-17 11:11:13 +0200199
200 if not user.username in sessions:
201 sessions[user.username] = {}
202
203 key = req['key']
204 if not key in sessions[user.username]:
205 return(json.dumps({'success': False, 'error-msg': 'Invalid session key.'}))
206
Radek Krejci2e578562017-10-17 11:11:13 +0200207 try:
Radek Krejci4d3896c2018-01-08 17:10:43 +0100208 sessions[user.username][key]['data'] = sessions[user.username][key]['session'].rpcGet()
Radek Krejci010475d2018-03-08 13:14:19 +0100209 except ConnectionError as e:
210 reply = {'success': False, 'error': [{'msg': str(e)}]}
211 del sessions[user.username][key]
212 return(json.dumps(reply))
Radek Krejci2e578562017-10-17 11:11:13 +0200213 except nc.ReplyError as e:
214 reply = {'success': False, 'error': []}
215 for err in e.args[0]:
216 reply['error'].append(json.loads(str(err)))
217 return(json.dumps(reply))
218
Radek Krejcia1339602017-11-02 13:52:38 +0100219 if not 'path' in req:
Radek Krejci4d3896c2018-01-08 17:10:43 +0100220 return(dataInfoRoots(sessions[user.username][key]['data'], True if req['recursive'] == 'true' else False))
Radek Krejcia1339602017-11-02 13:52:38 +0100221 else:
Radek Krejci4d3896c2018-01-08 17:10:43 +0100222 return(dataInfoSubtree(sessions[user.username][key]['data'], req['path'], True if req['recursive'] == 'true' else False))
223
224
Radek Krejci6e772b22018-01-25 13:28:57 +0100225def _checkvalue(session, req, schema):
226 user = session['user'];
Radek Krejci4d3896c2018-01-08 17:10:43 +0100227
228 if not 'key' in req:
229 return(json.dumps({'success': False, 'error-msg': 'Missing session key.'}))
230 if not 'path' in req:
231 return(json.dumps({'success': False, 'error-msg': 'Missing path to validate value.'}))
232 if not 'value' in req:
233 return(json.dumps({'success': False, 'error-msg': 'Missing value to validate.'}))
234
Radek Krejci6e772b22018-01-25 13:28:57 +0100235 key = req['key']
236 if not key in sessions[user.username]:
237 return(json.dumps({'success': False, 'error-msg': 'Invalid session key.'}))
238
Radek Krejcicf719cb2018-02-15 16:47:32 +0100239 ctx = sessions[user.username][key]['session'].context;
Radek Krejci6e772b22018-01-25 13:28:57 +0100240 if schema:
Radek Krejcicf719cb2018-02-15 16:47:32 +0100241 search = ctx.find_path(req['path'])
Radek Krejci6e772b22018-01-25 13:28:57 +0100242 else:
243 search = sessions[user.username][key]['data'].find_path(req['path'])
244
245 if search.number() != 1:
246 return(json.dumps({'success': False, 'error-msg': 'Invalid data path.'}))
247
248 if schema:
249 node = search.schema()[0]
250 else:
251 node = search.data()[0]
252
253 if node.validate_value(req['value']):
Radek Krejcicf719cb2018-02-15 16:47:32 +0100254 errors = yang.get_ly_errors(ctx)
255 if errors.size():
256 return(json.dumps({'success': False, 'error-msg': errors[errors.size() - 1].errmsg()}))
257 else:
258 return(json.dumps({'success': False, 'error-msg': 'unknown error'}))
Radek Krejci6e772b22018-01-25 13:28:57 +0100259
260 return(json.dumps({'success': True}))
261
262@auth.required()
263def data_checkvalue():
Jakub Man47834ec2018-06-21 15:04:35 +0200264 session = auth.lookup(request.headers.get('lgui-Authorization', None))
Radek Krejci6e772b22018-01-25 13:28:57 +0100265 req = request.args.to_dict()
266
267 return _checkvalue(session, req, False)
268
269
270@auth.required()
271def schema_checkvalue():
Jakub Man47834ec2018-06-21 15:04:35 +0200272 session = auth.lookup(request.headers.get('lgui-Authorization', None))
Radek Krejci6e772b22018-01-25 13:28:57 +0100273 req = request.args.to_dict()
274
275 return _checkvalue(session, req, True)
276
277
278@auth.required()
Radek Krejcid0ce4cf2018-02-09 14:44:34 +0100279def schema_values():
Jakub Man47834ec2018-06-21 15:04:35 +0200280 session = auth.lookup(request.headers.get('lgui-Authorization', None))
Radek Krejcid0ce4cf2018-02-09 14:44:34 +0100281 user = session['user']
282 req = request.args.to_dict()
283
284 if not 'key' in req:
285 return(json.dumps({'success': False, 'error-msg': 'Missing session key.'}))
286 if not 'path' in req:
287 return(json.dumps({'success': False, 'error-msg': 'Missing path to validate value.'}))
288
289 key = req['key']
290 if not key in sessions[user.username]:
291 return(json.dumps({'success': False, 'error-msg': 'Invalid session key.'}))
292
293 search = sessions[user.username][key]['session'].context.find_path(req['path'])
294 if search.number() != 1:
295 return(json.dumps({'success': False, 'error-msg': 'Invalid data path.'}))
296 schema = search.schema()[0]
297
Radek Krejci0f793fe2018-02-14 08:33:45 +0100298 if schema.nodetype() != yang.LYS_LEAF and schema.nodetype != yang.LYS_LEAFLIST:
Radek Krejcid0ce4cf2018-02-09 14:44:34 +0100299 result = None
300 else:
301 result = typeValues(schema.subtype().type(), [])
302 return(json.dumps({'success': True, 'data': result}))
303
304
305@auth.required()
Radek Krejci6e772b22018-01-25 13:28:57 +0100306def schema_info():
Jakub Man47834ec2018-06-21 15:04:35 +0200307 session = auth.lookup(request.headers.get('lgui-Authorization', None))
Radek Krejci6e772b22018-01-25 13:28:57 +0100308 user = session['user']
309 req = request.args.to_dict()
310
311 if not 'key' in req:
312 return(json.dumps({'success': False, 'error-msg': 'Missing session key.'}))
313 if not 'path' in req:
314 return(json.dumps({'success': False, 'error-msg': 'Missing path to validate value.'}))
Radek Krejci4d3896c2018-01-08 17:10:43 +0100315
316 key = req['key']
317 if not key in sessions[user.username]:
318 return(json.dumps({'success': False, 'error-msg': 'Invalid session key.'}))
319
Radek Krejci6e772b22018-01-25 13:28:57 +0100320 if req['path'] == '/':
Radek Krejcif71867f2018-01-30 13:28:28 +0100321 node = None
Radek Krejci6e772b22018-01-25 13:28:57 +0100322 else:
323 search = sessions[user.username][key]['session'].context.find_path(req['path'])
324 if search.number() != 1:
325 return(json.dumps({'success': False, 'error-msg': 'Invalid data path.'}))
326 node = search.schema()[0]
Radek Krejci4d3896c2018-01-08 17:10:43 +0100327
Radek Krejci6e772b22018-01-25 13:28:57 +0100328 result = [];
329 if 'relative' in req:
330 if req['relative'] == 'children':
Radek Krejcif71867f2018-01-30 13:28:28 +0100331 if node:
332 instantiables = node.child_instantiables(0)
333 else:
334 # top level
335 instantiables = sessions[user.username][key]['session'].context.data_instantiables(0)
Radek Krejci6e772b22018-01-25 13:28:57 +0100336 elif req['relative'] == 'siblings':
337 if node.parent():
Radek Krejcif71867f2018-01-30 13:28:28 +0100338 instantiables = node.parent().child_instantiables(0)
339 else:
340 # top level
341 instantiables = sessions[user.username][key]['session'].context.data_instantiables(0)
Radek Krejci6e772b22018-01-25 13:28:57 +0100342 else:
343 return(json.dumps({'success': False, 'error-msg': 'Invalid relative parameter.'}))
Radek Krejcif71867f2018-01-30 13:28:28 +0100344
345 for child in instantiables:
Radek Krejci0f793fe2018-02-14 08:33:45 +0100346 if child.flags() & yang.LYS_CONFIG_R:
Radek Krejcif71867f2018-01-30 13:28:28 +0100347 # ignore status nodes
348 continue
Radek Krejci0f793fe2018-02-14 08:33:45 +0100349 if child.nodetype() & (yang.LYS_RPC | yang.LYS_NOTIF | yang.LYS_ACTION):
Radek Krejcif71867f2018-01-30 13:28:28 +0100350 # ignore RPCs, Notifications and Actions
351 continue
352 result.append(schemaInfoNode(child))
Radek Krejci6e772b22018-01-25 13:28:57 +0100353 else:
354 result.append(schemaInfoNode(node))
Radek Krejci4d3896c2018-01-08 17:10:43 +0100355
Radek Krejci6e772b22018-01-25 13:28:57 +0100356 return(json.dumps({'success': True, 'data': result}))
Radek Krejci2e578562017-10-17 11:11:13 +0200357
358
Radek Krejcif041d652018-02-06 10:21:25 +0100359def _create_child(ctx, parent, child_def):
Radek Krejciad45e572018-02-21 10:54:54 +0100360 at = child_def['info']['module'].find('@')
361 if at == -1:
362 module = ctx.get_module(child_def['info']['module'])
363 else:
364 module = ctx.get_module(child_def['info']['module'][:at], child_def['info']['module'][at + 1:])
Radek Krejcif041d652018-02-06 10:21:25 +0100365 # print('child: ' + json.dumps(child_def))
Radek Krejci7a244f02018-02-21 09:46:18 +0100366 # print('parent: ' + parent.schema().name())
367 # print('module: ' + module.name())
368 # print('name: ' + child_def['info']['name'])
369 if child_def['info']['type'] == 4 :
370 # print('value: ' + str(child_def['value']))
Radek Krejci0f793fe2018-02-14 08:33:45 +0100371 yang.Data_Node(parent, module, child_def['info']['name'], child_def['value'])
Radek Krejci7a244f02018-02-21 09:46:18 +0100372 elif child_def['info']['type'] == 8:
373 # print('value: ' + child_def['value'][0])
374 yang.Data_Node(parent, module, child_def['info']['name'], child_def['value'][0])
Radek Krejcif041d652018-02-06 10:21:25 +0100375 else:
Radek Krejci0f793fe2018-02-14 08:33:45 +0100376 child = yang.Data_Node(parent, module, child_def['info']['name'])
Radek Krejcif041d652018-02-06 10:21:25 +0100377 if 'children' in child_def:
378 for grandchild in child_def['children']:
379 _create_child(ctx, child, grandchild)
380
381
382@auth.required()
383def session_commit():
Jakub Man47834ec2018-06-21 15:04:35 +0200384 session = auth.lookup(request.headers.get('lgui-Authorization', None))
Radek Krejcif041d652018-02-06 10:21:25 +0100385 user = session['user']
386
Radek Krejci5a93f9d2018-03-21 13:17:18 +0100387 req = request.get_json(keep_order = True)
Radek Krejcif041d652018-02-06 10:21:25 +0100388 if not 'key' in req:
389 return(json.dumps({'success': False, 'error-msg': 'Missing session key.'}))
390 if not 'modifications' in req:
391 return(json.dumps({'success': False, 'error-msg': 'Missing modifications.'}))
392
393 mods = req['modifications']
394 ctx = sessions[user.username][req['key']]['session'].context
395 root = None
Radek Krejci010475d2018-03-08 13:14:19 +0100396 reorders = []
Radek Krejcif041d652018-02-06 10:21:25 +0100397 for key in mods:
398 recursion = False
399 # get correct path and value if needed
400 path = key
401 value = None
402 if mods[key]['type'] == 'change':
403 value = mods[key]['value']
404 elif mods[key]['type'] == 'create' or mods[key]['type'] == 'replace':
Radek Krejci7a244f02018-02-21 09:46:18 +0100405 if mods[key]['data']['info']['type'] == 1:
406 # creating/replacing container
407 recursion = True
408 elif mods[key]['data']['info']['type'] == 4:
Radek Krejcif041d652018-02-06 10:21:25 +0100409 # creating/replacing leaf
410 value = mods[key]['data']['value']
Radek Krejci7a244f02018-02-21 09:46:18 +0100411 elif mods[key]['data']['info']['type'] == 8:
412 # creating/replacing leaf-list
413 value = mods[key]['data']['value'][0]
414 path = mods[key]['data']['path']
Radek Krejcif041d652018-02-06 10:21:25 +0100415 elif mods[key]['data']['info']['type'] == 16:
416 recursion = True
417 path = mods[key]['data']['path']
Radek Krejci010475d2018-03-08 13:14:19 +0100418 elif mods[key]['type'] == 'reorder':
419 # postpone reorders
420 reorders.extend(mods[key]['transactions'])
421 continue
Radek Krejcif041d652018-02-06 10:21:25 +0100422
423 # create node
Radek Krejci7a244f02018-02-21 09:46:18 +0100424 # print("creating " + path)
425 # print("value " + str(value))
Radek Krejcif041d652018-02-06 10:21:25 +0100426 if root:
427 root.new_path(ctx, path, value, 0, 0)
428 else:
Radek Krejci0f793fe2018-02-14 08:33:45 +0100429 root = yang.Data_Node(ctx, path, value, 0, 0)
Radek Krejcif041d652018-02-06 10:21:25 +0100430 node = root.find_path(path).data()[0];
431
432 # set operation attribute and add additional data if any
433 if mods[key]['type'] == 'change':
434 node.insert_attr(None, 'ietf-netconf:operation', 'merge')
435 elif mods[key]['type'] == 'delete':
436 node.insert_attr(None, 'ietf-netconf:operation', 'delete')
437 elif mods[key]['type'] == 'create':
438 node.insert_attr(None, 'ietf-netconf:operation', 'create')
439 elif mods[key]['type'] == 'replace':
440 node.insert_attr(None, 'ietf-netconf:operation', 'replace')
441 else:
442 return(json.dumps({'success': False, 'error-msg': 'Invalid modification ' + key}))
443
444 if recursion and 'children' in mods[key]['data']:
445 for child in mods[key]['data']['children']:
Radek Krejcif73403b2018-02-08 14:57:25 +0100446 if 'key' in child['info'] and child['info']['key']:
Radek Krejcif041d652018-02-06 10:21:25 +0100447 continue
448 _create_child(ctx, node, child)
449
Radek Krejci010475d2018-03-08 13:14:19 +0100450 # finally process reorders which must be last since they may refer newly created nodes
451 # and they do not reflect removed nodes
452 for move in reorders:
453 try:
454 node = root.find_path(move['node']).data()[0];
455 parent = node.parent()
456 node.unlink()
457 if parent:
458 parent.insert(node)
459 else:
460 root.insert_sibling(node)
461 except:
462 if root:
463 root.new_path(ctx, move['node'], None, 0, 0)
464 else:
465 root = yang.Data_Node(ctx, move['node'], None, 0, 0)
466 node = root.find_path(move['node']).data()[0];
467 node.insert_attr(None, 'yang:insert', move['insert'])
468 if move['insert'] == 'after' or move['insert'] == 'before':
469 if 'key' in move:
470 node.insert_attr(None, 'yang:key', move['key'])
471 elif 'value' in move:
472 node.insert_attr(None, 'yang:value', move['value'])
473
Radek Krejci0f793fe2018-02-14 08:33:45 +0100474 # print(root.print_mem(yang.LYD_XML, yang.LYP_FORMAT))
Radek Krejcif73403b2018-02-08 14:57:25 +0100475 try:
476 sessions[user.username][req['key']]['session'].rpcEditConfig(nc.DATASTORE_RUNNING, root)
477 except nc.ReplyError as e:
478 reply = {'success': False, 'error': []}
479 for err in e.args[0]:
480 reply['error'].append(json.loads(str(err)))
481 return(json.dumps(reply))
482
Radek Krejcif041d652018-02-06 10:21:25 +0100483 return(json.dumps({'success': True}))
484
485
Radek Krejci2e578562017-10-17 11:11:13 +0200486@auth.required()
Radek Krejcicbbb1972017-09-21 13:25:19 +0200487def session_close():
Jakub Man47834ec2018-06-21 15:04:35 +0200488 session = auth.lookup(request.headers.get('lgui-Authorization', None))
Radek Krejcicbbb1972017-09-21 13:25:19 +0200489 user = session['user']
490 req = request.args.to_dict()
491
492 if not 'key' in req:
493 return(json.dumps({'success': False, 'error-msg': 'Missing session key.'}))
494
495 if not user.username in sessions:
496 sessions[user.username] = {}
497
498 key = req['key']
499 if not key in sessions[user.username]:
500 return(json.dumps({'success': False, 'error-msg': 'Invalid session key.'}))
501
502 del sessions[user.username][key]
503 return(json.dumps({'success': True}))
504
505@auth.required()
506def session_alive():
Jakub Man47834ec2018-06-21 15:04:35 +0200507 session = auth.lookup(request.headers.get('lgui-Authorization', None))
Radek Krejcicbbb1972017-09-21 13:25:19 +0200508 user = session['user']
509 req = request.args.to_dict()
510
511 if not 'key' in req:
512 return(json.dumps({'success': False, 'error-msg': 'Missing session key.'}))
513
514 if not user.username in sessions:
515 sessions[user.username] = {}
Jakub Man47834ec2018-06-21 15:04:35 +0200516
Radek Krejcicbbb1972017-09-21 13:25:19 +0200517 key = req['key']
518 if not key in sessions[user.username]:
519 return(json.dumps({'success': False, 'error-msg': 'Invalid session key.'}))
520
521 return(json.dumps({'success': True}))