7e266fa2b7f5c51dd710b864412c3e75f91a76b5
[nepi.git] / src / nepi / util / sfarspec_proc.py
1 #
2 #    NEPI, a framework to manage network experiments
3 #    Copyright (C) 2013 INRIA
4 #
5 #    This program is free software: you can redistribute it and/or modify
6 #    it under the terms of the GNU General Public License as published by
7 #    the Free Software Foundation, either version 3 of the License, or
8 #    (at your option) any later version.
9 #
10 #    This program is distributed in the hope that it will be useful,
11 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 #    GNU General Public License for more details.
14 #
15 #    You should have received a copy of the GNU General Public License
16 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 #
18
19 from nepi.util.logger import Logger
20 try:
21     from sfa.rspecs.rspec import RSpec
22     from sfa.util.xrn import Xrn, get_leaf, get_authority, hrn_to_urn, urn_to_hrn
23 except ImportError:
24     log = Logger("SFA RSpec Processing")
25     log.debug("Package sfa-common not installed.\
26          Could not import sfa.rspecs.rspec and sfa.util.xrn")
27
28 from types import StringTypes, ListType
29
30
31 class SfaRSpecProcessing(object):
32     """
33     Class to process SFA RSpecs, parse the RSpec replies such as Advertisement RSpecs,
34     and build in the case of Request RSpecs.
35     """
36     def __init__(self, config=None):
37         self._log = Logger("SFA RSpec Processing")
38         self.config = config 
39
40     def make_dict_rec(self, obj):
41         if not obj or isinstance(obj, (StringTypes, bool)):
42             return obj
43         if isinstance(obj, list):
44             objcopy = []
45             for x in obj:
46                 objcopy.append(self.make_dict_rec(x))
47             return objcopy
48         # We thus suppose we have a child of dict
49         objcopy = {}
50         for k, v in obj.items():
51             objcopy[k] = self.make_dict_rec(v)
52         return objcopy
53
54     def parse_sfa_rspec(self, rspec_string):
55         """
56         Parse the RSpec XML as a string.
57         """
58         # rspec_type and rspec_version should be set in the config of the platform,
59         # we use GENIv3 as default one if not
60         if self.config:
61             if 'rspec_type' and 'rspec_version' in self.config:
62                 rspec_version = self.config['rspec_type'] + ' ' + self.config['rspec_version']
63         else:
64             rspec_version = 'GENI 3'
65         self._log.debug(rspec_version)
66         rspec = RSpec(rspec_string, version=rspec_version)
67         
68         try:
69             nodes = rspec.version.get_nodes()
70         except Exception, e:
71             self._log.warn("Could not retrieve nodes in RSpec: %s" % e)
72         try:
73             leases = rspec.version.get_leases()
74         except Exception, e:
75             self._log.warn("Could not retrieve leases in RSpec: %s" % e)
76         try:
77             links = rspec.version.get_links()
78         except Exception, e:
79             self._log.warn("Could not retrieve links in RSpec: %s" % e)
80         try:
81             channels = rspec.version.get_channels()
82         except Exception, e:
83             self._log.warn("Could not retrieve channels in RSpec: %s" % e)
84   
85         resources = [] 
86         # Extend object and Format object field's name
87         for node in nodes:
88             node['type'] = 'node'
89             node['network_hrn'] = Xrn(node['component_id']).authority[0] # network ? XXX
90             node['hrn'] = urn_to_hrn(node['component_id'])[0]
91             node['urn'] = node['component_id']
92             node['hostname'] = node['component_name']
93             node['initscripts'] = node.pop('pl_initscripts')
94             if 'exclusive' in node and node['exclusive']:
95                 node['exclusive'] = node['exclusive'].lower() == 'true'
96  
97             # XXX This should use a MAP as before
98             if 'position' in node: # iotlab
99                 node['x'] = node['position']['posx']
100                 node['y'] = node['position']['posy']
101                 node['z'] = node['position']['posz']
102                 del node['position']
103  
104             if 'location' in node:
105                 if node['location']:
106                     node['latitude'] = node['location']['latitude']
107                     node['longitude'] = node['location']['longitude']
108                 del node['location']
109  
110             # Flatten tags
111             if 'tags' in node:
112                 if node['tags']:
113                     for tag in node['tags']:
114                         node[tag['tagname']] = tag['value']
115                 del node['tags']
116  
117             
118             # We suppose we have children of dict that cannot be serialized
119             # with xmlrpc, let's make dict
120             resources.append(self.make_dict_rec(node))
121  
122         # NOTE a channel is a resource and should not be treated independently
123         #     resource
124         #        |
125         #   +----+------+-------+
126         #   |    |      |       |
127         # node  link  channel  etc.
128         #resources.extend(nodes)
129         #resources.extend(channels)
130  
131         return {'resource': resources, 'lease': leases } 
132 #               'channel': channels \
133 #               }
134
135  
136     def build_sfa_rspec(self, slice_id, resources, properties, leases):
137         """
138         Build the XML RSpec from list of resources' urns.
139         eg. resources = ["urn:publicid:IDN+ple:modenaple+node+planetlab-1.ing.unimo.it"]
140         """
141         #if isinstance(resources, str):
142         #    resources = eval(resources)
143         # rspec_type and rspec_version should be set in the config of the platform,
144         # we use GENIv3 as default one if not
145         if self.config:
146             if 'rspec_type' and 'rspec_version' in self.config:
147                 rspec_version = self.config['rspec_type'] + ' ' + self.config['rspec_version']
148         else:
149             rspec_version = 'GENI 3'
150
151         # extend rspec version with "content_type"
152         rspec_version += ' request'
153         
154         rspec = RSpec(version=rspec_version)
155
156         nodes = []
157         channels = []
158         links = []
159         self._log.debug("Building RSpec for resources %s" % resources)
160         cardinal = 0
161         wilab = False
162         for urn in resources:
163             # XXX TO BE CORRECTED, this handles None values
164             if not urn:
165                 continue
166             self._log.debug(urn)
167             resource = dict()
168             # TODO: take into account the case where we send a dict of URNs without keys
169             #resource['component_id'] = resource.pop('urn')
170             resource['component_id'] = urn
171             resource_hrn, resource_type = urn_to_hrn(resource['component_id'])
172             # build component_manager_id
173             top_auth = resource_hrn.split('.')[0]
174             cm = urn.split("+")
175             resource['component_manager_id'] = "%s+%s+authority+cm" % (cm[0],top_auth)
176
177             if resource_type == 'node':
178                 # XXX dirty hack WiLab !!!
179 #                Commented Lucia, doesn't work for wilabt  
180 #                if self.config:
181 #                    if 'wilab2' in self.config['sm']:
182 #                        resource['client_id'] = "PC"
183 #                        resource['sliver_type'] = "raw-pc"
184                 if 'wilab2' in urn:
185                     wilab = True
186                     resource['client_id'] = "node%s" % cardinal
187                     resource['sliver_type'] = "raw-pc"
188                     resource['disk_image'] = "hola"
189                     top_auth = resource_hrn.replace("\\", "").split('.')
190                     top_auth.pop()
191                     top_auth = '.'.join(top_auth)
192                     cm = urn.split("+")
193                     resource['component_manager_id'] = "%s+%s+authority+cm" % (cm[0],top_auth)
194                     cardinal += 1
195                 nodes.append(resource)
196             elif resource_type == 'link':
197                 links.append(resource)
198             elif resource_type == 'channel':
199                 channels.append(resource)
200             else:
201                 raise Exception, "Not supported type of resource" 
202         
203         rspec.version.add_nodes(nodes, rspec_content_type="request")
204         #rspec.version.add_leases(leases)
205         #rspec.version.add_links(links)
206         #rspec.version.add_channels(channels)
207
208         #self._log.debug("request rspec: %s"%rspec.toxml())
209         string = rspec.toxml()
210         if wilab and properties is not None:
211             ## dirty hack for the f4f demo
212             b = string.split('\n')
213             for i, n in enumerate(b):
214                 if 'sliver_type name="raw-pc"' in n:
215                     b[i] = '<sliver_type name="raw-pc">'
216                     b.insert(i+1, '<disk_image name="urn:publicid:IDN+wall2.ilabt.iminds.be+image+emulab-ops//%s"/>' % properties['disk_image'])
217                     #b.insert(i+1, '<disk_image name="urn:publicid:IDN+wilab2.ilabt.iminds.be+image+nepi:%s"/>' % properties['disk_image'])
218                     b.insert(i+2, '</sliver_type>')
219             string = ''.join(b)
220         self._log.debug("request rspec : %s" % string)
221         return string
222
223