RIFT OSM R1 Initial Submission
[osm/SO.git] / rwlaunchpad / plugins / rwlaunchpadtasklet / rift / tasklets / rwlaunchpad / tosca.py
diff --git a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/tosca.py b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/tosca.py
new file mode 100644 (file)
index 0000000..8ccc899
--- /dev/null
@@ -0,0 +1,240 @@
+# Copyright 2016 RIFT.io Inc
+#
+# 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 logging
+import os
+import shutil
+import subprocess
+import tempfile
+import uuid
+import zipfile
+
+from rift.mano.tosca_translator.shell import TranslatorShell
+from rift.mano.yang_translator.rwmano.yang_translator import YangTranslator
+
+
+class ToscaPackageError(Exception):
+    pass
+
+
+class ToscaPackageReadError(Exception):
+    pass
+
+
+class InvalidToscaPackageError(ToscaPackageError):
+    pass
+
+
+class ToscaTranslateError(ToscaPackageError):
+    pass
+
+
+class YangTranslateError(Exception):
+    pass
+
+
+class ToscaArchiveCreateError(YangTranslateError):
+    pass
+
+
+class YangTranslateNsdError(YangTranslateError):
+    pass
+
+
+class ExportTosca(object):
+    def __init__(self, log=None):
+        if log is None:
+            self.log = logging.getLogger("rw-mano-log")
+        else:
+            self.log = log
+        self.nsds = {}
+        self.csars = list()
+
+    def add_image(self, nsd_id, image, chksum=None):
+        if image.name not in self.images:
+            self.images[image.name] = image
+
+    def add_vld(self, nsd_id, vld, pkg=None):
+        if not 'vlds' in self.nsds[nsd_id]:
+            self.nsds[nsd_id]['vlds'] = []
+        self.nsds[nsd_id]['vlds'].append(vld)
+        if pkg:
+            self.nsds[nsd_id]['pkgs'].append(pkg)
+
+    def add_vnfd(self, nsd_id, vnfd, pkg=None):
+        if not 'vnfds' in self.nsds[nsd_id]:
+            self.nsds[nsd_id]['vnfds'] = []
+        self.nsds[nsd_id]['vnfds'].append(vnfd)
+        if pkg:
+            self.nsds[nsd_id]['pkgs'].append(pkg)
+
+    def add_nsd(self, nsd, pkg=None):
+        nsd_id = str(uuid.uuid4())
+        self.nsds[nsd_id] = {'nsd': nsd}
+        self.nsds[nsd_id]['pkgs'] = []
+        if pkg:
+            self.nsds[nsd_id]['pkgs'].append(pkg)
+        return nsd_id
+
+    def create_csar(self, nsd_id, dest=None):
+        if dest is None:
+            dest = tempfile.mkdtemp()
+
+        # Convert YANG to dict
+        yangs = {}
+        yangs['vnfd'] = []
+        for vnfd in self.nsds[nsd_id]['vnfds']:
+            yangs['vnfd'].append(vnfd.as_dict())
+            self.log.debug("Translate VNFD: {}".format(vnfd.as_dict()))
+        yangs['nsd'] = []
+        yangs['nsd'].append(self.nsds[nsd_id]['nsd'].as_dict())
+        self.log.debug("Translate NSD : {}".format(yangs['nsd']))
+
+        # Translate YANG model to TOSCA template
+        translator = YangTranslator(self.log,
+                                    yangs=yangs,
+                                    packages=self.nsds[nsd_id]['pkgs'])
+        output = translator.translate()
+        self.csars.extend(translator.write_output(output,
+                                                  output_dir=dest,
+                                                  archive=True))
+        self.log.debug("Created CSAR archive {}".format(self.csars[-1]))
+
+    def create_archive(self, archive_name, dest=None):
+        if not len(self.nsds):
+            self.log.error("Did not find any NSDs to export")
+            return
+
+        if dest is None:
+            dest = tempfile.mkdtemp()
+
+        prevdir = os.getcwd()
+
+        if not os.path.exists(dest):
+            os.makedirs(dest)
+
+        try:
+            # Convert each NSD to a TOSCA template
+            for nsd_id in self.nsds:
+                # Not passing the dest dir to prevent clash in case
+                # multiple export of the same desc happens
+                self.create_csar(nsd_id)
+
+        except Exception as e:
+            msg = "Exception converting NSD {}: {}".format(nsd_id, e)
+            self.log.exception(e)
+            raise YangTranslateNsdError(msg)
+
+        os.chdir(dest)
+
+        try:
+            if archive_name.endswith(".zip"):
+                archive_name = archive_name[:-4]
+
+            archive_path = os.path.join(dest, archive_name)
+
+            # Construct a zip of the csar archives
+            zip_name = '{}.zip'.format(archive_path)
+
+            if len(self.csars) == 1:
+                # Only 1 TOSCA template, just rename csar if required
+                if self.csars[0] != zip_name:
+                    mv_cmd = "mv {} {}".format(self.csars[0], zip_name)
+                    subprocess.check_call(mv_cmd, shell=True, stdout=subprocess.DEVNULL)
+                    # Remove the temporary directory created
+                    shutil.rmtree(os.path.dirname(self.csars[0]))
+
+            else:
+                with zipfile.ZipFile('{}.partial'.format(zip_name), 'w') as zf:
+                    for csar in self.csars:
+                        # Move file to the current dest dir
+                        if os.path.dirname(csar) != dest:
+                            file_mv = "mv {} {}".format(csar, dest)
+                            subprocess.check_call(file_mv,
+                                                  shell=True,
+                                                  stdout=subprocess.DEVNULL)
+                            # Remove the temporary directory created
+                            shutil.rmtree(os.path.dirname(csar))
+
+                        csar_f = os.basename(csar)
+                        # Now add to the archive
+                        zf.write(csar_f)
+                        # Remove the csar file
+                        os.remove(csar_f)
+
+                    # Rename archive to final name
+                    mv_cmd = "mv {0}.partial {0}".format(zip_name)
+                    subprocess.check_call(mv_cmd, shell=True, stdout=subprocess.DEVNULL)
+
+            return zip_name
+
+        except Exception as e:
+            msg = "Creating CSAR archive failed: {0}".format(e)
+            self.log.exception(e)
+            raise YangTranslateError(msg)
+
+        finally:
+            os.chdir(prevdir)
+
+class ImportTosca(object):
+
+    def __init__(self, log, in_file, out_dir=None):
+        if log is None:
+            self.log = logging.getLogger("rw-mano-log")
+        else:
+            self.log = log
+        self.log = log
+        self.in_file = in_file
+        self.out_dir = out_dir
+
+    def translate(self):
+        # Check if the input file is a zip file
+        if not zipfile.is_zipfile(self.in_file):
+            err_msg = "{} is not a zip file.".format(self.in_file)
+            self.log.error(err_msg)
+            raise InvalidToscaPackageError(err_msg)
+
+        try:
+            # Store the current working directory
+            prevdir = os.getcwd()
+
+            # See if we need to create a output directory
+            out_dir = self.out_dir
+            if out_dir is None:
+                out_dir = tempfile.mkdtemp()
+
+            # Call the TOSCA translator
+            self.log.debug("Calling tosca-translator for {}".
+                           format(self.in_file))
+            return TranslatorShell(self.log).translate(self.in_file,
+                                                       out_dir,
+                                                       archive=True)
+
+        except Exception as e:
+            self.log.exception(e)
+            raise ToscaTranslateError("Error translating TOSCA package {}: {}".
+                                      format(self.in_file, e))
+
+        finally:
+                os.chdir(prevdir)
+
+    @staticmethod
+    def is_tosca_package(in_file):
+        if zipfile.is_zipfile(in_file):
+            return True
+        else:
+            return False
+
+