New tool to upgrade vnf,ns descriptors to R. THREE
[osm/devops.git] / descriptor-packages / tools / upgrade_descriptor_version.py
diff --git a/descriptor-packages/tools/upgrade_descriptor_version.py b/descriptor-packages/tools/upgrade_descriptor_version.py
new file mode 100755 (executable)
index 0000000..4259ece
--- /dev/null
@@ -0,0 +1,214 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+##
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+##
+
+import json
+import yaml
+# import logging
+import sys
+import getopt
+
+
+"""
+Converts OSM VNFD, NSD descriptor from release TWO to release THREE format
+"""
+__author__ = "Alfonso Tierno"
+__date__ = "2017-10-14"
+__version__ = "0.0.1"
+version_date = "Nov 2017"
+
+
+class ArgumentParserError(Exception):
+    pass
+
+
+def usage():
+    print("Usage: {} [options] FILE".format(sys.argv[0]))
+    print(" EXPERIMENTAL: Upgrade vnfd, nsd descriptor from old versions to release THREE version")
+    print(" FILE: a yaml or json vnfd-catalog or nsd-catalog descriptor")
+    print(" OPTIONS:")
+    print("      -v|--version: prints current version")
+    print("      -h|--help: shows this help")
+    print("      -i|--input FILE: (same as param FILE) descriptor file to be upgraded")
+    print("      -o|--output FILE: where to write generated descriptor. By default stdout")
+    # print("      --test: Content is tested to check wrong format or unknown keys")
+    return
+
+
+def remove_prefix(desc, prefix):
+    """
+    Recursively removes prefix from keys
+    :param desc: dictionary or list to change
+    :param prefix: prefix to remove. Must
+    :return: None, param desc is changed
+    """
+    prefix_len = len(prefix)
+    if isinstance(desc, dict):
+        prefixed_list=[]
+        for k,v in desc.items():
+            if isinstance(v, (list, tuple, dict)):
+                remove_prefix(v, prefix)
+            if isinstance(k, str) and k.startswith(prefix) and k != prefix:
+                prefixed_list.append(k)
+        for k in prefixed_list:
+            desc[k[prefix_len:]] = desc.pop(k)
+    elif isinstance(desc, (list, tuple)):
+        for i in desc:
+            if isinstance(desc, (list, tuple, dict)):
+                remove_prefix(i, prefix)
+
+
+if __name__=="__main__":
+    error_position = []
+    format_output_yaml = True
+    input_file_name = None
+    output_file_name = None
+    test_file = None
+    try:
+        # load parameters and configuration
+        opts, args = getopt.getopt(sys.argv[1:], "hvi:o:", ["input=", "help", "version", "output=", "test",])
+
+        for o, a in opts:
+            if o in ("-v", "--version"):
+                print ("upgrade descriptor version " + __version__ + ' ' + version_date)
+                sys.exit()
+            elif o in ("-h", "--help"):
+                usage()
+                sys.exit()
+            elif o in ("-i", "--input"):
+                input_file_name = a
+            elif o in ("-o", "--output"):
+                output_file_name = a
+            elif o == "--test":
+                test_file = True
+            else:
+                assert False, "Unhandled option"
+        if not input_file_name:
+            if not args:
+                raise ArgumentParserError("missing DESCRIPTOR_FILE parameter. Type --help for more info")
+            input_file_name = args[0]
+
+        # Open files
+        file_name = input_file_name
+        with open(file_name, 'r') as f:
+            descriptor_str = f.read()
+        if output_file_name:
+            file_name = output_file_name
+            output = open(file_name, 'w')
+        else:
+            output = sys.stdout
+
+        if input_file_name.endswith('.yaml') or input_file_name.endswith('.yml') or not \
+            (input_file_name.endswith('.json') or '\t' in descriptor_str):
+            data = yaml.load(descriptor_str)
+        else:   # json
+            data = json.loads(descriptor_str)
+            format_output_yaml = False
+
+        # Convert version
+        if "vnfd:vnfd-catalog" in data or "vnfd-catalog" in data:
+            remove_prefix(data, "vnfd:")
+            error_position.append("vnfd-catalog")
+            vnfd_descriptor = data["vnfd-catalog"]
+
+            vnfd_list = vnfd_descriptor["vnfd"]
+            error_position.append("vnfd")
+            for vnfd in vnfd_list:
+                error_position[-1] = "vnfd[{}]".format(vnfd["id"])
+
+                # Remove vnf-configuration:config-attributes
+                if "vnf-configuration" in vnfd and "config-attributes" in vnfd["vnf-configuration"]:
+                    del vnfd["vnf-configuration"]["config-attributes"]
+
+                # Iterate with vdu:interfaces
+                vdu_list = vnfd["vdu"]
+                error_position.append("vdu")
+                for vdu in vdu_list:
+                    error_position[-1] = "vdu[{}]".format(vdu["id"])
+
+                    # Change exernal/internal interface
+                    interface_list = []
+                    external_interface_list = vdu.pop("external-interface", ())
+                    error_position.append("external-interface")
+                    for external_interface in external_interface_list:
+                        error_position[-1] = "external-interface[{}]".format(external_interface["name"])
+                        external_interface["type"] = "EXTERNAL"
+                        external_interface["external-connection-point-ref"] = \
+                            external_interface.pop("vnfd-connection-point-ref")
+                        interface_list.append(external_interface)
+                    error_position.pop()
+
+                    internal_interface_list = vdu.pop("internal-interface", ())
+                    error_position.append("internal-interface")
+                    for internal_interface in internal_interface_list:
+                        error_position[-1] = "internal-interface[{}]".format(internal_interface["name"])
+                        internal_interface["type"] = "INTERNAL"
+                        internal_interface["internal-connection-point-ref"] = \
+                            internal_interface.pop("vdu-internal-connection-point-ref")
+                        interface_list.append(internal_interface)
+                    error_position.pop()
+
+                    if interface_list:
+                        vdu["interface"] = interface_list
+                error_position.pop()
+            error_position = []
+        elif "nsd:nsd-catalog" in data or "nsd-catalog" in data:
+            remove_prefix(data, "nsd:")
+            error_position.append("nsd-catalog")
+            nsd_descriptor = data["nsd-catalog"]
+
+            nsd_list = nsd_descriptor["nsd"]
+            error_position.append("nsd")
+            for nsd in nsd_list:
+                error_position[-1] = "nsd[{}]".format(nsd["id"])
+
+                # Change initial-config-primitive into initial-service-primitive
+                if "initial-config-primitive" in nsd:
+                    nsd['initial-service-primitive'] = nsd.pop("initial-config-primitive")
+            error_position = []
+        else:
+            error_position = ["global"]
+            raise KeyError("This is not neither nsd-catalog nor vnfd-catalog descriptor")
+
+        if format_output_yaml:
+            yaml.dump(data, output, indent=4, default_flow_style=False)
+        else:
+            json.dump(data, output)
+
+    except yaml.YAMLError as exc:
+        error_pos = ""
+        if hasattr(exc, 'problem_mark'):
+            mark = exc.problem_mark
+            error_pos = "at line:%s column:%s" % (mark.line + 1, mark.column + 1)
+        print("Error loading file '{}'. yaml format error {}".format(input_file_name, error_pos), file=sys.stderr)
+
+    except json.decoder.JSONDecodeError as e:
+        print("Invalid field at configuration file '{file}' {message}".format(file=input_file_name, message=str(e)),
+              file=sys.stderr)
+    except ArgumentParserError as e:
+        print(str(e), file=sys.stderr)
+    except IOError as e:
+        print("Error loading file '{}': {}".format(file_name, e), file=sys.stderr)
+    except Exception as e:
+        if error_position:
+            print("Descriptor error at '{}': {}".format(":".join(error_position), e), file=sys.stderr)
+        else:
+            raise
+            # print("Unexpected exception {}".format(e), file=sys.stderr)
+    exit(1)