3c6dd4198dac4a0fcfa384d0fd69e36e40a13945
[osm/devops.git] / descriptor-packages / tools / validate_descriptor.py
1 #!/usr/bin/env python2
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
40 def usage():
41 print("Usage: {} [options] FILE".format(sys.argv[0]))
42 print(" Validates vnfd, nsd and nst descriptors format")
43 print(" FILE: a yaml or json vnfd-catalog, nsd-catalog or nst descriptor")
44 print(" OPTIONS:")
45 print(" -v|--version: prints current version")
46 print(" -h|--help: shows this help")
47 print(" -i|--input FILE: (same as param FILE) descriptor file to be upgraded")
48 print(" -c|--charms: looks for the charms folder and validates its coherency with the descriptor")
49 return
50
51
52 def remove_prefix(desc, prefix):
53 """
54 Recursively removes prefix from keys
55 :param desc: dictionary or list to change
56 :param prefix: prefix to remove. Must
57 :return: None, param desc is changed
58 """
59 prefix_len = len(prefix)
60 if isinstance(desc, dict):
61 prefixed_list=[]
62 for k,v in desc.items():
63 if isinstance(v, (list, tuple, dict)):
64 remove_prefix(v, prefix)
65 if isinstance(k, str) and k.startswith(prefix) and k != prefix:
66 prefixed_list.append(k)
67 for k in prefixed_list:
68 desc[k[prefix_len:]] = desc.pop(k)
69 elif isinstance(desc, (list, tuple)):
70 for i in desc:
71 if isinstance(desc, (list, tuple, dict)):
72 remove_prefix(i, prefix)
73
74
75 # Mrityunjay Yadav: Function to verify charm included in VNF Package
76 def validate_charm(charm, desc_file):
77 """
78 Verify charm included in VNF Package and raised error if invalid
79 :param charm: vnf-configuration/vdu-configuration
80 :param desc_file: descriptor file
81 :return: None
82 """
83 check_list = ['layer.yaml', 'metadata.yaml', 'actions.yaml', 'actions', 'hooks']
84 charm_name = charm['juju']['charm']
85 charm_dir = os.path.join(os.path.abspath(os.path.dirname(desc_file)), 'charms', charm_name)
86
87 config_primitive = charm.get('config-primitive', [])
88 initial_config_primitive = charm.get('initial-config-primitive', [])
89
90 if charm.get('metrics'):
91 check_list.append('metrics.yaml')
92
93 if os.path.exists(charm_dir):
94 if not all(item in os.listdir(charm_dir) for item in check_list):
95 raise KeyError("Invalid charm {}".format(charm_name))
96 else:
97 raise KeyError("Provided charm:{} does not exist in descriptor.".format(charm_name))
98
99
100 if __name__ == "__main__":
101 error_position = []
102 format_output_yaml = True
103 input_file_name = None
104 test_file = None
105 file_name = None
106 validate_charms = False
107 try:
108 # load parameters and configuration
109 opts, args = getopt.getopt(sys.argv[1:], "hvi:o:", ["input=", "help", "version",])
110
111 for o, a in opts:
112 if o in ("-v", "--version"):
113 print ("test descriptor version THREE " + __version__ + ' ' + version_date)
114 sys.exit()
115 elif o in ("-h", "--help"):
116 usage()
117 sys.exit()
118 elif o in ("-i", "--input"):
119 input_file_name = a
120 elif o in ("-c", "--charms"):
121 validate_charms = True
122 else:
123 assert False, "Unhandled option"
124 if not input_file_name:
125 if not args:
126 raise ArgumentParserError("missing DESCRIPTOR_FILE parameter. Type --help for more info")
127 input_file_name = args[0]
128
129 # Open files
130 file_name = input_file_name
131 with open(file_name, 'r') as f:
132 descriptor_str = f.read()
133 file_name = None
134
135 if input_file_name.endswith('.yaml') or input_file_name.endswith('.yml') or not \
136 (input_file_name.endswith('.json') or '\t' in descriptor_str):
137 data = yaml.load(descriptor_str)
138 else: # json
139 data = json.loads(descriptor_str)
140 format_output_yaml = False
141
142 import osm_im.vnfd as vnfd_catalog
143 import osm_im.nsd as nsd_catalog
144 import osm_im.nst as nst_catalog
145 from pyangbind.lib.serialise import pybindJSONDecoder
146
147 if "vnfd:vnfd-catalog" in data or "vnfd-catalog" in data:
148 descriptor = "VNF"
149 # Check if mgmt-interface is defined:
150 remove_prefix(data, "vnfd:")
151 vnfd_descriptor = data["vnfd-catalog"]
152 vnfd_list = vnfd_descriptor["vnfd"]
153 mgmt_iface = False
154 for vnfd in vnfd_list:
155 vdu_list = vnfd["vdu"]
156 for vdu in vdu_list:
157 interface_list = []
158 external_interface_list = vdu.pop("external-interface", ())
159 for external_interface in external_interface_list:
160 if external_interface.get("virtual-interface", {}).get("type") == "OM-MGMT":
161 raise KeyError(
162 "Wrong 'Virtual-interface type': Deprecated 'OM-MGMT' value. Please, use 'PARAVIRT' instead")
163 interface_list = vdu.get("interface", ())
164 for interface in interface_list:
165 if interface.get("virtual-interface", {}).get("type") == "OM-MGMT":
166 raise KeyError(
167 "Wrong 'Virtual-interface type': Deprecated 'OM-MGMT' value. Please, use 'PARAVIRT' instead")
168 # Mrityunjay yadav: Verify charm if included in vdu
169 if vdu.get("vdu-configuration", False) and validate_charms:
170 validate_charm(vdu["vdu-configuration"], input_file_name)
171 if vnfd.get("mgmt-interface"):
172 mgmt_iface = True
173 if vnfd["mgmt-interface"].get("vdu-id"):
174 raise KeyError("'mgmt-iface': Deprecated 'vdu-id' field. Please, use 'cp' field instead")
175 # Mrityunjay yadav: Verify charm if included in vnf
176 if vnfd.get("vnf-configuration", False) and validate_charms:
177 validate_charm(vnfd["vnf-configuration"], input_file_name)
178
179 if not mgmt_iface:
180 raise KeyError("'mgmt-interface' is a mandatory field and it is not defined")
181 myvnfd = vnfd_catalog.vnfd()
182 pybindJSONDecoder.load_ietf_json(data, None, None, obj=myvnfd)
183 elif "nsd:nsd-catalog" in data or "nsd-catalog" in data:
184 descriptor = "NS"
185 mynsd = nsd_catalog.nsd()
186 pybindJSONDecoder.load_ietf_json(data, None, None, obj=mynsd)
187 elif "nst:nst" in data or "nst" in data:
188 descriptor = "NST"
189 mynst = nst_catalog.nst()
190 pybindJSONDecoder.load_ietf_json(data, None, None, obj=mynst)
191 else:
192 descriptor = None
193 raise KeyError("This is not neither nsd-catalog nor vnfd-catalog descriptor")
194 exit(0)
195
196 except yaml.YAMLError as exc:
197 error_pos = ""
198 if hasattr(exc, 'problem_mark'):
199 mark = exc.problem_mark
200 error_pos = "at line:%s column:%s" % (mark.line + 1, mark.column + 1)
201 print("Error loading file '{}'. yaml format error {}".format(input_file_name, error_pos), file=sys.stderr)
202 except ArgumentParserError as e:
203 print(str(e), file=sys.stderr)
204 except IOError as e:
205 print("Error loading file '{}': {}".format(file_name, e), file=sys.stderr)
206 except ImportError as e:
207 print ("Package python-osm-im not installed: {}".format(e), file=sys.stderr)
208 except Exception as e:
209 if file_name:
210 print("Error loading file '{}': {}".format(file_name, str(e)), file=sys.stderr)
211 else:
212 if descriptor:
213 print("Error. Invalid {} descriptor format in '{}': {}".format(descriptor, input_file_name, str(e)), file=sys.stderr)
214 else:
215 print("Error. Invalid descriptor format in '{}': {}".format(input_file_name, str(e)), file=sys.stderr)
216 exit(1)