feature5837: tools/validate_descriptor.py updated to support vdu and kdu
[osm/devops.git] / descriptor-packages / tools / validate_descriptor.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3
4 ##
5 # All Rights Reserved.
6 #
7 # Licensed under the Apache License, Version 2.0 (the "License"); you may
8 # not use this file except in compliance with the License. You may obtain
9 # a copy of the License at
10 #
11 # http://www.apache.org/licenses/LICENSE-2.0
12 #
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16 # License for the specific language governing permissions and limitations
17 # under the License.
18 #
19 ##
20 from __future__ import print_function
21 import json
22 import yaml
23 import sys
24 import getopt
25 import os
26
27 """
28 Tests the format of OSM VNFD and NSD descriptors
29 """
30 __author__ = "Alfonso Tierno, Guillermo Calvino"
31 __date__ = "2018-04-16"
32 __version__ = "0.0.1"
33 version_date = "Apr 2018"
34
35
36 class ArgumentParserError(Exception):
37 pass
38
39 class DescriptorValidationError(Exception):
40 pass
41
42 def usage():
43 print("Usage: {} [options] FILE".format(sys.argv[0]))
44 print(" Validates vnfd, nsd and nst descriptors format")
45 print(" FILE: a yaml or json vnfd-catalog, nsd-catalog or nst descriptor")
46 print(" OPTIONS:")
47 print(" -v|--version: prints current version")
48 print(" -h|--help: shows this help")
49 print(" -i|--input FILE: (same as param FILE) descriptor file to be upgraded")
50 print(" -c|--charms: looks for the charms folder and validates its coherency with the descriptor")
51 return
52
53
54 def remove_prefix(desc, prefix):
55 """
56 Recursively removes prefix from keys
57 :param desc: dictionary or list to change
58 :param prefix: prefix to remove. Must
59 :return: None, param desc is changed
60 """
61 prefix_len = len(prefix)
62 if isinstance(desc, dict):
63 prefixed_list=[]
64 for k,v in desc.items():
65 if isinstance(v, (list, tuple, dict)):
66 remove_prefix(v, prefix)
67 if isinstance(k, str) and k.startswith(prefix) and k != prefix:
68 prefixed_list.append(k)
69 for k in prefixed_list:
70 desc[k[prefix_len:]] = desc.pop(k)
71 elif isinstance(desc, (list, tuple)):
72 for i in desc:
73 if isinstance(desc, (list, tuple, dict)):
74 remove_prefix(i, prefix)
75
76
77 # Mrityunjay Yadav: Function to verify charm included in VNF Package
78 def validate_charm(charm, desc_file):
79 """
80 Verify charm included in VNF Package and raised error if invalid
81 :param charm: vnf-configuration/vdu-configuration
82 :param desc_file: descriptor file
83 :return: None
84 """
85 check_list = ['layer.yaml', 'metadata.yaml', 'actions.yaml', 'actions', 'hooks']
86 charm_name = charm['juju']['charm']
87 charm_dir = os.path.join(os.path.abspath(os.path.dirname(desc_file)), 'charms', charm_name)
88
89 config_primitive = charm.get('config-primitive', [])
90 initial_config_primitive = charm.get('initial-config-primitive', [])
91
92 if charm.get('metrics'):
93 check_list.append('metrics.yaml')
94
95 if os.path.exists(charm_dir):
96 if not all(item in os.listdir(charm_dir) for item in check_list):
97 raise KeyError("Invalid charm {}".format(charm_name))
98 else:
99 raise KeyError("Provided charm:{} does not exist in descriptor.".format(charm_name))
100
101
102 if __name__ == "__main__":
103 error_position = []
104 format_output_yaml = True
105 input_file_name = None
106 test_file = None
107 file_name = None
108 validate_charms = False
109 try:
110 # load parameters and configuration
111 opts, args = getopt.getopt(sys.argv[1:], "hvi:o:", ["input=", "help", "version",])
112
113 for o, a in opts:
114 if o in ("-v", "--version"):
115 print("test descriptor version THREE " + __version__ + ' ' + version_date)
116 sys.exit()
117 elif o in ("-h", "--help"):
118 usage()
119 sys.exit()
120 elif o in ("-i", "--input"):
121 input_file_name = a
122 elif o in ("-c", "--charms"):
123 validate_charms = True
124 else:
125 assert False, "Unhandled option"
126 if not input_file_name:
127 if not args:
128 raise ArgumentParserError("missing DESCRIPTOR_FILE parameter. Type --help for more info")
129 input_file_name = args[0]
130
131 # Open files
132 file_name = input_file_name
133 with open(file_name, 'r') as f:
134 descriptor_str = f.read()
135 file_name = None
136
137 if input_file_name.endswith('.yaml') or input_file_name.endswith('.yml') or not \
138 (input_file_name.endswith('.json') or '\t' in descriptor_str):
139 data = yaml.load(descriptor_str)
140 else: # json
141 data = json.loads(descriptor_str)
142 format_output_yaml = False
143
144 import osm_im.vnfd as vnfd_catalog
145 import osm_im.nsd as nsd_catalog
146 import osm_im.nst as nst_catalog
147 from pyangbind.lib.serialise import pybindJSONDecoder
148
149 if "vnfd:vnfd-catalog" in data or "vnfd-catalog" in data:
150 descriptor = "VNF"
151 # Check if mgmt-interface is defined:
152 remove_prefix(data, "vnfd:")
153 vnfd_descriptor = data["vnfd-catalog"]
154 vnfd_list = vnfd_descriptor["vnfd"]
155 mgmt_iface = False
156 for vnfd in vnfd_list:
157 if "vdu" not in vnfd and "kdu" not in vnfd:
158 raise DescriptorValidationError("vdu or kdu not present in the descriptor")
159 vdu_list = vnfd.get("vdu",[])
160 for vdu in vdu_list:
161 interface_list = []
162 external_interface_list = vdu.pop("external-interface", ())
163 for external_interface in external_interface_list:
164 if external_interface.get("virtual-interface", {}).get("type") == "OM-MGMT":
165 raise KeyError(
166 "Wrong 'Virtual-interface type': Deprecated 'OM-MGMT' value. Please, use 'PARAVIRT' instead")
167 interface_list = vdu.get("interface", ())
168 for interface in interface_list:
169 if interface.get("virtual-interface", {}).get("type") == "OM-MGMT":
170 raise KeyError(
171 "Wrong 'Virtual-interface type': Deprecated 'OM-MGMT' value. Please, use 'PARAVIRT' instead")
172 # Mrityunjay yadav: Verify charm if included in vdu
173 if vdu.get("vdu-configuration", False) and validate_charms:
174 validate_charm(vdu["vdu-configuration"], input_file_name)
175 if vnfd.get("mgmt-interface"):
176 mgmt_iface = True
177 if vnfd["mgmt-interface"].get("vdu-id"):
178 raise KeyError("'mgmt-iface': Deprecated 'vdu-id' field. Please, use 'cp' field instead")
179 # Mrityunjay yadav: Verify charm if included in vnf
180 if vnfd.get("vnf-configuration", False) and validate_charms:
181 validate_charm(vnfd["vnf-configuration"], input_file_name)
182 kdu_list = vnfd.get("kdu",[])
183
184 if not mgmt_iface:
185 raise KeyError("'mgmt-interface' is a mandatory field and it is not defined")
186 myvnfd = vnfd_catalog.vnfd()
187 pybindJSONDecoder.load_ietf_json(data, None, None, obj=myvnfd)
188 elif "nsd:nsd-catalog" in data or "nsd-catalog" in data:
189 descriptor = "NS"
190 mynsd = nsd_catalog.nsd()
191 pybindJSONDecoder.load_ietf_json(data, None, None, obj=mynsd)
192 elif "nst:nst" in data or "nst" in data:
193 descriptor = "NST"
194 mynst = nst_catalog.nst()
195 pybindJSONDecoder.load_ietf_json(data, None, None, obj=mynst)
196 else:
197 descriptor = None
198 raise KeyError("This is not neither nsd-catalog nor vnfd-catalog descriptor")
199 exit(0)
200
201 except yaml.YAMLError as exc:
202 error_pos = ""
203 if hasattr(exc, 'problem_mark'):
204 mark = exc.problem_mark
205 error_pos = "at line:%s column:%s" % (mark.line + 1, mark.column + 1)
206 print("Error loading file '{}'. yaml format error {}".format(input_file_name, error_pos), file=sys.stderr)
207 except DescriptorValidationError as e:
208 print(str(e), file=sys.stderr)
209 except ArgumentParserError as e:
210 print(str(e), file=sys.stderr)
211 except IOError as e:
212 print("Error loading file '{}': {}".format(file_name, e), file=sys.stderr)
213 except ImportError as e:
214 print ("Package python-osm-im not installed: {}".format(e), file=sys.stderr)
215 except Exception as e:
216 if file_name:
217 print("Error loading file '{}': {}".format(file_name, str(e)), file=sys.stderr)
218 else:
219 if descriptor:
220 print("Error. Invalid {} descriptor format in '{}': {}".format(descriptor, input_file_name, str(e)), file=sys.stderr)
221 else:
222 print("Error. Invalid descriptor format in '{}': {}".format(input_file_name, str(e)), file=sys.stderr)
223 exit(1)