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