12 from getpass import getpass
13 from operator import attrgetter, itemgetter
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)
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
30 auth = {'AuthMethod' : 'session', 'session' : session}
33 def __init__(self, plc, auth):
37 def __getattr__(self, name):
38 method = getattr(self.plc, name)
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)
44 def call(name,*params):
45 print "DEBUG not running: %s(%s)" % (name, params)
46 return lambda *params : call(name,*params)
48 plc_api = PLC(plc, auth)
50 # make sure the session is working
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)
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.
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:
74 Connect to TARGET and perform ACTION. The current actions are:
75 enabled -- Manage site, node, and slice 'enabled' states.
77 exempt -- Manage site, node, and slice exemptions from
80 removeall -- Remove all exemptions at site from site, nodes, slices
81 addall -- Add exemptions at site to site, nodes, slices
83 freeze -- Clamp down everything at a site:
87 release -- Release everything at a site:
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
95 # list current exemptions at plc
98 # to list only one site (nothing will show if no exemption is present)
99 myops.py plc exempt princeton
101 # add a new exemption to site 'princeton' for a day
102 myops.py plc exempt princeton -a
104 # add a new exemption to a specific date
105 myops.py plc exempt princeton -a --expires 20120131
110 def unparse_expire_str(value):
111 expires = time.mktime(time.strptime(value, "%Y%m%d")) - time.time()
114 def parse_expire_str(value):
118 elif len(value) <= 3:
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.
123 raise optparse.OptionValueError
126 class PlcObj(object):
127 def __init__(self, name):
128 if type(name) == type(""):
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']
136 self.name = name['name']
141 elif '.' in self.name:
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))
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'})
158 api.AddSliceTag(self.name, 'enabled', '1' if state else '0')
161 api.UpdateSliceTag(t['slice_tag_id'], {'value' : '1' if state else '0'})
162 elif self.kind == 'Node':
164 api.UpdateNode(self.name, {'boot_state' : 'boot'})
166 api.UpdateNode(self.name, {'boot_state' : 'disabled'})
167 elif self.kind == 'Site':
168 api.UpdateSite(self.name, {'enabled' : state})
170 def exempt(self,api,expires):
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)
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)
201 from optparse import OptionParser
203 parser = OptionParser()
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")
219 (opt, args) = parser.parse_args()
220 opt.expires = parse_expire_str(opt.expires)
227 api = get_plc_api(target, opt.url, opt.username, opt.password, unparse_expire_str(opt.expires), opt.debug)
229 action_list = ['enabled', 'exempt', 'removeall', 'addall', 'release', 'freeze']
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:
238 if action == "enabled":
240 if not opt.list and not opt.hostname and not opt.slicename and not opt.login_base:
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'})
248 for (header,objlist) in [("Sites:",sites), ("Nodes:", nodes), ("Slices:", slices)]:
249 if len(objlist) > 0: print header
252 o.list(target, action)
254 if action == "exempt":
255 if not opt.list and not opt.remove and not opt.add:
260 sites = api.GetSiteTags({'tagname' : 'exempt_site_until'})
261 nodes = api.GetNodeTags({'tagname' : 'exempt_node_until'})
262 slices = api.GetSliceTags({'tagname' : 'exempt_slice_until'})
264 try: sites = api.GetSiteTags({'login_base': objname, 'tagname' : 'exempt_site_until'})
266 try: nodes = api.GetNodeTags({'hostname' : objname, 'tagname' : 'exempt_node_until'})
268 try: slices = api.GetSliceTags({'name' : objname, 'tagname' : 'exempt_slice_until'})
271 for (header,objlist) in [("Sites:",sites), ("Nodes:", nodes), ("Slices:", slices)]:
272 if len(objlist) > 0: print header
275 o.list(target, action, t['value'])
278 if objname == None: raise Exception("provide an object name to remove exemption")
279 obj = PlcObj(objname)
283 if objname == None: raise Exception("provide an object name to add exemption")
284 obj = PlcObj(objname)
285 obj.exempt(api,opt.expires)
287 if action == "freeze":
288 if objname == None: raise Exception("Provide a site name to freeze")
289 # disable site, disable slices,
291 slices = api.GetSlices(api.GetSites(objname, ['slice_ids'])[0]['slice_ids'])
294 obj = PlcObj(objname)
295 obj.enable(api,False)
297 obj = PlcObj(sl['name'])
298 obj.enable(api,False)
300 if action == "release":
301 # enable site, enable slices,
302 if objname == None: raise Exception("Provide a site name to release")
304 slices = api.GetSlices(api.GetSites(objname, ['slice_ids'])[0]['slice_ids'])
307 obj = PlcObj(objname)
310 obj = PlcObj(sl['name'])
313 if action == "removeall":
314 # remove enable site, enable slices,
315 if objname == None: raise Exception("Provide a site name to release")
317 slices = api.GetSlices(api.GetSites(objname, ['slice_ids'])[0]['slice_ids'])
320 obj = PlcObj(objname)
323 obj = PlcObj(sl['name'])
326 if action == "addall":
327 if objname == None: raise Exception("Provide a site name to release")
329 slices = api.GetSlices(api.GetSites(objname, ['slice_ids'])[0]['slice_ids'])
332 obj = PlcObj(objname)
333 obj.exempt(api,opt.expires)
335 obj = PlcObj(sl['name'])
336 obj.exempt(api,opt.expires)
338 if __name__ == '__main__':