rename src/nepi/ into just nepi/
[nepi.git] / nepi / util / sfarspec_proc.py
diff --git a/nepi/util/sfarspec_proc.py b/nepi/util/sfarspec_proc.py
new file mode 100644 (file)
index 0000000..b131ddc
--- /dev/null
@@ -0,0 +1,222 @@
+#
+#    NEPI, a framework to manage network experiments
+#    Copyright (C) 2013 INRIA
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License version 2 as
+#    published by the Free Software Foundation;
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+from nepi.util.logger import Logger
+try:
+    from sfa.rspecs.rspec import RSpec
+    from sfa.util.xrn import Xrn, get_leaf, get_authority, hrn_to_urn, urn_to_hrn
+except ImportError:
+    log = Logger("SFA RSpec Processing")
+    log.debug("Package sfa-common not installed.\
+         Could not import sfa.rspecs.rspec and sfa.util.xrn")
+
+from six import string_types
+
+
+class SfaRSpecProcessing(object):
+    """
+    Class to process SFA RSpecs, parse the RSpec replies such as Advertisement RSpecs,
+    and build in the case of Request RSpecs.
+    """
+    def __init__(self, config=None):
+        self._log = Logger("SFA RSpec Processing")
+        self.config = config 
+
+    def make_dict_rec(self, obj):
+        if not obj or isinstance(obj, (bool,) + string_types):
+            return obj
+        if isinstance(obj, list):
+            objcopy = []
+            for x in obj:
+                objcopy.append(self.make_dict_rec(x))
+            return objcopy
+        # We thus suppose we have a child of dict
+        objcopy = {}
+        for k, v in obj.items():
+            objcopy[k] = self.make_dict_rec(v)
+        return objcopy
+
+    def parse_sfa_rspec(self, rspec_string):
+        """
+        Parse the RSpec XML as a string.
+        """
+        # rspec_type and rspec_version should be set in the config of the platform,
+        # we use GENIv3 as default one if not
+        if self.config:
+            if 'rspec_type' and 'rspec_version' in self.config:
+                rspec_version = self.config['rspec_type'] + ' ' + self.config['rspec_version']
+        else:
+            rspec_version = 'GENI 3'
+        self._log.debug(rspec_version)
+        rspec = RSpec(rspec_string, version=rspec_version)
+        
+        try:
+            nodes = rspec.version.get_nodes()
+        except Exception as e:
+            self._log.warn("Could not retrieve nodes in RSpec: %s" % e)
+        try:
+            leases = rspec.version.get_leases()
+        except Exception as e:
+            self._log.warn("Could not retrieve leases in RSpec: %s" % e)
+        try:
+            links = rspec.version.get_links()
+        except Exception as e:
+            self._log.warn("Could not retrieve links in RSpec: %s" % e)
+        try:
+            channels = rspec.version.get_channels()
+        except Exception as e:
+            self._log.warn("Could not retrieve channels in RSpec: %s" % e)
+  
+        resources = [] 
+        # Extend object and Format object field's name
+        for node in nodes:
+            node['type'] = 'node'
+            node['network_hrn'] = Xrn(node['component_id']).authority[0] # network ? XXX
+            node['hrn'] = urn_to_hrn(node['component_id'])[0]
+            node['urn'] = node['component_id']
+            node['hostname'] = node['component_name']
+            node['initscripts'] = node.pop('pl_initscripts')
+            if 'exclusive' in node and node['exclusive']:
+                node['exclusive'] = node['exclusive'].lower() == 'true'
+            # XXX This should use a MAP as before
+            if 'position' in node: # iotlab
+                node['x'] = node['position']['posx']
+                node['y'] = node['position']['posy']
+                node['z'] = node['position']['posz']
+                del node['position']
+            if 'location' in node:
+                if node['location']:
+                    node['latitude'] = node['location']['latitude']
+                    node['longitude'] = node['location']['longitude']
+                del node['location']
+            # Flatten tags
+            if 'tags' in node:
+                if node['tags']:
+                    for tag in node['tags']:
+                        node[tag['tagname']] = tag['value']
+                del node['tags']
+            
+            # We suppose we have children of dict that cannot be serialized
+            # with xmlrpc, let's make dict
+            resources.append(self.make_dict_rec(node))
+        # NOTE a channel is a resource and should not be treated independently
+        #     resource
+        #        |
+        #   +----+------+-------+
+        #   |    |      |       |
+        # node  link  channel  etc.
+        #resources.extend(nodes)
+        #resources.extend(channels)
+        return {'resource': resources, 'lease': leases } 
+#               'channel': channels \
+#               }
+
+    def build_sfa_rspec(self, slice_id, resources, properties, leases):
+        """
+        Build the XML RSpec from list of resources' urns.
+        eg. resources = ["urn:publicid:IDN+ple:modenaple+node+planetlab-1.ing.unimo.it"]
+        """
+        #if isinstance(resources, str):
+        #    resources = eval(resources)
+        # rspec_type and rspec_version should be set in the config of the platform,
+        # we use GENIv3 as default one if not
+        if self.config:
+            if 'rspec_type' and 'rspec_version' in self.config:
+                rspec_version = self.config['rspec_type'] + ' ' + self.config['rspec_version']
+        else:
+            rspec_version = 'GENI 3'
+
+        # extend rspec version with "content_type"
+        rspec_version += ' request'
+        
+        rspec = RSpec(version=rspec_version)
+
+        nodes = []
+        channels = []
+        links = []
+        self._log.debug("Building RSpec for resources %s" % resources)
+        cardinal = 0
+        wilab = False
+        for urn in resources:
+            # XXX TO BE CORRECTED, this handles None values
+            if not urn:
+                continue
+            self._log.debug(urn)
+            resource = dict()
+            # TODO: take into account the case where we send a dict of URNs without keys
+            #resource['component_id'] = resource.pop('urn')
+            resource['component_id'] = urn
+            resource_hrn, resource_type = urn_to_hrn(resource['component_id'])
+            # build component_manager_id
+            top_auth = resource_hrn.split('.')[0]
+            cm = urn.split("+")
+            resource['component_manager_id'] = "%s+%s+authority+cm" % (cm[0],top_auth)
+
+            if resource_type == 'node':
+                # XXX dirty hack WiLab !!!
+#                Commented Lucia, doesn't work for wilabt  
+#                if self.config:
+#                    if 'wilab2' in self.config['sm']:
+#                        resource['client_id'] = "PC"
+#                        resource['sliver_type'] = "raw-pc"
+                if 'wilab2' in urn:
+                    wilab = True
+                    resource['client_id'] = "node%s" % cardinal
+                    resource['sliver_type'] = "raw-pc"
+                    resource['disk_image'] = "hola"
+                    top_auth = resource_hrn.replace("\\", "").split('.')
+                    top_auth.pop()
+                    top_auth = '.'.join(top_auth)
+                    cm = urn.split("+")
+                    resource['component_manager_id'] = "%s+%s+authority+cm" % (cm[0],top_auth)
+                    cardinal += 1
+                nodes.append(resource)
+            elif resource_type == 'link':
+                links.append(resource)
+            elif resource_type == 'channel':
+                channels.append(resource)
+            else:
+                raise Exception("Not supported type of resource") 
+        
+        rspec.version.add_nodes(nodes, rspec_content_type="request")
+        #rspec.version.add_leases(leases)
+        #rspec.version.add_links(links)
+        #rspec.version.add_channels(channels)
+
+        #self._log.debug("request rspec: %s"%rspec.toxml())
+        string = rspec.toxml()
+        if wilab and properties is not None:
+            ## dirty hack for the f4f demo
+            b = string.split('\n')
+            for i, n in enumerate(b):
+                if 'sliver_type name="raw-pc"' in n:
+                    b[i] = '<sliver_type name="raw-pc">'
+                    b.insert(i+1, '<disk_image name="urn:publicid:IDN+wall2.ilabt.iminds.be+image+emulab-ops//%s"/>' % properties['disk_image'])
+                    #b.insert(i+1, '<disk_image name="urn:publicid:IDN+wilab2.ilabt.iminds.be+image+nepi:%s"/>' % properties['disk_image'])
+                    b.insert(i+2, '</sliver_type>')
+            string = ''.join(b)
+        self._log.debug("request rspec : %s" % string)
+        return string
+
+