added support for urn name format. urn is the default name format used over the wire
[sfa.git] / sfa / plc / nodes.py
1 ### $Id$
2 ### $URL$
3
4 import os
5 import time
6 import datetime
7 import sys
8 import traceback
9
10 from sfa.util.namespace import *
11 from sfa.util.rspec import *
12 from sfa.util.specdict import * 
13 from sfa.util.faults import *
14 from sfa.util.storage import *
15 from sfa.util.debug import log
16 from sfa.util.rspec import *
17 from sfa.util.specdict import * 
18 from sfa.util.policy import Policy
19 from sfa.server.aggregate import Aggregates 
20
21 class Nodes(SimpleStorage):
22
23     def __init__(self, api, ttl = 1, origin_hrn=None):
24         self.api = api
25         self.ttl = ttl
26         self.threshold = None
27         path = self.api.config.SFA_DATA_DIR
28         filename = ".".join([self.api.interface, self.api.hrn, "nodes"])
29         filepath = path + os.sep + filename
30         self.nodes_file = filepath
31         SimpleStorage.__init__(self, self.nodes_file)
32         self.policy = Policy(api)
33         self.load()
34         self.origin_hrn = origin_hrn
35
36
37     def refresh(self):
38         """
39         Update the cached list of nodes
40         """
41
42         # Reload components list
43         now = datetime.datetime.now()
44         if not self.has_key('threshold') or not self.has_key('timestamp') or \
45            now > datetime.datetime.fromtimestamp(time.mktime(time.strptime(self['threshold'], self.api.time_format))): 
46             if self.api.interface in ['aggregate']:
47                 self.refresh_nodes_aggregate()
48             elif self.api.interface in ['slicemgr']:
49                 self.refresh_nodes_smgr()
50
51     def refresh_nodes_aggregate(self):
52         rspec = RSpec()
53         rspec.parseString(self.get_rspec())
54         
55         # filter nodes according to policy
56         blist = self.policy['node_blacklist']
57         wlist = self.policy['node_whitelist']
58         rspec.filter('NodeSpec', 'name', blacklist=blist, whitelist=wlist)
59
60         # extract ifspecs from rspec to get ips'
61         ips = []
62         ifspecs = rspec.getDictsByTagName('IfSpec')
63         for ifspec in ifspecs:
64             if ifspec.has_key('addr') and ifspec['addr']:
65                 ips.append(ifspec['addr'])
66
67         # extract nodespecs from rspec to get dns names
68         hostnames = []
69         nodespecs = rspec.getDictsByTagName('NodeSpec')
70         for nodespec in nodespecs:
71             if nodespec.has_key('name') and nodespec['name']:
72                 hostnames.append(nodespec['name'])
73
74         # update timestamp and threshold
75         timestamp = datetime.datetime.now()
76         hr_timestamp = timestamp.strftime(self.api.time_format)
77         delta = datetime.timedelta(hours=self.ttl)
78         threshold = timestamp + delta
79         hr_threshold = threshold.strftime(self.api.time_format)
80
81         node_details = {}
82         node_details['rspec'] = rspec.toxml()
83         node_details['ip'] = ips
84         node_details['dns'] = hostnames
85         node_details['timestamp'] = hr_timestamp
86         node_details['threshold'] = hr_threshold
87         # save state 
88         self.update(node_details)
89         self.write()       
90  
91     def get_rspec_smgr(self, xrn = None):
92         hrn, type = urn_to_hrn(xrn)
93         # convert and threshold to ints
94         if self.has_key('timestamp') and self['timestamp']:
95             hr_timestamp = self['timestamp']
96             timestamp = datetime.datetime.fromtimestamp(time.mktime(time.strptime(hr_timestamp, self.api.time_format)))
97             hr_threshold = self['threshold']
98             threshold = datetime.datetime.fromtimestamp(time.mktime(time.strptime(hr_threshold, self.api.time_format)))
99         else:
100             timestamp = datetime.datetime.now()
101             hr_timestamp = timestamp.strftime(self.api.time_format)
102             delta = datetime.timedelta(hours=self.ttl)
103             threshold = timestamp + delta
104             hr_threshold = threshold.strftime(self.api.time_format)
105
106         start_time = int(timestamp.strftime("%s"))
107         end_time = int(threshold.strftime("%s"))
108         duration = end_time - start_time
109
110         aggregates = Aggregates(self.api)
111         rspecs = {}
112         networks = []
113         rspec = RSpec()
114         credential = self.api.getCredential()
115         origin_hrn = self.origin_hrn
116         for aggregate in aggregates:
117           if aggregate not in [self.api.auth.client_cred.get_gid_caller().get_hrn()]:
118             try:
119                 # get the rspec from the aggregate
120                 agg_rspec = aggregates[aggregate].get_resources(credential, xrn, origin_hrn)
121                 # extract the netspec from each aggregates rspec
122                 rspec.parseString(agg_rspec)
123                 networks.extend([{'NetSpec': rspec.getDictsByTagName('NetSpec')}])
124             except:
125                 # XX print out to some error log
126                 print >> log, "Error getting resources at aggregate %s" % aggregate
127                 traceback.print_exc(log)
128                 print >> log, "%s" % (traceback.format_exc())
129         # create the rspec dict
130         resources = {'networks': networks, 'start_time': start_time, 'duration': duration}
131         resourceDict = {'RSpec': resources}
132         # convert rspec dict to xml
133         rspec.parseDict(resourceDict)
134         return rspec.toxml()
135
136     def refresh_nodes_smgr(self):
137
138         rspec = self.get_remote_resources()        
139         # filter according to policy
140         blist = self.policy['node_blacklist']
141         wlist = self.policy['node_whitelist']    
142         rspec.filter('NodeSpec', 'name', blacklist=blist, whitelist=wlist)
143
144         # update timestamp and threshold
145         timestamp = datetime.datetime.now()
146         hr_timestamp = timestamp.strftime(self.api.time_format)
147         delta = datetime.timedelta(hours=self.ttl)
148         threshold = timestamp + delta
149         hr_threshold = threshold.strftime(self.api.time_format)
150
151         nodedict = {'rspec': rspec.toxml(),
152                     'timestamp': hr_timestamp,
153                     'threshold':  hr_threshold}
154
155         self.update(nodedict)
156         self.write()
157
158     def get_rspec(self, xrn = None):
159
160         if self.api.interface in ['slicemgr']:
161             return self.get_rspec_smgr(xrn)
162         elif self.api.interface in ['aggregate']:
163             return self.get_rspec_aggregate(xrn)     
164
165     def get_rspec_aggregate(self, xrn = None):
166         """
167         Get resource information from PLC
168         """
169         hrn, type = urn_to_hrn(xrn)
170         slicename = None
171         # Get the required nodes
172         if not hrn:
173             nodes = self.api.plshell.GetNodes(self.api.plauth, {'peer_id': None})
174             try:  linkspecs = self.api.plshell.GetLinkSpecs() # if call is supported
175             except:  linkspecs = []
176         else:
177             slicename = hrn_to_pl_slicename(hrn)
178             slices = self.api.plshell.GetSlices(self.api.plauth, [slicename])
179             if not slices:
180                 nodes = []
181             else:
182                 slice = slices[0]
183                 node_ids = slice['node_ids']
184                 nodes = self.api.plshell.GetNodes(self.api.plauth, {'peer_id': None, 'node_id': node_ids})
185
186         # Filter out whitelisted nodes
187         public_nodes = lambda n: n.has_key('slice_ids_whitelist') and not n['slice_ids_whitelist']
188             
189         # ...only if they are not already assigned to this slice.
190         if (not slicename):        
191             nodes = filter(public_nodes, nodes)
192
193         # Get all network interfaces
194         interface_ids = []
195         for node in nodes:
196             # The field name has changed in plcapi 4.3
197             if self.api.plshell_version in ['4.2']:
198                 interface_ids.extend(node['nodenetwork_ids'])
199             elif self.api.plshell_version in ['4.3']:
200                 interface_ids.extend(node['interface_ids'])
201             else:
202                 raise SfaAPIError, "Unsupported plcapi version ", \
203                                  self.api.plshell_version
204
205         if self.api.plshell_version in ['4.2']:
206             interfaces = self.api.plshell.GetNodeNetworks(self.api.plauth, interface_ids)
207         elif self.api.plshell_version in ['4.3']:
208             interfaces = self.api.plshell.GetInterfaces(self.api.plauth, interface_ids)
209         else:
210             raise SfaAPIError, "Unsupported plcapi version ", \
211                                 self.api.plshell_version 
212         interface_dict = {}
213         for interface in interfaces:
214             if self.api.plshell_version in ['4.2']:
215                 interface_dict[interface['nodenetwork_id']] = interface
216             elif self.api.plshell_version in ['4.3']:
217                 interface_dict[interface['interface_id']] = interface
218             else:
219                 raise SfaAPIError, "Unsupported plcapi version", \
220                                     self.api.plshell_version 
221
222         # join nodes with thier interfaces
223         for node in nodes:
224             node['interfaces'] = []
225             if self.api.plshell_version in ['4.2']:
226                 for nodenetwork_id in node['nodenetwork_ids']:
227                     node['interfaces'].append(interface_dict[nodenetwork_id])
228             elif self.api.plshell_version in ['4.3']:
229                 for interface_id in node['interface_ids']:
230                     node['interfaces'].append(interface_dict[interface_id])
231             else:
232                 raise SfaAPIError, "Unsupported plcapi version", \
233                                     self.api.plshell_version
234
235         # convert and threshold to ints
236         if self.has_key('timestamp') and self['timestamp']:
237             timestamp = datetime.datetime.fromtimestamp(time.mktime(time.strptime(self['timestamp'], self.api.time_format)))
238             threshold = datetime.datetime.fromtimestamp(time.mktime(time.strptime(self['threshold'], self.api.time_format)))
239         else:
240             timestamp = datetime.datetime.now()
241             delta = datetime.timedelta(hours=self.ttl)
242             threshold = timestamp + delta
243
244         start_time = int(timestamp.strftime("%s"))
245         end_time = int(threshold.strftime("%s"))
246         duration = end_time - start_time
247
248         # create the plc dict
249         networks = [{'nodes': nodes,
250                      'name': self.api.hrn,
251                      'start_time': start_time,
252                      'duration': duration}]
253         if not hrn:
254             networks[0]['links'] = linkspecs
255         resources = {'networks': networks, 'start_time': start_time, 'duration': duration}
256
257         # convert the plc dict to an rspec dict
258         resourceDict = RSpecDict(resources)
259         # convert the rspec dict to xml
260         rspec = RSpec()
261         rspec.parseDict(resourceDict)
262         return rspec.toxml()
263