9f7b88b503483a7e63440ffc61c3f3c6e3f18524
[monitor.git] / commands / myops.py
1 #!/usr/bin/python
2
3 import os
4 import sys
5 import string
6 import time
7
8 import getopt
9 import sys
10 import os
11 import xml, xmlrpclib
12 from getpass import getpass
13 from operator import attrgetter, itemgetter
14
15 def get_plc_api(target, url, username, password, expires, debug_mode):
16     # Either read session from disk or create it and save it for later
17     metasession = "%s/%s_%s" % (os.environ['HOME'], ".metasession", target)
18     if os.path.exists(metasession):
19         (localurl,session) =  open(metasession, 'r').read().strip().split()
20         plc = xmlrpclib.Server(localurl, verbose=False, allow_none=True)
21     else:
22         plc = xmlrpclib.Server(url, verbose=False, allow_none=True)
23         if password == None: password = getpass()
24         auth = {'Username' : username, 
25                 'AuthMethod' : 'password',
26                 'AuthString' : password}
27         session = plc.GetSession(auth, expires*(60*60*24))
28         with open(metasession, 'w') as f: f.write("%s %s\n" % (url,session)) # 'with' auto-closes
29
30     auth = {'AuthMethod' : 'session', 'session' : session}
31
32     class PLC:
33         def __init__(self, plc, auth):
34             self.plc = plc
35             self.auth = auth
36
37         def __getattr__(self, name):
38             method = getattr(self.plc, name)
39             if method is None:
40                 raise AssertionError("Method does not exist: %s" % method)
41             if not debug_mode or ('Get' in name or 'AuthCheck' in name):
42                 return lambda *params : method(self.auth, *params)
43             else:
44                 def call(name,*params):
45                     print "DEBUG not running: %s(%s)" % (name, params)
46                 return lambda *params : call(name,*params)
47
48     plc_api = PLC(plc, auth)
49     try:
50         # make sure the session is working
51         plc_api.AuthCheck()
52     except:
53         # everything worked except the auth check. try again asking for passwd.
54         plc_api = get_plc_api(target, url, username, None, expires, debug_mode)
55
56     return plc_api
57
58 def usage(parser):
59     print """
60 myops.py <TARGET> <ACTION> [<object>] [args]
61     MYOPS CLI uses sessions to avoid storing passwords.
62     You choose the session expiration via --expires days.
63
64 TARGET:
65     When your session is saved it is identified by your given 'target'
66     name.  This is a unique string you chose to identify the --url. 
67     For example, one might use:
68         plc
69         vicci
70         test
71         vini
72
73 ACTION:
74     Connect to TARGET and perform ACTION. The current actions are:
75         enabled   --  Manage site, node, and slice 'enabled' states.
76         
77         exempt    --  Manage site, node, and slice exemptions from 
78                      myops policy actions.
79
80         removeall --  Remove all exemptions at site from site, nodes, slices 
81         addall    --  Add exemptions at site to site, nodes, slices 
82
83         freeze    --  Clamp down everything at a site: 
84                         disable site, 
85                         disable slices,
86
87         release   --  Release everything at a site: 
88                         re-enable site, 
89                         re-enable slices,
90 EXAMPLES:
91     # setup session and save target name 'plc' for future calls
92     myops.py plc --apiurl https://boot.planet-lab.org/PLCAPI/ \\
93                 --username soltesz@cs.princeton.edu
94
95     # list current exemptions at plc
96     myops.py plc exempt
97
98     # to list only one site (nothing will show if no exemption is present)
99     myops.py plc exempt princeton
100
101     # add a new exemption to site 'princeton' for a day
102     myops.py plc exempt princeton -a
103
104     # add a new exemption to a specific date
105     myops.py plc exempt princeton -a --expires 20120131
106
107 """ 
108     parser.print_help()
109
110 def unparse_expire_str(value):
111     expires = time.mktime(time.strptime(value, "%Y%m%d")) - time.time()
112     return int(expires)
113
114 def parse_expire_str(value):
115     import optparse
116     if value == "0":
117         value = "20990101"
118     elif len(value) <= 3:
119         # days from now
120         value = time.strftime("%Y%m%d", time.localtime(time.time()+int(value)*60*60*24))
121     elif len(value) != 8 and value[:3] != "201": # 201 == this decade.
122         # flip out
123         raise optparse.OptionValueError
124     return value
125
126 class PlcObj(object):
127     def __init__(self, name):
128         if type(name) == type(""):
129             self.name = name
130         elif type(name) == type({}):
131             if 'login_base' in name:
132                 self.name = name['login_base']
133             elif 'hostname' in name:
134                 self.name = name['hostname']
135             elif 'name' in name:
136                 self.name = name['name']
137
138         self.kind = None
139         if '_' in self.name: 
140             kind = 'Slice'
141         elif '.' in self.name: 
142             kind='Node'
143         else: 
144             kind='Site'
145         self.kind = kind
146
147     def list(self,target,action,*vals):
148         if action == "enabled":
149             print ("\t%s %s %s" % (sys.argv[0],target,action)) + (" %-20s --disable" % self.name)
150         elif action == "exempt":
151             print ("\t%s %s %s" % (sys.argv[0],target,action)) + (" %-20s -a --expires %s" % ((self.name,)+ vals))
152
153     def enable(self,api,state):
154         if self.kind == 'Slice':
155             # change value of existing slice tag, if it exists.
156             tl = api.GetSliceTags({'name' : self.name, 'tagname' : 'enabled', 'value' : '0' if state else '1'})
157             if len(tl) == 0:
158                 api.AddSliceTag(self.name, 'enabled', '1' if state else '0')
159             else:
160                 for t in tl: 
161                     api.UpdateSliceTag(t['slice_tag_id'], {'value' : '1' if state else '0'})
162         elif self.kind == 'Node':
163             if state == True: 
164                 api.UpdateNode(self.name, {'boot_state' : 'boot'})
165             else: 
166                 api.UpdateNode(self.name, {'boot_state' : 'disabled'})
167         elif self.kind == 'Site':
168             api.UpdateSite(self.name, {'enabled' : state})
169             
170     def exempt(self,api,expires):
171         if expires != None:
172             if self.kind == 'Slice': 
173                 try: api.AddSliceTag(self.name, 'exempt_slice_until', expires)
174                 except: api.UpdateSliceTag(self.name, expires)
175             elif self.kind == 'Node': 
176                 try: api.AddNodeTag(self.name, 'exempt_node_until', expires)
177                 except: api.UpdateNodeTag(self.name, expires)
178             elif self.kind == 'Site': 
179                 try: api.AddSiteTag(api.GetSites(self.name, ['site_id'])[0]['site_id'], 'exempt_site_until', expires)
180                 except: api.UpdateSiteTag(api.GetSiteTags({'login_base' : self.name, 'tagname' : 'exempt_site_until'})[0]['site_tag_id'], expires)
181         else:
182             # remove
183             if self.kind == 'Slice': 
184                 tag_id_l = api.GetSliceTags({'name' : self.name, 'tagname' : 'exempt_slice_until'}, ['slice_tag_id'])
185                 if len(tag_id_l) > 0:   
186                     tag_id = tag_id_l[0]['slice_tag_id']
187                     api.DeleteSliceTag(tag_id)
188             elif self.kind == 'Node': 
189                 tag_id_l = api.GetNodeTags({'hostname' : self.name, 'tagname' : 'exempt_node_until'}, ['node_tag_id'])
190                 if len(tag_id_l) > 0: 
191                     tag_id = tag_id_l[0]['node_tag_id']
192                     api.DeleteNodeTag(tag_id)
193             elif self.kind == 'Site': 
194                 tag_id_l = api.GetSiteTags({'login_base' : self.name, 'tagname' : 'exempt_site_until'}, ['site_tag_id'])
195                 if len(tag_id_l) > 0: 
196                     tag_id = tag_id_l[0]['site_tag_id']
197                     api.DeleteSiteTag(tag_id)
198
199
200 def main():
201     from optparse import OptionParser
202     copy = False
203     parser = OptionParser()
204
205     parser.add_option("-d", "--debug", dest="debug", action="store_true", default=False, help="")
206     parser.add_option("-v", "--verbose", dest="verbose", default=False, help="")
207     parser.add_option("-u", "--apiurl", dest="url", default="https://www.planet-lab.org/PLCAPI/", help="Set PLC URL for action")
208     parser.add_option("-U", "--username", dest="username", default=None, help="Login as username")
209     parser.add_option("-P", "--password", dest="password", default=None, help="Use provided password; otherwise prompt for password")
210     parser.add_option("-e", "--expires", dest="expires", default="1", help="Set expiration date YYYYMMDD; default is 1 day from now.")
211     parser.add_option("", "--disable", dest="disable", default=False, action="store_true", help="Disable object.")
212     parser.add_option("-r", "--remove", dest="remove", action="store_true", default=False, help="Remove object from exemption" )
213     parser.add_option("-l", "--list", dest="list", action="store_true", default=False, help="List objects with command used to generate them")
214     parser.add_option("-a", "--add", dest="add", action="store_true", default=False, help="Add exempt object")
215     parser.add_option("-S", "--site", dest="login_base", default=None, help="Act on this site")
216     parser.add_option("-H", "--host", dest="hostname", default=None, help="Act on this node")
217     parser.add_option("-s", "--slice", dest="slicename", default=None, help="Act on this site")
218
219     (opt, args) = parser.parse_args()
220     opt.expires = parse_expire_str(opt.expires)
221
222     if len(args) == 0:
223         usage(parser)
224         sys.exit(1)
225
226     target = args[0]; 
227     api = get_plc_api(target, opt.url, opt.username, opt.password, unparse_expire_str(opt.expires), opt.debug)
228
229     action_list = ['enabled', 'exempt', 'removeall', 'addall', 'release', 'freeze']
230
231     for i,action in enumerate(args[1:]):
232         if action in action_list:
233             if len(args) > i+2 and args[i+2] not in action_list:
234                 objname = args[i+2]
235             else:
236                 objname = None
237
238         if action == "enabled":
239
240             if not opt.list and not opt.hostname and not opt.slicename and not opt.login_base:
241                 opt.list = True
242             if opt.list:
243                 print "Listing only *disabled* objects"
244                 sites = api.GetSites({'peer_id' : None, 'enabled': False})
245                 nodes = api.GetNodes({'peer_id' : None, 'boot_state' : 'disabled'})
246                 slices= api.GetSliceTags({'tagname' : 'enabled'})
247
248                 for (header,objlist) in [("Sites:",sites), ("Nodes:", nodes), ("Slices:", slices)]:
249                     if len(objlist) > 0: print header
250                     for t in objlist:
251                         o = PlcObj(t)
252                         o.list(target, action)
253
254         if action == "exempt":
255             if not opt.list and not opt.remove and not opt.add:
256                 opt.list = True
257
258             if opt.list:
259                 if objname == None:
260                     sites = api.GetSiteTags({'tagname' : 'exempt_site_until'})
261                     nodes = api.GetNodeTags({'tagname' : 'exempt_node_until'})
262                     slices = api.GetSliceTags({'tagname' : 'exempt_slice_until'})
263                 else:
264                     try: sites = api.GetSiteTags({'login_base': objname, 'tagname' : 'exempt_site_until'})
265                     except: sites = []
266                     try: nodes = api.GetNodeTags({'hostname' : objname, 'tagname' : 'exempt_node_until'})
267                     except: nodes = []
268                     try: slices = api.GetSliceTags({'name' : objname, 'tagname' : 'exempt_slice_until'})
269                     except: slices = []
270
271                 for (header,objlist) in [("Sites:",sites), ("Nodes:", nodes), ("Slices:", slices)]:
272                     if len(objlist) > 0: print header
273                     for t in objlist:
274                         o = PlcObj(t)
275                         o.list(target, action, t['value'])
276
277             if opt.remove:
278                 if objname == None: raise Exception("provide an object name to remove exemption")
279                 obj = PlcObj(objname)
280                 obj.exempt(api,None)
281
282             if opt.add:
283                 if objname == None: raise Exception("provide an object name to add exemption")
284                 obj = PlcObj(objname)
285                 obj.exempt(api,opt.expires)
286
287         if action == "freeze":
288             if objname == None: raise Exception("Provide a site name to freeze")
289             #  disable site, disable slices,
290             try:
291                 slices = api.GetSlices(api.GetSites(objname, ['slice_ids'])[0]['slice_ids'])
292             except:
293                 slices = []
294             obj = PlcObj(objname)
295             obj.enable(api,False)
296             for sl in slices:
297                 obj = PlcObj(sl['name'])
298                 obj.enable(api,False)
299                 
300         if action == "release":
301             #  enable site, enable slices,
302             if objname == None: raise Exception("Provide a site name to release")
303             try:
304                 slices = api.GetSlices(api.GetSites(objname, ['slice_ids'])[0]['slice_ids'])
305             except:
306                 slices = []
307             obj = PlcObj(objname)
308             obj.enable(api,True)
309             for sl in slices:
310                 obj = PlcObj(sl['name'])
311                 obj.enable(api,True)
312
313         if action == "removeall":
314             #  remove enable site, enable slices,
315             if objname == None: raise Exception("Provide a site name to release")
316             try:
317                 slices = api.GetSlices(api.GetSites(objname, ['slice_ids'])[0]['slice_ids'])
318             except:
319                 slices = []
320             obj = PlcObj(objname)
321             obj.exempt(api,None)
322             for sl in slices:
323                 obj = PlcObj(sl['name'])
324                 obj.exempt(api,None)
325
326         if action == "addall":
327             if objname == None: raise Exception("Provide a site name to release")
328             try:
329                 slices = api.GetSlices(api.GetSites(objname, ['slice_ids'])[0]['slice_ids'])
330             except:
331                 slices = []
332             obj = PlcObj(objname)
333             obj.exempt(api,opt.expires)
334             for sl in slices:
335                 obj = PlcObj(sl['name'])
336                 obj.exempt(api,opt.expires)
337
338 if __name__ == '__main__':
339     main()