added get_rspec_smgr(), get_rspec_aggregate(), get_remote_resources() methods
[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.misc 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):
24         self.api = api
25         self.ttl = ttl
26         self.threshold = None
27         path = self.api.config.SFA_BASE_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
35
36     def refresh(self):
37         """
38         Update the cached list of nodes
39         """
40
41         # Reload components list
42         now = datetime.datetime.now()
43         if not self.has_key('threshold') or not self.has_key('timestamp') or \
44            now > datetime.datetime.fromtimestamp(time.mktime(time.strptime(self['threshold'], self.api.time_format))): 
45             if self.api.interface in ['aggregate']:
46                 self.refresh_nodes_aggregate()
47             elif self.api.interface in ['slicemgr']:
48                 self.refresh_nodes_smgr()
49
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_remote_resources(self, hrn = None):
92         # convert and threshold to ints
93         if self.has_key('timestamp') and self['timestamp']:
94             hr_timestamp = self['timestamp']
95             timestamp = datetime.datetime.fromtimestamp(time.mktime(time.strptime(hr_timestamp, self.api.time_format)))
96             hr_threshold = self['threshold']
97             threshold = datetime.datetime.fromtimestamp(time.mktime(time.strptime(hr_threshold, self.api.time_format)))
98         else:
99             timestamp = datetime.datetime.now()
100             hr_timestamp = timestamp.strftime(self.api.time_format)
101             delta = datetime.timedelta(hours=self.ttl)
102             threshold = timestamp + delta
103             hr_threshold = threshold.strftime(self.api.time_format)
104
105         start_time = int(timestamp.strftime("%s"))
106         end_time = int(threshold.strftime("%s"))
107         duration = end_time - start_time
108
109         aggregates = Aggregates(self.api)
110         rspecs = {}
111         networks = []
112         rspec = Rspec()
113         credential = self.api.getCredential() 
114         for aggregate in aggregates:
115             try:
116                 # get the rspec from the aggregate
117                 agg_rspec = aggregates[aggregate].get_resources(credential, hrn)
118                 # extract the netspec from each aggregates rspec
119                 rspec.parseString(agg_rspec)
120                 networks.extend([{'NetSpec': rspec.getDictsByTagName('NetSpec')}])
121             except:
122                 # XX print out to some error log
123                 print >> log, "Error calling list nodes at aggregate %s" % aggregate
124                 traceback.print_exc(log)
125                 exc_type, exc_value, exc_traceback = sys.exc_info()
126                 raise exc_type, exc_value
127         # create the rspec dict
128         resources = {'networks': networks, 'start_time': start_time, 'duration': duration}
129         resourceDict = {'Rspec': resources}
130         # convert rspec dict to xml
131         rspec.parseDict(resourceDict)
132         return rspec
133
134     def refresh_node_smgr(self):
135
136         rspec = self.get_remote_resources()        
137         # filter according to policy
138         blist = self.policy['node_blacklist']
139         wlist = self.policy['node_whitelist']    
140         rspec.filter('NodeSpec', 'name', blacklist=blist, whitelist=wlist)
141
142         # update timestamp and threshold
143         timestamp = datetime.datetime.now()
144         hr_timestamp = timestamp.strftime(self.api.time_format)
145         delta = datetime.timedelta(hours=self.ttl)
146         threshold = timestamp + delta
147         hr_threshold = threshold.strftime(self.api.time_format)
148
149         nodedict = {'rspec': rspec.toxml(),
150                     'timestamp': hr_timestamp,
151                     'threshold':  hr_threshold}
152
153         self.update(nodedict)
154         self.write()
155
156     def get_rspec(self, hrn = None):
157
158         if self.api.interface in ['slicemgr']:
159             return self.get_rspec_smgr(hrn)
160         elif self.api.interface in ['aggregate']:
161             return self.get_rspec_aggregate(hrn)     
162
163     def get_rspec_smgr(self, hrn = None):
164         aggregates = Aggregates(self.api)
165         credential = self.api.getCredential()
166         rspecs = {}
167         
168         # send the request to all known aggregates
169         for aggregate in aggregates:
170             try:
171                 rspec = aggregates[aggregate].get_resources(credential, hrn)
172                 tmp_rspec = Rspec()
173                 tmp_rspec.parseString(rspec)
174             except:
175                 print >> log, "Error calling get resources at aggregate %(aggregate)s" % locals()
176
177
178     def get_rspec_aggregate(self, hrn = None):
179         """
180         Get resource information from PLC
181         """
182
183         # Get the required nodes
184         if not hrn:
185             nodes = self.api.plshell.GetNodes(self.api.plauth, {'peer_id': None})
186             try:  linkspecs = self.api.plshell.GetLinkSpecs() # if call is supported
187             except:  linkspecs = []
188         else:
189             slicename = hrn_to_pl_slicename(hrn)
190             slices = self.api.plshell.GetSlices(self.api.plauth, [slicename])
191             if not slices:
192                 nodes = []
193             else:
194                 slice = slices[0]
195                 node_ids = slice['node_ids']
196                 nodes = self.api.plshell.GetNodes(self.api.plauth, {'peer_id': None, 'node_id': node_ids})
197
198         # Filter out whitelisted nodes
199         public_nodes = lambda n: n.has_key('slice_ids_whitelist') and not n['slice_ids_whitelist']
200         nodes = filter(public_nodes, nodes)
201
202         # Get all network interfaces
203         interface_ids = []
204         for node in nodes:
205             # The field name has changed in plcapi 4.3
206             if self.api.plshell_version in ['4.2']:
207                 interface_ids.extend(node['nodenetwork_ids'])
208             elif self.api.plshell_version in ['4.3']:
209                 interface_ids.extend(node['interface_ids'])
210             else:
211                 raise GeniAPIError, "Unsupported plcapi version ", \
212                                  self.api.plshell_version
213
214         if self.api.plshell_version in ['4.2']:
215             interfaces = self.api.plshell.GetNodeNetworks(self.api.plauth, interface_ids)
216         elif self.api.plshell_version in ['4.3']:
217             interfaces = self.api.plshell.GetInterfaces(self.api.plauth, interface_ids)
218         else:
219             raise GeniAPIError, "Unsupported plcapi version ", \
220                                 self.api.plshell_version 
221         interface_dict = {}
222         for interface in interfaces:
223             if self.api.plshell_version in ['4.2']:
224                 interface_dict[interface['nodenetwork_id']] = interface
225             elif self.api.plshell_version in ['4.3']:
226                 interface_dict[interface['interface_id']] = interface
227             else:
228                 raise GeniAPIError, "Unsupported plcapi version", \
229                                     self.api.plshell_version 
230
231         # join nodes with thier interfaces
232         for node in nodes:
233             node['interfaces'] = []
234             if self.api.plshell_version in ['4.2']:
235                 for nodenetwork_id in node['nodenetwork_ids']:
236                     node['interfaces'].append(interface_dict[nodenetwork_id])
237             elif self.api.plshell_version in ['4.3']:
238                 for interface_id in node['interface_ids']:
239                     node['interfaces'].append(interface_dict[interface_id])
240             else:
241                 raise GeniAPIError, "Unsupported plcapi version", \
242                                     self.api.plshell_version
243
244         # convert and threshold to ints
245         if self.has_key('timestamp') and self['timestamp']:
246             timestamp = datetime.datetime.fromtimestamp(time.mktime(time.strptime(self['timestamp'], self.api.time_format)))
247             threshold = datetime.datetime.fromtimestamp(time.mktime(time.strptime(self['threshold'], self.api.time_format)))
248         else:
249             timestamp = datetime.datetime.now()
250             delta = datetime.timedelta(hours=self.ttl)
251             threshold = timestamp + delta
252
253         start_time = int(timestamp.strftime("%s"))
254         end_time = int(threshold.strftime("%s"))
255         duration = end_time - start_time
256
257         # create the plc dict
258         networks = [{'nodes': nodes,
259                      'name': self.api.hrn,
260                      'start_time': start_time,
261                      'duration': duration}]
262         if not hrn:
263             networks[0]['links'] = linkspecs
264         resources = {'networks': networks, 'start_time': start_time, 'duration': duration}
265
266         # convert the plc dict to an rspec dict
267         resourceDict = RspecDict(resources)
268         # convert the rspec dict to xml
269         rspec = Rspec()
270         rspec.parseDict(resourceDict)
271         return rspec.toxml()
272