2578abf16997142cac759d18192136fd7dfee24b
[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         self.nodes_file = os.sep.join([self.api.server_basedir, self.api.interface +'.'+ self.api.hrn + '.nodes'])
24         SimpleStorage.__init__(self, self.nodes_file)
25         self.policy = Policy(api)
26         self.load()
27
28
29     def refresh(self):
30         """
31         Update the cached list of nodes
32         """
33
34         # Reload components list
35         now = datetime.datetime.now()
36         if not self.has_key('threshold') or not self.has_key('timestamp') or \
37            now > datetime.datetime.fromtimestamp(time.mktime(time.strptime(self['threshold'], self.api.time_format))): 
38             if self.api.interface in ['aggregate']:
39                 self.refresh_nodes_aggregate()
40             elif self.api.interface in ['slicemgr']:
41                 self.refresh_nodes_smgr()
42
43         
44     def refresh_nodes_aggregate(self):
45         rspec = Rspec()
46         rspec.parseString(self.get_rspec())
47         
48         # filter nodes according to policy
49         blist = self.policy['node_blacklist']
50         wlist = self.policy['node_whitelist']
51         rspec.filter('NodeSpec', 'name', blacklist=blist, whitelist=wlist)
52
53         # extract ifspecs from rspec to get ips'
54         ips = []
55         ifspecs = rspec.getDictsByTagName('IfSpec')
56         for ifspec in ifspecs:
57             if ifspec.has_key('addr') and ifspec['addr']:
58                 ips.append(ifspec['addr'])
59
60         # extract nodespecs from rspec to get dns names
61         hostnames = []
62         nodespecs = rspec.getDictsByTagName('NodeSpec')
63         for nodespec in nodespecs:
64             if nodespec.has_key('name') and nodespec['name']:
65                 hostnames.append(nodespec['name'])
66
67         # update timestamp and threshold
68         timestamp = datetime.datetime.now()
69         hr_timestamp = timestamp.strftime(self.api.time_format)
70         delta = datetime.timedelta(hours=self.ttl)
71         threshold = timestamp + delta
72         hr_threshold = threshold.strftime(self.api.time_format)
73
74         node_details = {}
75         node_details['rspec'] = rspec.toxml()
76         node_details['ip'] = ips
77         node_details['dns'] = hostnames
78         node_details['timestamp'] = hr_timestamp
79         node_details['threshold'] = hr_threshold
80         # save state 
81         self.update(node_details)
82         self.write()       
83  
84     def refresh_nodes_smgr(self):
85         # convert and threshold to ints
86         if self.has_key('timestamp') and self['timestamp']:
87             hr_timestamp = self['timestamp']
88             timestamp = datetime.datetime.fromtimestamp(time.mktime(time.strptime(hr_timestamp, self.api.time_format)))
89             hr_threshold = self['threshold']
90             threshold = datetime.datetime.fromtimestamp(time.mktime(time.strptime(hr_threshold, self.api.time_format)))
91         else:
92             timestamp = datetime.datetime.now()
93             hr_timestamp = timestamp.strftime(self.api.time_format)
94             delta = datetime.timedelta(hours=self.ttl)
95             threshold = timestamp + delta
96             hr_threshold = threshold.strftime(self.api.time_format)
97
98         start_time = int(timestamp.strftime("%s"))
99         end_time = int(threshold.strftime("%s"))
100         duration = end_time - start_time
101
102         aggregates = Aggregates(self.api)
103         rspecs = {}
104         networks = []
105         rspec = Rspec()
106         credential = self.api.getCredential() 
107         for aggregate in aggregates:
108             try:
109                 # get the rspec from the aggregate
110                 agg_rspec = aggregates[aggregate].resources(credential)
111                 # extract the netspec from each aggregates rspec
112                 rspec.parseString(agg_rspec)
113                 networks.extend([{'NetSpec': rspec.getDictsByTagName('NetSpec')}])
114             except:
115                 raise
116                 # XX print out to some error log
117                 print >> log, "Error calling list nodes at aggregate %s" % aggregate
118         # create the rspec dict
119         resources = {'networks': networks, 'start_time': start_time, 'duration': duration}
120         resourceDict = {'Rspec': resources}
121         # convert rspec dict to xml
122         rspec.parseDict(resourceDict)
123
124         # filter according to policy
125         blist = self.policy['node_blacklist']
126         wlist = self.policy['node_whitelist']    
127         rspec.filter('NodeSpec', 'name', blacklist=blist, whitelist=wlist)
128
129         # update timestamp and threshold
130         timestamp = datetime.datetime.now()
131         hr_timestamp = timestamp.strftime(self.api.time_format)
132         delta = datetime.timedelta(hours=self.ttl)
133         threshold = timestamp + delta
134         hr_threshold = threshold.strftime(self.api.time_format)
135
136         nodedict = {'rspec': rspec.toxml(),
137                     'timestamp': hr_timestamp,
138                     'threshold':  hr_threshold}
139
140         self.update(nodedict)
141         self.write()
142
143
144     def get_rspec(self, hrn = None):
145         """
146         Get resource information from PLC
147         """
148
149         # Get the required nodes
150         if not hrn:
151             nodes = self.api.plshell.GetNodes(self.api.plauth)
152             try:  linkspecs = self.api.plshell.GetLinkSpecs() # if call is supported
153             except:  linkspecs = []
154         else:
155             slicename = hrn_to_pl_slicename(hrn)
156             slices = self.api.plshell.GetSlices(self.api.plauth, [slicename])
157             if not slices:
158                 nodes = []
159             else:
160                 slice = slices[0]
161                 node_ids = slice['node_ids']
162                 nodes = self.api.plshell.GetNodes(self.api.plauth, node_ids)
163
164         # Filter out whitelisted nodes
165         public_nodes = lambda n: n.has_key('slice_ids_whitelist') and not n['slice_ids_whitelist']
166         nodes = filter(public_nodes, nodes)
167
168         # Get all network interfaces
169         interface_ids = []
170         for node in nodes:
171             interface_ids.extend(node['nodenetwork_ids'])
172         interfaces = self.api.plshell.GetNodeNetworks(self.api.plauth, interface_ids)
173         interface_dict = {}
174         for interface in interfaces:
175             interface_dict[interface['nodenetwork_id']] = interface
176
177         # join nodes with thier interfaces
178         for node in nodes:
179             node['interfaces'] = []
180             for nodenetwork_id in node['nodenetwork_ids']:
181                 node['interfaces'].append(interface_dict[nodenetwork_id])
182
183         # convert and threshold to ints
184         if self.has_key('timestamp') and self['timestamp']:
185             timestamp = datetime.datetime.fromtimestamp(time.mktime(time.strptime(self['timestamp'], self.api.time_format)))
186             threshold = datetime.datetime.fromtimestamp(time.mktime(time.strptime(self['threshold'], self.api.time_format)))
187         else:
188             timestamp = datetime.datetime.now()
189             delta = datetime.timedelta(hours=self.ttl)
190             threshold = timestamp + delta
191
192         start_time = int(timestamp.strftime("%s"))
193         end_time = int(threshold.strftime("%s"))
194         duration = end_time - start_time
195
196         # create the plc dict
197         networks = [{'nodes': nodes,
198                      'name': self.api.hrn,
199                      'start_time': start_time,
200                      'duration': duration}]
201         if not hrn:
202             networks[0]['links'] = linkspecs
203         resources = {'networks': networks, 'start_time': start_time, 'duration': duration}
204
205         # convert the plc dict to an rspec dict
206         resourceDict = RspecDict(resources)
207         # convert the rspec dict to xml
208         rspec = Rspec()
209         rspec.parseDict(resourceDict)
210         return rspec.toxml()
211