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