f0b805461d84ced07ae753266cd344365d644885
[osm/NBI.git] / osm_nbi / validation.py
1 # -*- coding: utf-8 -*-
2
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12 # implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 from jsonschema import validate as js_v, exceptions as js_e
17 from http import HTTPStatus
18
19 __author__ = "Alfonso Tierno <alfonso.tiernosepulveda@telefonica.com>"
20 __version__ = "0.1"
21 version_date = "Mar 2018"
22
23 """
24 Validator of input data using JSON schemas for those items that not contains an OSM yang information model
25 """
26
27 # Basis schemas
28 patern_name = "^[ -~]+$"
29 nameshort_schema = {"type": "string", "minLength": 1, "maxLength": 60, "pattern": "^[^,;()\\.\\$'\"]+$"}
30 passwd_schema = {"type": "string", "minLength": 1, "maxLength": 60}
31 name_schema = {"type": "string", "minLength": 1, "maxLength": 255, "pattern": "^[^,;()'\"]+$"}
32 string_schema = {"type": "string", "minLength": 1, "maxLength": 255}
33 xml_text_schema = {"type": "string", "minLength": 1, "maxLength": 1000, "pattern": "^[^']+$"}
34 description_schema = {"type": ["string", "null"], "maxLength": 255, "pattern": "^[^'\"]+$"}
35 id_schema_fake = {"type": "string", "minLength": 2, "maxLength": 36}
36 bool_schema = {"type": "boolean"}
37 null_schema = {"type": "null"}
38 # "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$"
39 id_schema = {"type": "string", "pattern": "^[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}$"}
40 time_schema = {"type": "string", "pattern": "^[0-9]{4}-[0-1][0-9]-[0-3][0-9]T[0-2][0-9]([0-5]:){2}"}
41 pci_schema = {"type": "string", "pattern": "^[0-9a-fA-F]{4}(:[0-9a-fA-F]{2}){2}\\.[0-9a-fA-F]$"}
42 # allows [] for wildcards. For that reason huge length limit is set
43 pci_extended_schema = {"type": "string", "pattern": "^[0-9a-fA-F.:-\\[\\]]{12,40}$"}
44 http_schema = {"type": "string", "pattern": "^https?://[^'\"=]+$"}
45 bandwidth_schema = {"type": "string", "pattern": "^[0-9]+ *([MG]bps)?$"}
46 memory_schema = {"type": "string", "pattern": "^[0-9]+ *([MG]i?[Bb])?$"}
47 integer0_schema = {"type": "integer", "minimum": 0}
48 integer1_schema = {"type": "integer", "minimum": 1}
49 path_schema = {"type": "string", "pattern": "^(\\.){0,2}(/[^/\"':{}\\(\\)]+)+$"}
50 vlan_schema = {"type": "integer", "minimum": 1, "maximum": 4095}
51 vlan1000_schema = {"type": "integer", "minimum": 1000, "maximum": 4095}
52 mac_schema = {"type": "string",
53 "pattern": "^[0-9a-fA-F][02468aceACE](:[0-9a-fA-F]{2}){5}$"} # must be unicast: LSB bit of MSB byte ==0
54 dpid_Schema = {"type": "string", "pattern": "^[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){7}$"}
55 # mac_schema={"type":"string", "pattern":"^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$"}
56 ip_schema = {"type": "string",
57 "pattern": "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"}
58 ip_prefix_schema = {"type": "string",
59 "pattern": "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}"
60 "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/(30|[12]?[0-9])$"}
61 port_schema = {"type": "integer", "minimum": 1, "maximum": 65534}
62 object_schema = {"type": "object"}
63 schema_version_2 = {"type": "integer", "minimum": 2, "maximum": 2}
64 # schema_version_string={"type":"string","enum": ["0.1", "2", "0.2", "3", "0.3"]}
65 log_level_schema = {"type": "string", "enum": ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]}
66 checksum_schema = {"type": "string", "pattern": "^[0-9a-fA-F]{32}$"}
67 size_schema = {"type": "integer", "minimum": 1, "maximum": 100}
68 array_edition_schema = {
69 "type": "object",
70 "patternProperties": {
71 "^\\$": "Any"
72 },
73 "additionalProperties": False,
74 "minProperties": 1,
75 }
76 nameshort_list_schema = {
77 "type": "array",
78 "minItems": 1,
79 "items": nameshort_schema,
80 }
81
82
83 ns_instantiate_vdu = {
84 "title": "ns action instantiate input schema for vdu",
85 "$schema": "http://json-schema.org/draft-04/schema#",
86 "type": "object",
87 "properties": {
88 "id": name_schema,
89 "volume": {
90 "type": "array",
91 "minItems": 1,
92 "items": {
93 "type": "object",
94 "properties": {
95 "name": name_schema,
96 "vim-volume-id": name_schema,
97 },
98 "required": ["name", "vim-volume-id"],
99 "additionalProperties": False
100 }
101 },
102 "interface": {
103 "type": "array",
104 "minItems": 1,
105 "items": {
106 "type": "object",
107 "properties": {
108 "name": name_schema,
109 "ip-address": ip_schema,
110 "mac-address": mac_schema,
111 "floating-ip-required": bool_schema,
112 },
113 "required": ["name"],
114 "additionalProperties": False
115 }
116 }
117 },
118 "required": ["id"],
119 "additionalProperties": False
120 }
121
122 ip_profile_dns_schema = {
123 "type": "array",
124 "minItems": 1,
125 "items": {
126 "type": "object",
127 "properties": {
128 "address": ip_schema,
129 },
130 "required": ["address"],
131 "additionalProperties": False
132 }
133 }
134
135 ip_profile_dhcp_schema = {
136 "type": "object",
137 "properties": {
138 "enabled": {"type": "boolean"},
139 "count": integer1_schema,
140 "start-address": ip_schema
141 },
142 "additionalProperties": False,
143 }
144
145 ip_profile_schema = {
146 "title": "ip profile validation schame",
147 "$schema": "http://json-schema.org/draft-04/schema#",
148 "type": "object",
149 "properties": {
150 "ip-version": {"enum": ["ipv4", "ipv6"]},
151 "subnet-address": ip_prefix_schema,
152 "gateway-address": ip_schema,
153 "dns-server": ip_profile_dns_schema,
154 "dhcp-params": ip_profile_dhcp_schema,
155 }
156 }
157
158 ip_profile_update_schema = {
159 "title": "ip profile validation schame",
160 "$schema": "http://json-schema.org/draft-04/schema#",
161 "type": "object",
162 "properties": {
163 "ip-version": {"enum": ["ipv4", "ipv6"]},
164 "subnet-address": {"oneOf": [null_schema, ip_prefix_schema]},
165 "gateway-address": {"oneOf": [null_schema, ip_schema]},
166 "dns-server": {"oneOf": [null_schema, ip_profile_dns_schema]},
167
168 "dhcp-params": {"oneOf": [null_schema, ip_profile_dhcp_schema]},
169 },
170 "additionalProperties": False
171 }
172
173 ns_instantiate_internal_vld = {
174 "title": "ns action instantiate input schema for vdu",
175 "$schema": "http://json-schema.org/draft-04/schema#",
176 "type": "object",
177 "properties": {
178 "name": name_schema,
179 "vim-network-name": name_schema,
180 "ip-profile": ip_profile_update_schema,
181 "internal-connection-point": {
182 "type": "array",
183 "minItems": 1,
184 "items": {
185 "type": "object",
186 "properties": {
187 "id-ref": name_schema,
188 "ip-address": ip_schema,
189 # "mac-address": mac_schema,
190 },
191 "required": ["id-ref"],
192 "minProperties": 2,
193 "additionalProperties": False
194 },
195 }
196 },
197 "required": ["name"],
198 "minProperties": 2,
199 "additionalProperties": False
200 }
201
202 ns_instantiate = {
203 "title": "ns action instantiate input schema",
204 "$schema": "http://json-schema.org/draft-04/schema#",
205 "type": "object",
206 "properties": {
207 "lcmOperationType": string_schema,
208 "nsInstanceId": id_schema,
209 "netsliceInstanceId": id_schema,
210 "nsName": name_schema,
211 "nsDescription": {"oneOf": [description_schema, {"type": "null"}]},
212 "nsdId": id_schema,
213 "vimAccountId": id_schema,
214 "ssh_keys": {"type": "array", "items": {"type": "string"}},
215 "nsr_id": id_schema,
216 "vduImage": name_schema,
217 "vnf": {
218 "type": "array",
219 "minItems": 1,
220 "items": {
221 "type": "object",
222 "properties": {
223 "member-vnf-index": name_schema,
224 "vimAccountId": id_schema,
225 "vdu": {
226 "type": "array",
227 "minItems": 1,
228 "items": ns_instantiate_vdu,
229 },
230 "internal-vld": {
231 "type": "array",
232 "minItems": 1,
233 "items": ns_instantiate_internal_vld
234 }
235 },
236 "required": ["member-vnf-index"],
237 "minProperties": 2,
238 "additionalProperties": False
239 }
240 },
241 "vld": {
242 "type": "array",
243 "minItems": 1,
244 "items": {
245 "type": "object",
246 "properties": {
247 "name": string_schema,
248 "vim-network-name": {"OneOf": [string_schema, object_schema]},
249 "ip-profile": object_schema,
250 "vnfd-connection-point-ref": {
251 "type": "array",
252 "minItems": 1,
253 "items": {
254 "type": "object",
255 "properties": {
256 "member-vnf-index-ref": name_schema,
257 "vnfd-connection-point-ref": name_schema,
258 "ip-address": ip_schema,
259 # "mac-address": mac_schema,
260 },
261 "required": ["member-vnf-index-ref", "vnfd-connection-point-ref"],
262 "minProperties": 3,
263 "additionalProperties": False
264 },
265 }
266 },
267 "required": ["name"],
268 "additionalProperties": False
269 }
270 },
271 },
272 "required": ["nsName", "nsdId", "vimAccountId"],
273 "additionalProperties": False
274 }
275
276 ns_action = { # TODO for the moment it is only contemplated the vnfd primitive execution
277 "title": "ns action input schema",
278 "$schema": "http://json-schema.org/draft-04/schema#",
279 "type": "object",
280 "properties": {
281 "lcmOperationType": string_schema,
282 "nsInstanceId": id_schema,
283 "member_vnf_index": name_schema,
284 "vnf_member_index": name_schema, # TODO for backward compatibility. To remove in future
285 "vdu_id": name_schema,
286 "primitive": name_schema,
287 "primitive_params": {"type": "object"},
288 },
289 "required": ["primitive", "primitive_params"], # TODO add member_vnf_index
290 "additionalProperties": False
291 }
292 ns_scale = { # TODO for the moment it is only VDU-scaling
293 "title": "ns scale input schema",
294 "$schema": "http://json-schema.org/draft-04/schema#",
295 "type": "object",
296 "properties": {
297 "lcmOperationType": string_schema,
298 "nsInstanceId": id_schema,
299 "scaleType": {"enum": ["SCALE_VNF"]},
300 "scaleVnfData": {
301 "type": "object",
302 "properties": {
303 "vnfInstanceId": name_schema,
304 "scaleVnfType": {"enum": ["SCALE_OUT", 'SCALE_IN']},
305 "scaleByStepData": {
306 "type": "object",
307 "properties": {
308 "scaling-group-descriptor": name_schema,
309 "member-vnf-index": name_schema,
310 "scaling-policy": name_schema,
311 },
312 "required": ["scaling-group-descriptor", "member-vnf-index"],
313 "additionalProperties": False
314 },
315 },
316 "required": ["scaleVnfType", "scaleByStepData"], # vnfInstanceId
317 "additionalProperties": False
318 },
319 "scaleTime": time_schema,
320 },
321 "required": ["scaleType", "scaleVnfData"],
322 "additionalProperties": False
323 }
324
325
326 schema_version = {"type": "string", "enum": ["1.0"]}
327 vim_account_edit_schema = {
328 "title": "vim_account edit input schema",
329 "$schema": "http://json-schema.org/draft-04/schema#",
330 "type": "object",
331 "properties": {
332 "name": name_schema,
333 "description": description_schema,
334 "type": nameshort_schema, # currently "openvim" or "openstack", can be enlarged with plugins
335 "vim": name_schema,
336 "datacenter": name_schema,
337 "vim_url": description_schema,
338 "vim_url_admin": description_schema,
339 "vim_tenant": name_schema,
340 "vim_tenant_name": name_schema,
341 "vim_username": nameshort_schema,
342 "vim_password": passwd_schema,
343 "config": {"type": "object"}
344 },
345 "additionalProperties": False
346 }
347 schema_type = {"type": "string"}
348
349 vim_account_new_schema = {
350 "title": "vim_account creation input schema",
351 "$schema": "http://json-schema.org/draft-04/schema#",
352 "type": "object",
353 "properties": {
354 "schema_version": schema_version,
355 "schema_type": schema_type,
356 "name": name_schema,
357 "description": description_schema,
358 "vim": name_schema,
359 "datacenter": name_schema,
360 "vim_type": {"enum": ["openstack", "openvim", "vmware", "opennebula", "aws"]},
361 "vim_url": description_schema,
362 # "vim_url_admin": description_schema,
363 # "vim_tenant": name_schema,
364 "vim_tenant_name": name_schema,
365 "vim_user": nameshort_schema,
366 "vim_password": passwd_schema,
367 "config": {"type": "object"}
368 },
369 "required": ["name", "vim_url", "vim_type", "vim_user", "vim_password", "vim_tenant_name"],
370 "additionalProperties": False
371 }
372
373
374 sdn_properties = {
375 "name": name_schema,
376 "description": description_schema,
377 "dpid": dpid_Schema,
378 "ip": ip_schema,
379 "port": port_schema,
380 "type": {"type": "string", "enum": ["opendaylight", "floodlight", "onos"]},
381 "version": {"type": "string", "minLength": 1, "maxLength": 12},
382 "user": nameshort_schema,
383 "password": passwd_schema
384 }
385 sdn_new_schema = {
386 "title": "sdn controller information schema",
387 "$schema": "http://json-schema.org/draft-04/schema#",
388 "type": "object",
389 "properties": sdn_properties,
390 "required": ["name", "port", 'ip', 'dpid', 'type'],
391 "additionalProperties": False
392 }
393 sdn_edit_schema = {
394 "title": "sdn controller update information schema",
395 "$schema": "http://json-schema.org/draft-04/schema#",
396 "type": "object",
397 "properties": sdn_properties,
398 # "required": ["name", "port", 'ip', 'dpid', 'type'],
399 "additionalProperties": False
400 }
401 sdn_port_mapping_schema = {
402 "$schema": "http://json-schema.org/draft-04/schema#",
403 "title": "sdn port mapping information schema",
404 "type": "array",
405 "items": {
406 "type": "object",
407 "properties": {
408 "compute_node": nameshort_schema,
409 "ports": {
410 "type": "array",
411 "items": {
412 "type": "object",
413 "properties": {
414 "pci": pci_extended_schema,
415 "switch_port": nameshort_schema,
416 "switch_mac": mac_schema
417 },
418 "required": ["pci"]
419 }
420 }
421 },
422 "required": ["compute_node", "ports"]
423 }
424 }
425 sdn_external_port_schema = {
426 "$schema": "http://json-schema.org/draft-04/schema#",
427 "title": "External port information",
428 "type": "object",
429 "properties": {
430 "port": {"type": "string", "minLength": 1, "maxLength": 60},
431 "vlan": vlan_schema,
432 "mac": mac_schema
433 },
434 "required": ["port"]
435 }
436
437 # PDUs
438 pdu_interface = {
439 "type": "object",
440 "properties": {
441 "name": nameshort_schema,
442 "mgmt": bool_schema,
443 "type": {"enum": ["overlay", 'underlay']},
444 "ip-address": ip_schema,
445 # TODO, add user, password, ssh-key
446 "mac-address": mac_schema,
447 "vim-network-name": nameshort_schema, # interface is connected to one vim network, or switch port
448 # TODO "vim-network-id": nameshort_schema,
449 # # provide this in case SDN assist must deal with this interface
450 # "switch-dpid": dpid_Schema,
451 # "switch-port": nameshort_schema,
452 # "switch-mac": nameshort_schema,
453 # "switch-vlan": vlan_schema,
454 },
455 "required": ["name", "mgmt", "ip-address"],
456 "additionalProperties": False
457 }
458 pdu_new_schema = {
459 "title": "pdu creation input schema",
460 "$schema": "http://json-schema.org/draft-04/schema#",
461 "type": "object",
462 "properties": {
463 "name": nameshort_schema,
464 "type": nameshort_schema,
465 "description": description_schema,
466 "shared": bool_schema,
467 "vims": nameshort_list_schema,
468 "vim_accounts": nameshort_list_schema,
469 "interfaces": {
470 "type": "array",
471 "items": pdu_interface,
472 "minItems": 1
473 }
474 },
475 "required": ["name", "type", "interfaces"],
476 "additionalProperties": False
477 }
478
479 pdu_edit_schema = {
480 "title": "pdu edit input schema",
481 "$schema": "http://json-schema.org/draft-04/schema#",
482 "type": "object",
483 "properties": {
484 "name": nameshort_schema,
485 "type": nameshort_schema,
486 "description": description_schema,
487 "shared": bool_schema,
488 "vims": {"oneOff": [array_edition_schema, nameshort_list_schema]},
489 "vim_accounts": {"oneOff": [array_edition_schema, nameshort_list_schema]},
490 "interfaces": {"oneOff": [
491 array_edition_schema,
492 {
493 "type": "array",
494 "items": pdu_interface,
495 "minItems": 1
496 }
497 ]}
498 },
499 "additionalProperties": False,
500 "minProperties": 1
501 }
502
503 # USERS
504 user_new_schema = {
505 "$schema": "http://json-schema.org/draft-04/schema#",
506 "title": "New user schema",
507 "type": "object",
508 "properties": {
509 "username": nameshort_schema,
510 "password": passwd_schema,
511 "projects": nameshort_list_schema,
512 },
513 "required": ["username", "password", "projects"],
514 "additionalProperties": False
515 }
516 user_edit_schema = {
517 "$schema": "http://json-schema.org/draft-04/schema#",
518 "title": "User edit schema for administrators",
519 "type": "object",
520 "properties": {
521 "password": passwd_schema,
522 "projects": {
523 "oneOff": [
524 nameshort_list_schema,
525 array_edition_schema
526 ]
527 },
528 },
529 "minProperties": 1,
530 "additionalProperties": False
531 }
532
533 # PROJECTS
534 project_new_schema = {
535 "$schema": "http://json-schema.org/draft-04/schema#",
536 "title": "New project schema for administrators",
537 "type": "object",
538 "properties": {
539 "name": nameshort_schema,
540 "admin": bool_schema,
541 },
542 "required": ["name"],
543 "additionalProperties": False
544 }
545 project_edit_schema = {
546 "$schema": "http://json-schema.org/draft-04/schema#",
547 "title": "Project edit schema for administrators",
548 "type": "object",
549 "properties": {
550 "admin": bool_schema,
551 },
552 "additionalProperties": False,
553 "minProperties": 1
554 }
555
556 # GLOBAL SCHEMAS
557
558 nbi_new_input_schemas = {
559 "users": user_new_schema,
560 "projects": project_new_schema,
561 "vim_accounts": vim_account_new_schema,
562 "sdns": sdn_new_schema,
563 "ns_instantiate": ns_instantiate,
564 "ns_action": ns_action,
565 "ns_scale": ns_scale,
566 "pdus": pdu_new_schema,
567 }
568
569 nbi_edit_input_schemas = {
570 "users": user_edit_schema,
571 "projects": project_edit_schema,
572 "vim_accounts": vim_account_edit_schema,
573 "sdns": sdn_edit_schema,
574 "pdus": pdu_edit_schema,
575 }
576
577 # NETSLICE SCHEMAS
578 nsi_instantiate = {
579 "title": "netslice action instantiate input schema",
580 "$schema": "http://json-schema.org/draft-04/schema#",
581 "type": "object",
582 "properties": {
583 "lcmOperationType": string_schema,
584 "nsiInstanceId": id_schema,
585 "nsiName": name_schema,
586 "nsiDescription": {"oneOf": [description_schema, {"type": "null"}]},
587 "nstId": string_schema,
588 "vimAccountId": id_schema,
589 "ssh_keys": {"type": "string"},
590 "nsi_id": id_schema,
591 "ns": {
592 "type": "array",
593 "minItems": 1,
594 "items": ns_instantiate
595 },
596 },
597 "required": ["nsiName", "nstId", "vimAccountId"],
598 "additionalProperties": False
599 }
600
601 nsi_action = {
602
603 }
604
605 nsi_terminate = {
606
607 }
608
609
610 class ValidationError(Exception):
611 def __init__(self, message, http_code=HTTPStatus.UNPROCESSABLE_ENTITY):
612 self.http_code = http_code
613 Exception.__init__(self, message)
614
615
616 def validate_input(indata, schema_to_use):
617 """
618 Validates input data against json schema
619 :param indata: user input data. Should be a dictionary
620 :param schema_to_use: jsonschema to test
621 :return: None if ok, raises ValidationError exception on error
622 """
623 try:
624 if schema_to_use:
625 js_v(indata, schema_to_use)
626 return None
627 except js_e.ValidationError as e:
628 if e.path:
629 error_pos = "at '" + ":".join(map(str, e.path)) + "'"
630 else:
631 error_pos = ""
632 raise ValidationError("Format error {} '{}' ".format(error_pos, e.message))
633 except js_e.SchemaError:
634 raise ValidationError("Bad json schema {}".format(schema_to_use), http_code=HTTPStatus.INTERNAL_SERVER_ERROR)