first commit

Change-Id: I8a65ee5527dd16d81e87c8ac5d4bdb471e5e759d
Signed-off-by: lombardof <flombardo@cnit.it>
diff --git a/lib/TopologyModels/example/example.yaml b/lib/TopologyModels/example/example.yaml
new file mode 100644
index 0000000..04633f5
--- /dev/null
+++ b/lib/TopologyModels/example/example.yaml
@@ -0,0 +1,53 @@
+name: Example                              #Model name
+description: example                       #Model description
+version: 1.0                            #Model version
+designer: Pierluigi Greto               #Model designer
+nodes:                                   #List of nodes, with id and label used in the gui (We can add more fields)
+  examplenode1:
+    label: EXN1
+  examplenode2:
+    label: EXN2
+layer:                                  #List of Layers
+    exampleLayer:
+      nodes:                  #List of node to be visualized in the current layer
+        examplenode1:
+          addable:
+            callback: addNode
+          removable: true
+        examplenode1:
+          addable:
+            callback: addNode
+          removable: true
+      allowed_edges:                  #List of allowed edges between the layer's nodes
+        examplenode1:                        #Edge's source
+          destination:           #List of edge's destination with the list of controls callback id to call when there is a connections
+            examplenode1:
+              direct_edge: false
+              removable: true
+            examplenode2:
+              direct_edge: false
+              removable: true
+action:           #Action to show on rightclick on a node/link
+    rightclick:
+      node:
+        delete:
+          title: Delete
+          callback: deleteNode
+      link:
+        delete:
+          title: Delete
+          callback: deleteLink
+
+callback:                             #List of callbacks used
+  addNode:
+      file: example_controller.js
+      class: ExampleController
+  removeNode:
+      file: example_controller.js
+      class: ExampleController
+  addLink:
+      file: example_controller.js
+      class: ExampleController
+  removeLink:
+      file: example_controller.js
+      class: ExampleController
diff --git a/lib/TopologyModels/osm/osm.yaml b/lib/TopologyModels/osm/osm.yaml
new file mode 100644
index 0000000..59ac883
--- /dev/null
+++ b/lib/TopologyModels/osm/osm.yaml
@@ -0,0 +1,187 @@
+name: Osm                            #Model name
+description: Osm                    #Model description
+version: 1.0                            #Model version
+designer: Francesco Lombardo              #Model designer
+nodes:                                  #List of nodes, with id and label used in the gui (We can add more fields)
+    vnf:
+        label: VNF
+    ns_vl:
+        label: VL
+    ns_cp:
+        label: SAP
+    vnf_vl:
+        label: intVL
+    vnf_vdu:
+        label: VDU
+    vnf_ext_cp:
+        label: ExtCP
+    vnf_vdu_cp:
+        label: VduCP
+layer:                                  #List of Layers
+    ns:
+        nodes:                  #List of node to be visualized in the current layer
+            vnf:
+                addable:
+                    callback: addVnf
+                removable:
+                    callback: removeNode
+                expands : vnf
+            ns_vl:
+                addable:
+                    callback: addNode
+                removable:
+                    callback: removeNode
+            ns_cp:
+                addable:
+                    callback: addNode
+                removable:
+                    callback: removeNode
+        allowed_edges:                  #List of allowed edges between the layer's nodes
+            vnf:                        #Edge's source
+                destination:            #List of edge's destination with the list of controls callback id to call when there is a connections
+                    ns_vl:
+                        callback: linkVnftoNsVl
+                        direct_edge: false
+                        removable:
+                            callback: removeLink
+                    ns_cp:
+                        callback: linkVnftoNsCp
+                        direct_edge: false
+                        removable:
+                            callback: removeLink
+            ns_vl:
+                destination:
+                    vnf:
+                        callback: linkVnftoNsVl
+                        direct_edge: false
+                        removable:
+                            callback: removeLink
+                    ns_cp:
+                        callback: nsCpExclusiveConnection
+                        direct_edge: false
+                        removable:
+                            callback: removeLink
+
+            ns_cp:
+                destination:
+                    vnf:
+                        callback: linkVnftoNsCp
+                        direct_edge: false
+                        removable:
+                            callback: removeLink
+                    ns_vl:
+                        callback: nsCpExclusiveConnection
+                        direct_edge: false
+                        removable:
+                            callback: removeLink
+        action:           #Action to show on rightclick all types of node/link
+            node:
+                addToCurrentVNFFG:
+                    title: Add to current VNFFG
+                    callback: addToCurrentVNFFG
+            link:
+
+    vnf:
+        nodes:                  #List of node to be visualized in the current layer
+            vnf_vl:
+                addable:
+                    callback: addNode
+                removable:
+                    callback: removeNode
+            vnf_vdu:
+                addable:
+                    callback: addVnfVdu
+                removable:
+                    callback: removeVnfVdu
+            vnf_ext_cp:
+                addable:
+                    callback: addNode
+                removable:
+                    callback: removeNode
+            vnf_vdu_cp:
+                addable:
+                    callback: addVnfVduCp
+                removable:
+                    callback: removeVnfVduCp
+        allowed_edges:                  #List of allowed edges between the layer's nodes
+            vnf_vl:                        #Edge's source
+                destination:            #List of edge's destination with the list of controls callback id to call when there is a connections
+                    vnf_vdu_cp:
+                        callback: linkVltoVduCp
+                        direct_edge: false
+                        removable:
+                            callback: removeLink
+                    vnf_ext_cp:
+                        callback: linkVnfVltoExpCp
+                        direct_edge: false
+                        removable:
+                            callback: removeLink
+            vnf_ext_cp:
+                destination:
+                    vnf_vl:
+                        callback: linkVnfVltoExpCp
+                        direct_edge: false
+                        removable:
+                            callback: removeLink
+            vnf_vdu_cp:
+                destination:
+                    vnf_vl:
+                        callback: linkVltoVduCp
+                        direct_edge: false
+                        removable:
+                            callback: removeLink
+
+
+
+callback:                             #List of callbacks used
+  chooseVnfExp:
+      file: osm_controller.js
+      class: OSMController
+  nsCpExclusiveConnection:
+      file: osm_controller.js
+      class: OSMController
+  getVduConnectedToVduCp:
+      file: osm_controller.js
+      class: OSMController
+  addVnf:
+      file: osm_controller.js
+      class: OSMController
+  addNode:
+      file: osm_controller.js
+      class: OSMController
+  addVnfVdu:
+      file: osm_controller.js
+      class: OSMController
+  addVnfVduCp:
+      file: osm_controller.js
+      class: OSMController
+  addLink:
+      file: osm_controller.js
+      class: OSMController
+  linkVnftoNsCp:
+      file: osm_controller.js
+      class: OSMController
+  linkVnftoNsVl:
+      file: osm_controller.js
+      class: OSMController
+  linkVltoVduCp:
+      file: osm_controller.js
+      class: OSMController
+  linkVnfVltoExpCp:
+      file: osm_controller.js
+      class: OSMController
+  removeNode:
+      file: osm_controller.js
+      class: OSMController
+  removeVnfVdu:
+      file: osm_controller.js
+      class: OSMController
+  removeVnfVduCp:
+      file: osm_controller.js
+      class: OSMController
+  removeLink:
+      file: osm_controller.js
+      class: OSMController
+  addToCurrentVNFFG:
+      file: osm_controller.js
+      class: OSMController
diff --git a/lib/__init__.py b/lib/__init__.py
new file mode 100644
index 0000000..bfcc6bf
--- /dev/null
+++ b/lib/__init__.py
@@ -0,0 +1 @@
+__all__ = ["Util", 'etsi']
diff --git a/lib/osm/__init__.py b/lib/osm/__init__.py
new file mode 100644
index 0000000..26d8656
--- /dev/null
+++ b/lib/osm/__init__.py
@@ -0,0 +1 @@
+__all__ = ["osm_parser", "osm_rdcl_graph"]
\ No newline at end of file
diff --git a/lib/osm/osm_parser.py b/lib/osm/osm_parser.py
new file mode 100644
index 0000000..a596405
--- /dev/null
+++ b/lib/osm/osm_parser.py
@@ -0,0 +1,89 @@
+#
+#   Copyright 2017 CNIT - Consorzio Nazionale Interuniversitario per le Telecomunicazioni
+#
+#   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  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 pyaml
+import yaml
+from lib.util import Util
+from lib.parser import Parser
+import logging
+import traceback
+import glob
+import os
+
+logging.basicConfig(level=logging.DEBUG)
+log = logging.getLogger('OsmParser')
+
+class OsmParser(Parser):
+    """Parser methods for osm project type
+
+    """
+
+    def __init__(self):
+        super(OsmParser, self).__init__()
+    
+    @classmethod        
+    def importprojectdir(cls,dir_project, file_type):
+        """Imports all descriptor files under a given folder
+
+        this method is specific for Osm project type
+        """
+
+        project = {
+            'nsd':{},
+
+            'vnfd':{},
+
+            'positions': {}
+        }
+
+
+        for desc_type in project:
+            cur_type_path = os.path.join(dir_project, desc_type.upper())
+            log.debug(cur_type_path)
+            if os.path.isdir(cur_type_path):
+                for file in glob.glob(os.path.join(cur_type_path, '*.'+file_type)):
+                    if file_type == 'json':
+                        project[desc_type][os.path.basename(file).split('.')[0]] = Util.loadjsonfile(file)
+                    elif file_type == 'yaml':
+                        project[desc_type][os.path.basename(file).split('.')[0]] = Util.loadyamlfile(file)
+
+
+        for vertices_file in glob.glob(os.path.join(dir_project, '*.json')):
+            if os.path.basename(vertices_file) == 'vertices.json':
+                project['positions']['vertices'] = Util.loadjsonfile(vertices_file)
+
+        return project
+
+    @classmethod
+    def importprojectfiles(cls, file_dict):
+        """Imports descriptors (extracted from the new project POST)
+
+        The keys in the dictionary are the file types
+        """
+        project = {
+            'nsd':{},
+
+            'vnfd':{},
+
+        }
+        for desc_type in project:
+            if desc_type in file_dict:
+                files_desc_type = file_dict[desc_type]
+                for file in files_desc_type:
+                    project[desc_type][os.path.splitext(file.name)[0]] = json.loads(file.read())
+
+        return project
\ No newline at end of file
diff --git a/lib/osm/osm_rdcl_graph.py b/lib/osm/osm_rdcl_graph.py
new file mode 100644
index 0000000..91b798f
--- /dev/null
+++ b/lib/osm/osm_rdcl_graph.py
@@ -0,0 +1,51 @@
+#
+#   Copyright 2017 CNIT - Consorzio Nazionale Interuniversitario per le Telecomunicazioni
+#
+#   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  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 logging
+import copy
+from lib.rdcl_graph import RdclGraph
+
+logging.basicConfig(level=logging.DEBUG)
+log = logging.getLogger('OsmRdclGraph')
+
+class OsmRdclGraph(RdclGraph):
+    """Operates on the graph representation used for the GUI graph views"""
+
+    def __init__(self):
+        pass
+
+
+    def build_graph_from_project(self, json_project, model={}):
+        """Creates a single graph for a whole project"""
+
+        #print "json_project ",json_project
+        graph_object = {
+            'vertices': [],
+            'edges': [],
+            'graph_parameters': {},
+            'model': model
+        }
+        try:
+            positions = json_project['positions'] if 'positions' in json_project else False
+            log.debug('build graph from project json')
+
+
+        except Exception as e:
+            log.exception('Exception in build_graph_from_project')
+            raise
+
+        return graph_object
diff --git a/lib/osm/osmclient/__init__.py b/lib/osm/osmclient/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/osm/osmclient/__init__.py
diff --git a/lib/osm/osmclient/client.py b/lib/osm/osmclient/client.py
new file mode 100644
index 0000000..48c9815
--- /dev/null
+++ b/lib/osm/osmclient/client.py
@@ -0,0 +1,430 @@
+import requests
+import logging
+import json
+import tarfile
+import yaml
+import pyaml
+import StringIO
+from lib.util import Util
+import hashlib
+import os
+
+logging.basicConfig(level=logging.DEBUG)
+log = logging.getLogger('helper.py')
+
+
+class Client(object):
+    def __init__(self, host=os.getenv('OSM_SERVER', "localhost"), so_port=9999, so_project='admin', ro_host=None, ro_port=9090, **kwargs):
+
+        self._user = 'admin'
+        self._password = 'admin'
+        # self._project = so_project
+        self._project = so_project
+        self._token_endpoint = 'admin/v1/tokens'
+        self._user_endpoint = 'admin/v1/users'
+
+        self._headers = {}
+        self._host = host
+
+        self._base_path = "https://{0}:{1}/osm".format(self._host, so_port)
+
+    def get_token(self):
+        postfields_dict = {'username': self._user,
+                           'password': self._password,
+                           'project-id': self._project}
+        token_url = "{0}/{1}".format(self._base_path, self._token_endpoint)
+        token = self._send_post(token_url, None, postfields_dict, headers={"Content-Type": "application/yaml", "accept": "application/json"})
+        if token is not None:
+            return token['id']
+        return None
+
+    def vim_list(self):
+        token = self.get_token()
+        if token:
+            self._headers['Authorization'] = 'Bearer {}'.format(token)
+            self._headers['accept'] = 'application/json'
+            _url = "{0}/admin/v1/vims".format(self._base_path)
+            return self._send_get(_url, headers=self._headers)
+
+    def vim_delete(self, id):
+        token = self.get_token()
+        if token:
+            self._headers['Authorization'] = 'Bearer {}'.format(token)
+            self._headers['accept'] = 'application/json'
+            _url = "{0}/admin/v1/vims/{1}".format(self._base_path, id)
+            return self._send_delete(_url, headers=self._headers)
+        return None
+
+    def vim_get(self, id):
+        token = self.get_token()
+        if token:
+            self._headers['Authorization'] = 'Bearer {}'.format(token)
+            self._headers['accept'] = 'application/json'
+            _url = "{0}/admin/v1/vims/{1}".format(self._base_path, id)
+            return self._send_get(_url, headers=self._headers)
+        return None
+
+    def vim_create(self, vim_data):
+        token = self.get_token()
+        headers = {}
+        if token:
+            headers['Authorization'] = 'Bearer {}'.format(token)
+            headers['Content-Type'] = 'application/json'
+            headers['accept'] = 'application/json'
+
+            _url = "{0}/admin/v1/vims".format(self._base_path)
+            return self._send_post(_url, headers=headers,
+                                  json=vim_data)
+        return None
+
+    def nsd_list(self):
+        token = self.get_token()
+        if token:
+            self._headers['Authorization'] = 'Bearer {}'.format(token)
+            self._headers['Content-Type'] = 'application/yaml'
+            self._headers['accept'] = 'application/json'
+            _url = "{0}/nsd/v1/ns_descriptors_content".format(self._base_path)
+            return self._send_get(_url, headers=self._headers)
+        return None
+
+    def nsd_get(self, id):
+        token = self.get_token()
+        if token:
+            self._headers['Authorization'] = 'Bearer {}'.format(token)
+            self._headers['Content-Type'] = 'application/yaml'
+            #self._headers['accept'] = 'application/json'
+            _url = "{0}/nsd/v1/ns_descriptors/{1}/nsd".format(self._base_path,id)
+            return yaml.load(self._send_get(_url, headers=self._headers))
+        return None
+
+    def nsd_delete(self, id):
+        token = self.get_token()
+        if token:
+            self._headers['Authorization'] = 'Bearer {}'.format(token)
+            self._headers['Content-Type'] = 'application/yaml'
+            self._headers['accept'] = 'application/json'
+            _url = "{0}/nsd/v1/ns_descriptors_content/{1}".format(self._base_path, id)
+            return self._send_delete(_url, headers=self._headers)
+        return None
+
+    def _descriptor_update(self, tarf, data):
+        print tarf.getnames()
+        # extract the package on a tmp directory
+        tarf.extractall('/tmp')
+
+        for name in tarf.getnames():
+            if name.endswith(".yaml") or name.endswith(".yml"):
+                with open('/tmp/' + name, 'w') as outfile:
+                    yaml.safe_dump(data, outfile, default_flow_style=False)
+                break
+
+        tarf_temp = tarfile.open('/tmp/' + tarf.getnames()[0] + ".tar.gz", "w:gz")
+        # tarf_temp = tarfile.open("pippo.tar.gz", "w:gz")
+        print tarf_temp.getnames()
+        # tarf_temp.add('/tmp/'+tarf.getnames()[0])
+        for tarinfo in tarf:
+            # if tarinfo.name.startswith(tarf.getnames()[0]):
+            #    new_name = tarinfo.name[len(tarf.getnames()[0]):]
+            tarf_temp.add('/tmp/' + tarinfo.name, tarinfo.name, recursive=False)
+        print tarf_temp.getnames()
+        tarf_temp.close()
+        return tarf
+
+    def nsd_update(self, id, data):
+        token = self.get_token()
+        headers = {}
+        if token:
+            # get the package onboarded
+            tar_pkg = self.get_nsd_pkg(id)
+            tarf = tarfile.open(fileobj=tar_pkg)
+
+            tarf = self._descriptor_update(tarf, data)
+            headers['Authorization'] = 'Bearer {}'.format(token)
+            headers['Content-Type'] = 'application/gzip'
+            headers['accept'] = 'application/json'
+            headers['Content-File-MD5'] = self.md5(open('/tmp/' + tarf.getnames()[0] + ".tar.gz", 'rb'))
+            #headers['Content-File-MD5'] = self.md5(open("pippo.tar.gz", 'rb'))
+
+            _url = "{0}/nsd/v1/ns_descriptors/{1}/nsd_content".format(self._base_path, id)
+            return self._send_put(_url, headers=headers, data=open('/tmp/'+tarf.getnames()[0] + ".tar.gz", 'rb'))
+            #return self._send_put(_url, headers=headers, data=open("pippo.tar.gz", 'rb'))
+
+        return None
+
+    def nsd_onboard(self, package):
+        token = self.get_token()
+        headers = {}
+        if token:
+
+            headers['Authorization'] = 'Bearer {}'.format(token)
+            headers['Content-Type'] = 'application/gzip'
+            headers['accept'] = 'application/json'
+            with open('/tmp/'+package.name, 'wb+') as destination:
+                for chunk in package.chunks():
+                    destination.write(chunk)
+            headers['Content-File-MD5'] = self.md5(open('/tmp/'+package.name, 'rb'))
+
+            _url = "{0}/nsd/v1/ns_descriptors_content/".format(self._base_path)
+            return self._send_post(_url, headers=headers,
+                                  data=open('/tmp/'+package.name, 'rb'))
+        return None
+
+    def nsd_artifacts(self, id):
+        token = self.get_token()
+        if token:
+            self._headers['Authorization'] = 'Bearer {}'.format(token)
+            self._headers['Content-Type'] = 'application/yaml'
+            self._headers['accept'] = 'text/plain'
+            _url = "{0}/nsd/v1/ns_descriptors/{1}/artifacts".format(self._base_path, id)
+            return self._send_get(_url, headers=self._headers)
+        return None
+
+    def ns_list(self):
+        token = self.get_token()
+        if token:
+            self._headers['Authorization'] = 'Bearer {}'.format(token)
+            self._headers['Content-Type'] = 'application/yaml'
+            self._headers['accept'] = 'application/json'
+            _url = "{0}/nslcm/v1/ns_instances_content".format(self._base_path)
+            return self._send_get(_url, headers=self._headers)
+        return None
+
+    def ns_create(self, ns_data):
+        token = self.get_token()
+        headers = {}
+        if token:
+            headers['Authorization'] = 'Bearer {}'.format(token)
+            headers['Content-Type'] = 'application/yaml'
+            headers['accept'] = 'application/json'
+
+            _url = "{0}/nslcm/v1/ns_instances_content".format(self._base_path)
+            return self._send_post(_url, headers=headers,
+                                  json=ns_data)
+        return None
+
+    def ns_get(self, id):
+        token = self.get_token()
+        if token:
+            self._headers['Authorization'] = 'Bearer {}'.format(token)
+            self._headers['Content-Type'] = 'application/json'
+            self._headers['accept'] = 'application/json'
+            _url = "{0}/nslcm/v1/ns_instances_content/{1}".format(self._base_path, id)
+            return self._send_get(_url, headers=self._headers)
+        return None
+
+    def ns_delete(self, id):
+        token = self.get_token()
+        if token:
+            self._headers['Authorization'] = 'Bearer {}'.format(token)
+            #self._headers['Content-Type'] = 'application/yaml'
+            self._headers['accept'] = 'application/json'
+            _url = "{0}/nslcm/v1/ns_instances_content/{1}".format(self._base_path, id)
+            return self._send_delete(_url, headers=self._headers)
+        return None
+
+    def ns_action(self, id, action_payload):
+        token = self.get_token()
+        headers = {}
+        if token:
+            headers['Authorization'] = 'Bearer {}'.format(token)
+            headers['Content-Type'] = 'application/json'
+            headers['accept'] = 'application/json'
+
+            _url = "{0}/nslcm/v1/ns_instances/{1}/action".format(self._base_path, id)
+            return self._send_post(_url, headers=headers,
+                                   json=action_payload)
+        return None
+
+    def vnfd_list(self):
+        token = self.get_token()
+        if token:
+            self._headers['Authorization'] = 'Bearer {}'.format(token)
+            self._headers['Content-Type'] = 'application/yaml'
+            self._headers['accept'] = 'application/json'
+            _url = "{0}/vnfpkgm/v1/vnf_packages_content".format(self._base_path)
+            return self._send_get(_url, headers=self._headers)
+        return None
+
+    def vnfd_get(self, id):
+        token = self.get_token()
+        if token:
+            self._headers['Authorization'] = 'Bearer {}'.format(token)
+            self._headers['Content-Type'] = 'application/yaml'
+            #self._headers['accept'] = 'application/yaml'
+            _url = "{0}/vnfpkgm/v1/vnf_packages/{1}/vnfd".format(self._base_path, id)
+            return yaml.load(self._send_get(_url, headers=self._headers))
+        return None
+
+    def vnfd_delete(self, id):
+        token = self.get_token()
+        if token:
+            self._headers['Authorization'] = 'Bearer {}'.format(token)
+            self._headers['Content-Type'] = 'application/yaml'
+            self._headers['accept'] = 'application/json'
+            _url = "{0}/vnfpkgm/v1/vnf_packages_content/{1}".format(self._base_path, id)
+            return self._send_delete(_url, headers=self._headers)
+        return None
+
+    def vnfd_update(self, id, data):
+        token = self.get_token()
+        headers = {}
+        if token:
+            # get the package onboarded
+            tar_pkg = self.get_vnfd_pkg(id)
+            tarf = tarfile.open(fileobj=tar_pkg)
+
+            tarf = self._descriptor_update(tarf, data)
+            headers['Authorization'] = 'Bearer {}'.format(token)
+            headers['Content-Type'] = 'application/gzip'
+            headers['accept'] = 'application/json'
+            headers['Content-File-MD5'] = self.md5(open('/tmp/' + tarf.getnames()[0] + ".tar.gz", 'rb'))
+            # headers['Content-File-MD5'] = self.md5(open("pippo.tar.gz", 'rb'))
+
+            _url = "{0}/vnfpkgm/v1/vnf_packages/{1}/package_content".format(self._base_path, id)
+            return self._send_put(_url, headers=headers, data=open('/tmp/' + tarf.getnames()[0] + ".tar.gz", 'rb'))
+            # return self._send_put(_url, headers=headers, data=open("pippo.tar.gz", 'rb'))
+
+        return None
+
+    def vnfd_onboard(self, package):
+        token = self.get_token()
+        headers = {}
+        if token:
+            headers['Authorization'] = 'Bearer {}'.format(token)
+            headers['Content-Type'] = 'application/gzip'
+            headers['accept'] = 'application/json'
+            with open('/tmp/'+package.name, 'wb+') as destination:
+                for chunk in package.chunks():
+                    destination.write(chunk)
+            headers['Content-File-MD5'] = self.md5(open('/tmp/'+package.name, 'rb'))
+            _url = "{0}/vnfpkgm/v1/vnf_packages_content".format(self._base_path)
+            return self._send_post(_url, headers=headers,
+                                   data=open('/tmp/' + package.name, 'rb'))
+        return None
+    def vnf_packages_artifacts(self, id):
+        token = self.get_token()
+        if token:
+            self._headers['Authorization'] = 'Bearer {}'.format(token)
+            self._headers['Content-Type'] = 'application/yaml'
+            _url = "{0}/vnfpkgm/v1/vnf_packages/{1}/artifacts".format(self._base_path, id)
+            return self._send_get(_url, headers=self._headers)
+        return None
+
+    def _upload_package(self, filename, package):
+        token = self.get_token()
+        headers = {}
+        if token:
+            headers['Authorization'] = 'Bearer {}'.format(token)
+            headers['Content-Type'] = 'application/gzip'
+            headers['Content-File-MD5'] = self.md5(package)
+            headers['accept'] = 'application/json'
+        return None
+
+    def _send_post(self, url, data=None, json=None, **kwargs):
+        try:
+            r = requests.post(url, data=data, json=json, verify=False, **kwargs)
+            #print r.text
+        except Exception as e:
+            log.exception(e)
+            #print "Exception during send POST"
+            return {'error': 'error during connection to agent'}
+        return Util.json_loads_byteified(r.text)
+
+    def _send_put(self, url, data=None, json=None, **kwargs):
+        try:
+            r = requests.put(url, data=data, json=json, verify=False, **kwargs)
+            print r.text
+        except Exception as e:
+            log.exception(e)
+            #print "Exception during send PUT"
+            return {'error': 'error during connection to agent'}
+
+        return r.json
+
+    def _send_get(self, url, params=None, **kwargs):
+        try:
+            r = requests.get(url, params=None, verify=False, stream=True, **kwargs)
+            #print r.headers
+        except Exception as e:
+            log.exception(e)
+            #print "Exception during send GET"
+            return {'error': 'error during connection to agent'}
+        if 'accept' in kwargs['headers']:
+            accept = kwargs['headers']['accept']
+            if accept == 'application/json':
+                #print "json"
+                return Util.json_loads_byteified(r.text)
+            elif accept == 'application/zip':
+                tarf =StringIO.StringIO(r.content)
+                #tarf = tarfile.open(fileobj=StringIO.StringIO(r.content))
+                # for tarinfo in tarf:
+                #     #print(tarinfo.name, "is", tarinfo.size, "bytes in size and is")
+                #     if tarinfo.isreg():
+                #         #print("a regular file.")
+                #     elif tarinfo.isdir():
+                #         #print("a directory.")
+                #     else:
+                #         #print("something else.")
+                return tarf
+            else:
+                return r.text
+        else:
+            return r.text
+
+    def _send_delete(self, url, params=None, **kwargs):
+        try:
+            r = requests.delete(url, params=None, verify=False, **kwargs)
+            len(r.content)
+            print r.text
+        except Exception as e:
+            log.exception(e)
+            print "Exception during send DELETE"
+            return {'error': 'error during connection to agent'}
+        return r.json
+
+    def md5(self, f):
+        hash_md5 = hashlib.md5()
+        for chunk in iter(lambda: f.read(1024), b""):
+            hash_md5.update(chunk)
+        return hash_md5.hexdigest()
+
+    def get_nsd_pkg(self, id):
+        token = self.get_token()
+        if token:
+            self._headers['Authorization'] = 'Bearer {}'.format(token)
+            #self._headers['Content-Type'] = 'application/yaml'
+            self._headers['accept'] = 'application/zip'
+            _url = "{0}/nsd/v1/ns_descriptors/{1}/nsd_content".format(self._base_path, id)
+            return self._send_get(_url, headers=self._headers)
+        return None
+
+    def get_vnfd_pkg(self, id):
+        token = self.get_token()
+        if token:
+            self._headers['Authorization'] = 'Bearer {}'.format(token)
+            #self._headers['Content-Type'] = 'application/yaml'
+            self._headers['accept'] = 'application/zip'
+            _url = "{0}/vnfpkgm/v1/vnf_packages/{1}/package_content".format(self._base_path, id)
+            return self._send_get(_url, headers=self._headers)
+        return None
+
+
+
+if __name__ == '__main__':
+
+
+    client = Client()
+    package = client.get_nsd_pkg('be489dfb-5f15-48c1-b693-67d830c591e5')
+    tarf = tarfile.open(fileobj=package)
+    tarf.extractall('/tmp')
+    yaml_object = yaml.safe_dump({}, default_flow_style=False)
+    yaml_file = open('/tmp/cirros_2vnf_ns/cirros_2vnf_nsd.yaml', 'w')
+    yaml_object = pyaml.dump(yaml_object, yaml_file, safe=True)
+    tarf_temp = tarfile.open(tarf.getnames()[0]+".tar.gz", "w:gz")
+
+    for tarinfo in tarf:
+        tarf_temp.add('/tmp/'+tarinfo.name, tarinfo.name)
+    tarf_temp.close()
+
+
+
diff --git a/lib/parser.py b/lib/parser.py
new file mode 100644
index 0000000..1b764d2
--- /dev/null
+++ b/lib/parser.py
@@ -0,0 +1,46 @@
+#
+#   Copyright 2017 CNIT - Consorzio Nazionale Interuniversitario per le Telecomunicazioni
+#
+#   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  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 pyaml
+import yaml
+from lib.util import Util
+import logging
+import traceback
+import glob
+import os
+
+
+class Parser(object):
+    """Parser methods base class
+
+    """
+
+    def __init__(self):
+        pass
+    
+    @classmethod        
+    def importprojectdir(cls,dir_project, type):
+        """Imports all files under a given folder
+
+        Returns an empty project
+        """
+
+        project = {}
+        return project
+
+    def get_all_ns_descriptors(self, nsd_id, project_data):
+        raise NotImplementedError
\ No newline at end of file
diff --git a/lib/rdcl_graph.py b/lib/rdcl_graph.py
new file mode 100644
index 0000000..9d96251
--- /dev/null
+++ b/lib/rdcl_graph.py
@@ -0,0 +1,82 @@
+#
+#   Copyright 2017 CNIT - Consorzio Nazionale Interuniversitario per le Telecomunicazioni
+#
+#   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  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 logging
+import copy
+
+logging.basicConfig(level=logging.DEBUG)
+log = logging.getLogger('RdclGraph')
+
+
+class RdclGraph(object):
+    """ Operates on the graph representation used for the GUI graph views """
+
+    node_t3d_base = {
+        'info': {
+            'property': {
+                'custom_label': '',
+            },
+            'type': '',
+            'group': []
+        }
+    }
+
+    def __init__(self):
+        pass
+
+    def add_link(self, source, target, view, group, graph_object, optional={}):
+        if (source is None) or (target is None):
+            return
+        edge_obj = {
+            'source': source,
+            'target': target,
+            'view': view,
+            'group': [group],
+
+        }
+
+        edge_obj.update(optional)
+        if edge_obj not in graph_object['edges']:
+            graph_object['edges'].append(edge_obj)
+
+    def add_node(self, id, type, group, positions, graph_object, optional={}):
+        if id is None:
+            return
+        node = next((x for x in graph_object['vertices'] if x['id'] == id), None)
+        if node is not None:
+            node['info']['group'].append(group)
+        else:
+            node = copy.deepcopy(self.node_t3d_base)
+            node['id'] = id
+            node['info']['type'] = type
+            if group is not None:
+                node['info']['group'].append(group)
+            if positions and id in positions['vertices'] and 'x' in positions['vertices'][id] and 'y' in positions['vertices'][id]:
+                node['fx'] = positions['vertices'][id]['x']
+                node['fy'] = positions['vertices'][id]['y']
+            node['info'].update(optional)
+            graph_object['vertices'].append(node)
+
+    def is_directed_edge(self, source_type=None, target_type=None, layer=None, model={}):
+        if source_type is None or target_type is None or layer is None:
+            return None
+        if layer in model['layer'] and 'allowed_edges' in model['layer'][layer]:
+            if source_type in model['layer'][layer]['allowed_edges'] and target_type in model['layer'][layer]['allowed_edges'][source_type]['destination']:
+                edge_pro = model['layer'][layer]['allowed_edges'][source_type]['destination'][target_type]
+                return edge_pro['direct_edge'] if 'direct_edge' in edge_pro else False
+
+        return None
diff --git a/lib/util.py b/lib/util.py
new file mode 100644
index 0000000..71fc36b
--- /dev/null
+++ b/lib/util.py
@@ -0,0 +1,198 @@
+#
+#   Copyright 2017 CNIT - Consorzio Nazionale Interuniversitario per le Telecomunicazioni
+#
+#   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  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 pyaml
+import logging
+import jsonschema
+import uuid 
+
+_lib_name = 'Util'
+
+
+logging.basicConfig(level=logging.DEBUG)
+log = logging.getLogger('lib/util.py')
+
+
+class Util(object):
+
+    def __init__(self):
+        # logging.basicConfig(level=logging.DEBUG)
+        # self.log = logging.getLogger('UtilLogger')
+        pass
+
+
+    @classmethod
+    def json_load_byteified(cls, file_handle):
+        return cls._byteify(
+            json.load(file_handle, object_hook=cls._byteify),
+            ignore_dicts=True
+        )
+
+    @classmethod
+    def json_loads_byteified(cls, json_text):
+        return cls._byteify(
+            json.loads(json_text, object_hook=cls._byteify),
+            ignore_dicts=True
+        )
+
+    @classmethod
+    def _byteify(cls, data, ignore_dicts = False):
+        # if this is a unicode string, return its string representation
+        if isinstance(data, unicode):
+            return data.encode('utf-8')
+        # if this is a list of values, return list of byteified values
+        if isinstance(data, list):
+            return [ cls._byteify(item, ignore_dicts=True) for item in data ]
+        # if this is a dictionary, return dictionary of byteified keys and values
+        # but only if we haven't already byteified it
+        if isinstance(data, dict) and not ignore_dicts:
+            return {
+                cls._byteify(key, ignore_dicts=True): cls._byteify(value, ignore_dicts=True)
+                for key, value in data.iteritems()
+            }
+        # if it's anything else, return it in its original form
+        return data
+
+    @classmethod
+    def yaml2json(cls, object_yaml):
+        """Converts a yaml object into a json representation"""
+        log.debug('yaml2json')
+        return json.dumps(object_yaml, sort_keys=True, indent=2) if not object_yaml is None else None
+
+    @classmethod
+    def json2yaml(cls, object_json):
+        """Converts a json object into a yaml representation"""
+        log.debug('json2yaml')
+        return yaml.safe_dump(object_json, default_flow_style=False) if not object_json is None else None
+
+    @classmethod
+    def openfile(cls, filepath, mode='r', buffering=1):
+        """Returns an open file given a filepath
+
+        If the filepath is already an open file, returns into
+        Raises Exception
+        """
+
+        log.debug('reading file ' + filepath)
+        try:
+            if isinstance(filepath, file):
+                return filepath
+            else:
+                return open(filepath, mode, buffering)
+
+        except IOError as e:
+            log.exception('openfile', e)
+            raise
+
+    @classmethod
+    def loadyamlfile(cls, name):
+        """Returns a yaml object from a filename or an open file
+
+        Raises Exception
+        """
+
+        yaml_object = None
+        try:
+            if isinstance(name, file):
+                yaml_object = yaml.load(name)
+            else:
+                yaml_file = cls.openfile(name)
+                yaml_object = yaml.load(yaml_file)
+
+            return yaml_object
+        except Exception as e:
+            log.exception('Exception loadYamlFile', e)
+            raise
+
+    @classmethod
+    def loadjsonfile(cls, name):
+        """Returns a json object from a filename or an open file
+
+        Raises Exception
+        """
+
+        json_object = None
+        try:
+            #raise IOError('error from throws')
+            if isinstance(name, file):
+                json_object = json.load(name)
+            else:
+                # json_file = self.openfile(name)
+                json_file = cls.openfile(name)
+                json_object = json.load(json_file)
+
+            return json_object
+        except Exception as e:
+            log.exception('Exception loadJsonFile', e)
+            raise
+
+    @classmethod
+    def writejsonfile(cls, name, json_object):
+        """Writes the dump of a json obj to a filename or an open file
+
+        Raises Exception
+        """
+
+        try:
+            log.debug('writejsonfile ' + name)
+            if isinstance(name, file):
+                json_object = json.dump(json_object, name)
+            else:
+                json_file = cls.openfile(name, 'w')
+                json_object = json.dump(json_object, json_file,separators=(',',': '), indent=4)
+        except Exception as e:
+            log.exception('Exception writejsonfile', e)
+            raise
+
+    @classmethod
+    def writeyamlfile(cls, name, yaml_object):
+        """Writes the dump of a yaml obj to a filename or an open file
+
+        Raises Exception
+        """
+
+        try:
+            log.debug('writeyamlfile ' + name)
+            if isinstance(name, file):
+                yaml_object = pyaml.dump(yaml_object, name, safe=True)
+            else:
+                yaml_file = cls.openfile(name, 'w')
+                yaml_object = pyaml.dump(yaml_object, yaml_file, safe=True)
+        except Exception as e:
+            log.exception('Exception writeyamlfile')
+            raise
+
+    @classmethod
+    def validate_json_schema(cls, reference_schema, data):
+        """Validates a json data against a json schema
+
+        Raises Exception
+        """
+
+        try:
+            # schema = cls.loadjsonfile("lib/etsi/schemas/"+type_descriptor+".json")
+             #print 'type_descriptor : '+type_descriptor
+            jsonschema.validate(data, reference_schema)
+            return True
+        except Exception as e:
+            log.exception('Exception validate json schema', e)
+            return False
+
+    @classmethod
+    def get_unique_id(cls):
+        return uuid.uuid4().hex[:6].upper()