fix show, list and remove. Add code to write records to disk, reorganize main to...
[sfa.git] / plc / aggregate.py
1 import os
2 import sys
3 import datetime
4 import time
5 import xmlrpclib
6
7 from util.geniserver import *
8 from util.cert import *
9 from util.trustedroot import *
10 from util.excep import *
11 from util.misc import *
12 from util.config import Config
13
14 class Aggregate(GeniServer):
15
16     hrn = None
17     aggregate_file = None
18     components_file = None
19     slices_file = None  
20     components_ttl = None
21     components = []
22     slices = [] 
23     policies = {}
24     timestamp = None
25     threshold = None    
26     shell = None
27      
28
29     def __init__(self, ip, port, key_file, cert_file, config = "/usr/share/geniwrapper/util/geni_config"):
30         GeniServer.__init__(ip, port, keyfile, cert_file)
31         
32         conf = Config(config)
33         basedir = conf.GENI_BASE_DIR + os.sep
34         server_basedir = basedir + os.sep + "plc" + os.sep
35         self.hrn = conf.GENI_INTERFACE_HRN
36         self.components_file = os.sep.join([server_basedir, 'components', hrn + '.comp'])
37         self.slices_file = os.sep.join([server_basedir, 'components', hrn + '.slices'])
38         self.timestamp_file = os.sep.join([server_basedir, 'components', hrn + '.timestamp']) 
39         self.components_ttl = components_ttl
40         self.connect()
41
42     def connect(self):
43         """
44         Connect to the plc api interface. First attempt to impor thte shell, if that fails
45         try to connect to the xmlrpc server.
46         """
47         self.auth = {'Username': conf.GENI_PLC_USER,
48                      'AuthMethod': 'password',
49                      'AuthString': conf.GENI_PLC_PASSWORD}
50
51         try:
52             # try to import PLC.Shell directly
53             sys.path.append(conf.GENI_PLC_SHELL_PATH) 
54             import PLC.Shell
55             self.shell = PLC.Shell.Shell(globals())
56             self.shell.AuthCheck()
57         except ImportError:
58             # connect to plc api via xmlrpc
59             plc_host = conf.GENI_PLC_HOST
60             plc_port = conf.GENI_PLC_PORT
61             plc_api_path = conf.GENI_PLC_API_PATH                    
62             url = "https://%(plc_host)s:%(plc_port)s/%(plc_api_path)s/" % locals()
63             self.auth = {'Username': conf.GENI_PLC_USER,
64                          'AuthMethod': 'password',
65                          'AuthString': conf.GENI_PLC_PASSWORD} 
66
67             self.shell = xmlrpclib.Server(url, verbose = 0, allow_none = True) 
68             self.shell.AuthCheck(self.auth) 
69
70     def hostname_to_hrn(self, login_base, hostname):
71         """
72         Convert hrn to plantelab name.
73         """
74         genihostname = "_".join(hostname.split("."))
75         return ".".join([self.hrn, login_base, genihostname])
76
77     def slicename_to_hrn(self, slicename):
78         """
79         Convert hrn to planetlab name.
80         """
81         slicename = slicename.replace("_", ".")
82         return ".".join([self.hrn, slicename])
83
84     def refresh_components(self):
85         """
86         Update the cached list of nodes and slices.
87         """
88         print "refreshing"      
89         # resolve component hostnames 
90         nodes = self.shell.GetNodes(self.auth, {}, ['hostname', 'site_id'])
91         
92         # resolve slices
93         slices = self.shell.GetSlices(self.auth, {}, ['name', 'site_id'])
94    
95         # resolve site login_bases
96         site_ids = [node['site_id'] for node in nodes]
97         sites = self.shell.GetSites(self.auth, site_ids, ['site_id', 'login_base'])
98         site_dict = {}
99         for site in sites:
100             site_dict[site['site_id']] = site['login_base']
101
102         # convert plc names to geni hrn
103         self.components = [self.hostname_to_hrn(site_dict[node['site_id']], node['hostname']) for node in nodes]
104         self.slices = [self.slicename_to_hrn(slice['name']) for slice in slices]
105                 
106         # update timestamp and threshold
107         self.timestamp = datetime.datetime.now()
108         delta = datetime.timedelta(hours=self.components_ttl)
109         self.threshold = self.timestamp + delta 
110         
111         f = open(self.components_file, 'w')
112         f.write(str(self.components))
113         f.close()
114         f = open(self.slices_file, 'w')
115         f.write(str(self.slices))
116         f.close()
117         f = open(self.timestamp_file, 'w')
118         f.write(str(self.threshold))
119         f.close()
120  
121     def load_components(self):
122         """
123         Read cached list of nodes and slices.
124         """
125         print "loading"
126         # Read component list from cached file 
127         if os.path.exists(self.components_file):
128             f = open(self.components_file, 'r')
129             self.components = eval(f.read())
130             f.close()
131         
132         if os.path.exists(self.slices_file):
133             f = open(self.components_file, 'r')
134             self.slices = eval(f.read())
135             f.close()
136
137         time_format = "%Y-%m-%d %H:%M:%S"
138         if os.path.exists(self.timestamp_file):
139             f = open(self.timestamp_file, 'r')
140             timestamp = str(f.read()).split(".")[0]
141             self.timestamp = datetime.datetime.fromtimestamp(time.mktime(time.strptime(timestamp, time_format)))
142             delta = datetime.timedelta(hours=self.components_ttl)
143             self.threshold = self.timestamp + delta
144             f.close()   
145
146     def get_components(self):
147         """
148         Return a list of components at this aggregate.
149         """
150         # Reload components list
151         now = datetime.datetime.now()
152         #self.load_components()
153         if not self.threshold or not self.timestamp or now > self.threshold:
154             self.refresh_components()
155         elif now < self.threshold and not self.components: 
156             self.load_components()
157         return self.components
158    
159      
160     def get_slices(self):
161         """
162         Return a list of instnatiated slices at this aggregate.
163         """
164         now = datetime.datetime.now()
165         #self.load_components()
166         if not self.threshold or not self.timestamp or now > self.threshold:
167             self.refresh_components()
168         elif now < self.threshold and not self.slices:
169             self.load_components()
170         return self.slices
171
172     def get_rspec(self, hrn, type):
173         #rspec = Rspec()
174         if type in ['node']:
175             nodes = self.shell.GetNodes(self.auth)
176         elif type in ['slice']:
177             slices = self.shell.GetSlices(self.auth)
178         elif type in ['aggregate']:
179             pass
180
181     def get_resources(self, slice_hrn):
182         """
183         Return the current rspec for the specified slice.
184         """
185         slicename = hrn_to_plcslicename(slice_hrn)
186         rspec = self.get_rspec(slicenamem, 'slice' )
187         
188         return rspec
189  
190     def create_slice(self, slice_hrn, rspec):
191         """
192         Instantiate the specified slice according to whats defined in the rspec.
193         """
194         slicename = self.hrn_to_plcslicename(slice_hrn)
195         #spec = Rspec(rspec)
196         #components = spec.components()
197         #shell.AddSliceToNodes(self.auth, slicename, components)
198         return 1
199         
200     def delete_slice_(self, slice_hrn):
201         """
202         Remove this slice from all components it was previouly associated with and 
203         free up the resources it was using.
204         """
205         slicename = self.hrn_to_plcslicename(slice_hrn)
206         rspec = self.get_resources(slice_hrn)
207         components = rspec.components()
208         shell.DeleteSliceFromNodes(self.auth, slicename, components)
209         return 1
210
211     def start_slice(self, slice_hrn):
212         """
213         Stop the slice at plc.
214         """
215         slicename = hrn_to_plcslicename(slice_hrn)
216         slices = self.shell.GetSlices(self.auth, {'name': slicename}, ['slice_id'])
217         if not slices:
218             raise RecordNotFound(slice_hrn)
219         slice_id = slices[0]
220         atrribtes = self.shell.GetSliceAttributes({'slice_id': slice_id, 'name': 'enabled'}, ['slice_attribute_id'])
221         attribute_id = attreibutes[0] 
222         self.shell.UpdateSliceAttribute(self.auth, attribute_id, "1" )
223         return 1
224
225     def stop_slice(self, slice_hrn):
226         """
227         Stop the slice at plc
228         """
229         slicename = hrn_to_plcslicename(slice_hrn)
230         slices = self.shell.GetSlices(self.auth, {'name': slicename}, ['slice_id'])
231         if not slices:
232             raise RecordNotFound(slice_hrn)
233         slice_id = slices[0]
234         atrribtes = self.shell.GetSliceAttributes({'slice_id': slice_id, 'name': 'enabled'}, ['slice_attribute_id'])
235         attribute_id = attreibutes[0]
236         self.shell.UpdateSliceAttribute(self.auth, attribute_id, "0")
237         return 1
238
239     def reset_slice(self, slice_hrn):
240         """
241         Reset the slice
242         """
243         slicename = self.hrn_to_plcslicename(slice_hrn)
244         return 1
245
246     def get_policy(self):
247         """
248         Return this aggregates policy as an rspec
249         """
250         rspec = self.get_rspec(self.hrn, 'aggregate')
251         return rspec
252         
253         
254
255 ##############################
256 ## Server methods here for now
257 ##############################
258
259     def nodes(self):
260         return self..get_components()
261
262     def slices(self):
263         return self.get_slices()
264
265     def resources(self, cred, hrn):
266         self.decode_authentication(cred, 'info')
267         self.verify_object_belongs_to_me(hrn)
268
269         return self.get_resources(hrn)
270
271     def create(self, cred, hrn, rspec):
272         self.decode_authentication(cred, 'embed')
273         self.verify_object_belongs_to_me(hrn, rspec)
274         return self.create(hrn)
275
276     def delete(self, cred, hrn):
277         self.decode_authentication(cred, 'embed')
278         self.verify_object_belongs_to_me(hrn)
279         return self.delete_slice(hrn)
280
281     def start(self, cred, hrn):
282         self.decode_authentication(cred, 'control')
283         return self.start(hrn)
284
285     def stop(self, cred, hrn):
286         self.decode_authentication(cred, 'control')
287         return self.stop(hrn)
288
289     def reset(self, cred, hrn):
290         self.decode_authentication(cred, 'control')
291         return self.reset(hrn)
292
293     def policy(self, cred):
294         self.decode_authentication(cred, 'info')
295         return self.get_policy()
296
297     def register_functions(self):
298         GeniServer.register_functions(self)
299
300         # Aggregate interface methods
301         self.server.register_function(self.components)
302         self.server.register_function(self.slices)
303         self.server.register_function(self.resources)
304         self.server.register_function(self.create)
305         self.server.register_function(self.delete)
306         self.server.register_function(self.start)
307         self.server.register_function(self.stop)
308         self.server.register_function(self.reset)
309         self.server.register_function(self.policy)
310