blob: fb0f6b4cd6265131fac017d70a73a8802a907907 [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
9
Radek Krejcicbbb1972017-09-21 13:25:19 +020010from liberouterapi import auth
11from flask import request
12import netconf2 as nc
Radek Krejcicbbb1972017-09-21 13:25:19 +020013
14from .inventory import INVENTORY
15from .devices import devices_get
16from .error import NetopeerException
Radek Krejcia1339602017-11-02 13:52:38 +010017from .data import *
Radek Krejcicbbb1972017-09-21 13:25:19 +020018
19sessions = {}
20
Radek Krejcie20e4d82017-11-08 14:18:05 +010021def hostkey_check(hostname, state, keytype, hexa, priv):
22 # TODO real check
23 return True
24
Radek Krejcicbbb1972017-09-21 13:25:19 +020025@auth.required()
26def connect():
27 session = auth.lookup(request.headers.get('Authorization', None))
28 user = session['user']
29 path = os.path.join(INVENTORY, user.username)
30
Radek Krejcia6c8b412017-10-17 16:59:38 +020031 data = request.get_json()
32 if 'id' in data:
33 # stored device
34 device = devices_get(data['id'], user.username)
35 elif 'device' in data:
36 # one-time connect, the device is specified in request
37 device = data['device']
38 else:
39 raise NetopeerException('Invalid connection request.')
40
Radek Krejcicbbb1972017-09-21 13:25:19 +020041 if not device:
42 raise NetopeerException('Unknown device to connect to request.')
43
44 nc.setSearchpath(path)
45
46 ssh = nc.SSH(device['username'], password=device['password'])
Radek Krejcie20e4d82017-11-08 14:18:05 +010047 ssh.setAuthHostkeyCheckClb(hostkey_check)
Radek Krejcicbbb1972017-09-21 13:25:19 +020048 try:
49 session = nc.Session(device['hostname'], device['port'], ssh)
50 except Exception as e:
51 return(json.dumps({'success': False, 'error-msg': str(e)}))
52
53 if not user.username in sessions:
54 sessions[user.username] = {}
55
56 # use key (as hostname:port:session-id) to store the created NETCONF session
57 key = session.host + ":" + str(session.port) + ":" + session.id
Radek Krejci4d3896c2018-01-08 17:10:43 +010058 sessions[user.username][key] = {}
59 sessions[user.username][key]['session'] = session
Radek Krejcicbbb1972017-09-21 13:25:19 +020060
61 return(json.dumps({'success': True, 'session-key': key}))
62
63
64@auth.required()
65def session_get_capabilities():
66 session = auth.lookup(request.headers.get('Authorization', None))
67 user = session['user']
68 req = request.args.to_dict()
69
70 if not 'key' in req:
71 return(json.dumps({'success': False, 'error-msg': 'Missing session key.'}))
72
73 if not user.username in sessions:
74 sessions[user.username] = {}
75
76 key = req['key']
77 if not key in sessions[user.username]:
78 return(json.dumps({'success': False, 'error-msg': 'Invalid session key.'}))
79
80 cpblts = []
Radek Krejci4d3896c2018-01-08 17:10:43 +010081 for c in sessions[user.username][key]['session'].capabilities:
Radek Krejcicbbb1972017-09-21 13:25:19 +020082 cpblts.append(c)
83
84 return(json.dumps({'success': True, 'capabilities': cpblts}))
85
86@auth.required()
Radek Krejci2e578562017-10-17 11:11:13 +020087def session_get():
88 session = auth.lookup(request.headers.get('Authorization', None))
89 user = session['user']
90 req = request.args.to_dict()
91
92 if not 'key' in req:
93 return(json.dumps({'success': False, 'error-msg': 'Missing session key.'}))
Radek Krejcia1339602017-11-02 13:52:38 +010094 if not 'recursive' in req:
95 return(json.dumps({'success': False, 'error-msg': 'Missing recursive flag.'}))
Radek Krejci2e578562017-10-17 11:11:13 +020096
97 if not user.username in sessions:
98 sessions[user.username] = {}
99
100 key = req['key']
101 if not key in sessions[user.username]:
102 return(json.dumps({'success': False, 'error-msg': 'Invalid session key.'}))
103
Radek Krejci2e578562017-10-17 11:11:13 +0200104 try:
Radek Krejci4d3896c2018-01-08 17:10:43 +0100105 sessions[user.username][key]['data'] = sessions[user.username][key]['session'].rpcGet()
Radek Krejci2e578562017-10-17 11:11:13 +0200106 except nc.ReplyError as e:
107 reply = {'success': False, 'error': []}
108 for err in e.args[0]:
109 reply['error'].append(json.loads(str(err)))
110 return(json.dumps(reply))
111
Radek Krejcia1339602017-11-02 13:52:38 +0100112 if not 'path' in req:
Radek Krejci4d3896c2018-01-08 17:10:43 +0100113 return(dataInfoRoots(sessions[user.username][key]['data'], True if req['recursive'] == 'true' else False))
Radek Krejcia1339602017-11-02 13:52:38 +0100114 else:
Radek Krejci4d3896c2018-01-08 17:10:43 +0100115 return(dataInfoSubtree(sessions[user.username][key]['data'], req['path'], True if req['recursive'] == 'true' else False))
116
117
Radek Krejci6e772b22018-01-25 13:28:57 +0100118def _checkvalue(session, req, schema):
119 user = session['user'];
Radek Krejci4d3896c2018-01-08 17:10:43 +0100120
121 if not 'key' in req:
122 return(json.dumps({'success': False, 'error-msg': 'Missing session key.'}))
123 if not 'path' in req:
124 return(json.dumps({'success': False, 'error-msg': 'Missing path to validate value.'}))
125 if not 'value' in req:
126 return(json.dumps({'success': False, 'error-msg': 'Missing value to validate.'}))
127
Radek Krejci6e772b22018-01-25 13:28:57 +0100128 key = req['key']
129 if not key in sessions[user.username]:
130 return(json.dumps({'success': False, 'error-msg': 'Invalid session key.'}))
131
132 if schema:
133 # TODO - go via context to cover case there is no data
134 print(req['path'])
135 search = sessions[user.username][key]['session'].context.find_path(req['path'])
136 print(search.number())
137 else:
138 search = sessions[user.username][key]['data'].find_path(req['path'])
139
140 if search.number() != 1:
141 return(json.dumps({'success': False, 'error-msg': 'Invalid data path.'}))
142
143 if schema:
144 node = search.schema()[0]
145 else:
146 node = search.data()[0]
147
148 if node.validate_value(req['value']):
149 return(json.dumps({'success': False, 'error-msg': ly.Error().errmsg()}))
150
151 return(json.dumps({'success': True}))
152
153@auth.required()
154def data_checkvalue():
155 session = auth.lookup(request.headers.get('Authorization', None))
156 req = request.args.to_dict()
157
158 return _checkvalue(session, req, False)
159
160
161@auth.required()
162def schema_checkvalue():
163 session = auth.lookup(request.headers.get('Authorization', None))
164 req = request.args.to_dict()
165
166 return _checkvalue(session, req, True)
167
168
169@auth.required()
170def schema_info():
171 session = auth.lookup(request.headers.get('Authorization', None))
172 user = session['user']
173 req = request.args.to_dict()
174
175 if not 'key' in req:
176 return(json.dumps({'success': False, 'error-msg': 'Missing session key.'}))
177 if not 'path' in req:
178 return(json.dumps({'success': False, 'error-msg': 'Missing path to validate value.'}))
Radek Krejci4d3896c2018-01-08 17:10:43 +0100179
180 key = req['key']
181 if not key in sessions[user.username]:
182 return(json.dumps({'success': False, 'error-msg': 'Invalid session key.'}))
183
Radek Krejci6e772b22018-01-25 13:28:57 +0100184 if req['path'] == '/':
185 # TODO top level
186 return(json.dumps({'success': False, 'error-msg': 'Not implemented yet.'}))
187 else:
188 search = sessions[user.username][key]['session'].context.find_path(req['path'])
189 if search.number() != 1:
190 return(json.dumps({'success': False, 'error-msg': 'Invalid data path.'}))
191 node = search.schema()[0]
Radek Krejci4d3896c2018-01-08 17:10:43 +0100192
Radek Krejci6e772b22018-01-25 13:28:57 +0100193 result = [];
194 if 'relative' in req:
195 if req['relative'] == 'children':
196 for child in node.child_instantiables(0):
197 if child.flags() & ly.LYS_CONFIG_R:
198 # ignore status nodes
199 continue
200 result.append(schemaInfoNode(child))
201 elif req['relative'] == 'siblings':
202 if node.parent():
203 for child in node.parent().child_instantiables(0):
204 result.append(schemaInfoNode(child))
205 # TODO top level - !node.parent()
206 else:
207 return(json.dumps({'success': False, 'error-msg': 'Invalid relative parameter.'}))
208 else:
209 result.append(schemaInfoNode(node))
Radek Krejci4d3896c2018-01-08 17:10:43 +0100210
Radek Krejci6e772b22018-01-25 13:28:57 +0100211 return(json.dumps({'success': True, 'data': result}))
Radek Krejci2e578562017-10-17 11:11:13 +0200212
213
214@auth.required()
Radek Krejcicbbb1972017-09-21 13:25:19 +0200215def session_close():
216 session = auth.lookup(request.headers.get('Authorization', None))
217 user = session['user']
218 req = request.args.to_dict()
219
220 if not 'key' in req:
221 return(json.dumps({'success': False, 'error-msg': 'Missing session key.'}))
222
223 if not user.username in sessions:
224 sessions[user.username] = {}
225
226 key = req['key']
227 if not key in sessions[user.username]:
228 return(json.dumps({'success': False, 'error-msg': 'Invalid session key.'}))
229
230 del sessions[user.username][key]
231 return(json.dumps({'success': True}))
232
233@auth.required()
234def session_alive():
235 session = auth.lookup(request.headers.get('Authorization', None))
236 user = session['user']
237 req = request.args.to_dict()
238
239 if not 'key' in req:
240 return(json.dumps({'success': False, 'error-msg': 'Missing session key.'}))
241
242 if not user.username in sessions:
243 sessions[user.username] = {}
244
245 key = req['key']
246 if not key in sessions[user.username]:
247 return(json.dumps({'success': False, 'error-msg': 'Invalid session key.'}))
248
249 return(json.dumps({'success': True}))