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 --apiurl.
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.
76 Object may be sitename, hostname, or slicename.
78 exempt -- Manage site, node, and slice exemptions from
79 myops policy actions. Object may be sitename,
80 hostname, or slicename.
82 removeall -- Remove all exemptions at site & from nodes, slices
84 exemptall -- Add exemptions at site & to nodes, slices
86 disableall-- Disable everything at a site:
90 enableall -- Release everything at a site:
95 # setup session and save target name 'plc' for future calls
96 myops.py plc --apiurl https://boot.planet-lab.org/PLCAPI/ \\
97 --username soltesz@cs.princeton.edu
99 # list current exemptions at plc
102 # to list only one site (nothing will show if no exemption is present)
103 myops.py plc exempt princeton
105 # add an exemption until a specific date
106 myops.py plc exempt princeton --expires 20120131
108 # remove this exemption
109 myops.py plc exempt princeton -r
111 # exempt just a slice, not the whole site.
112 myops.py plc exempt princeton_comon --expires 20120131
114 # re-enable a site & slices then, exempt site & slices for 7 days
115 myops.py plc enableall princeton
116 myops.py plc exemptall princeton --expires 7
121 def unparse_expire_str(value):
123 expires = 60*60*24*30 # 30 days default
125 expires = time.mktime(time.strptime(value, "%Y%m%d")) - time.time()
128 def parse_expire_str(value):
132 elif len(value) <= 3:
134 value = time.strftime("%Y%m%d", time.localtime(time.time()+int(value)*60*60*24))
135 elif len(value) != 8 and value[:3] != "201": # 201 == this decade.
137 raise optparse.OptionValueError
140 class PlcObj(object):
141 def __init__(self, name):
142 if type(name) == type(""):
144 elif type(name) == type({}):
145 if 'login_base' in name:
146 self.name = name['login_base']
147 elif 'hostname' in name:
148 self.name = name['hostname']
150 self.name = name['name']
155 elif '.' in self.name:
161 def list(self,target,action,*vals):
162 if action == "enabled":
163 print ("\t%s %s %s" % (sys.argv[0],target,action)) + (" %-20s --disable" % self.name)
164 elif action == "exempt":
165 print ("\t%s %s %s" % (sys.argv[0],target,action)) + (" %-20s --expires %s" % ((self.name,)+ vals))
167 def enable(self,api,state):
168 if self.kind == 'Slice':
169 # change value of existing slice tag, if it exists.
170 tl = api.GetSliceTags({'name' : self.name, 'tagname' : 'enabled', 'value' : '0' if state else '1'})
172 api.AddSliceTag(self.name, 'enabled', '1' if state else '0')
175 api.UpdateSliceTag(t['slice_tag_id'], {'value' : '1' if state else '0'})
176 elif self.kind == 'Node':
178 api.UpdateNode(self.name, {'boot_state' : 'boot'})
180 api.UpdateNode(self.name, {'boot_state' : 'disabled'})
181 elif self.kind == 'Site':
182 api.UpdateSite(self.name, {'enabled' : state})
184 def exempt(self,api,expires):
186 if self.kind == 'Slice':
187 try: api.AddSliceTag(self.name, 'exempt_slice_until', expires)
188 except: api.UpdateSliceTag(self.name, expires)
189 elif self.kind == 'Node':
190 try: api.AddNodeTag(self.name, 'exempt_node_until', expires)
191 except: api.UpdateNodeTag(self.name, expires)
192 elif self.kind == 'Site':
193 try: api.AddSiteTag(api.GetSites(self.name, ['site_id'])[0]['site_id'], 'exempt_site_until', expires)
194 except: api.UpdateSiteTag(api.GetSiteTags({'login_base' : self.name, 'tagname' : 'exempt_site_until'})[0]['site_tag_id'], expires)
197 if self.kind == 'Slice':
198 tag_id_l = api.GetSliceTags({'name' : self.name, 'tagname' : 'exempt_slice_until'}, ['slice_tag_id'])
199 if len(tag_id_l) > 0:
200 tag_id = tag_id_l[0]['slice_tag_id']
201 api.DeleteSliceTag(tag_id)
202 elif self.kind == 'Node':
203 tag_id_l = api.GetNodeTags({'hostname' : self.name, 'tagname' : 'exempt_node_until'}, ['node_tag_id'])
204 if len(tag_id_l) > 0:
205 tag_id = tag_id_l[0]['node_tag_id']
206 api.DeleteNodeTag(tag_id)
207 elif self.kind == 'Site':
208 tag_id_l = api.GetSiteTags({'login_base' : self.name, 'tagname' : 'exempt_site_until'}, ['site_tag_id'])
209 if len(tag_id_l) > 0:
210 tag_id = tag_id_l[0]['site_tag_id']
211 api.DeleteSiteTag(tag_id)
215 from optparse import OptionParser
217 parser = OptionParser()
219 parser.add_option("-d", "--debug", dest="debug", action="store_true", default=False, help="")
220 parser.add_option("-v", "--verbose", dest="verbose", default=False, help="")
221 parser.add_option("-u", "--apiurl", dest="url", default="https://www.planet-lab.org/PLCAPI/", help="Set PLC URL for action")
222 parser.add_option("-U", "--username", dest="username", default=None, help="Login as username")
223 parser.add_option("-P", "--password", dest="password", default=None, help="Use provided password; otherwise prompt for password")
224 parser.add_option("-e", "--expires", dest="expires", default=None, help="Set expiration date YYYYMMDD (or <days>); default is None (i.e. removed)")
225 parser.add_option("", "--disable", dest="disable", default=False, action="store_true", help="Disable object.")
226 parser.add_option("-r", "--remove", dest="remove", action="store_true", default=False, help="Remove object from exemption" )
227 parser.add_option("-l", "--list", dest="list", action="store_true", default=False, help="List objects with command used to generate them")
228 parser.add_option("-S", "--site", dest="login_base", default=None, help="Act on this site")
229 parser.add_option("-H", "--host", dest="hostname", default=None, help="Act on this node")
230 parser.add_option("-s", "--slice", dest="slicename", default=None, help="Act on this site")
232 (opt, args) = parser.parse_args()
233 opt.expires = parse_expire_str(opt.expires)
240 api = get_plc_api(target, opt.url, opt.username, opt.password, unparse_expire_str(opt.expires), opt.debug)
242 action_list = ['enabled', 'exempt', 'removeall', 'exemptall', 'enableall', 'disableall']
244 for i,action in enumerate(args[1:]):
245 if action in action_list:
246 if len(args) > i+2 and args[i+2] not in action_list:
251 if action == "enabled":
253 if not opt.list and not opt.hostname and not opt.slicename and not opt.login_base:
256 print "Listing only *disabled* objects"
257 sites = api.GetSites({'peer_id' : None, 'enabled': False})
258 nodes = api.GetNodes({'peer_id' : None, 'boot_state' : 'disabled'})
259 slices= api.GetSliceTags({'tagname' : 'enabled'})
261 for (header,objlist) in [("Sites:",sites), ("Nodes:", nodes), ("Slices:", slices)]:
262 if len(objlist) > 0: print header
265 o.list(target, action)
267 if action == "exempt":
268 if not opt.list and not opt.remove and opt.expires == None:
273 # NOTE: this works around a bug as of 2011/12/23 that
274 # deleted sites do not also delete all associated site tags.
275 site_lb = [ l['login_base'] for l in api.GetSites({'peer_id' : None}, ['login_base']) ]
276 sites = api.GetSiteTags({'tagname' : 'exempt_site_until'})
277 sites = filter(lambda x: x['login_base'] in site_lb, sites)
279 nodes = api.GetNodeTags({'tagname' : 'exempt_node_until'})
280 slices = api.GetSliceTags({'tagname' : 'exempt_slice_until'})
282 try: sites = api.GetSiteTags({'login_base': objname, 'tagname' : 'exempt_site_until'})
284 try: nodes = api.GetNodeTags({'hostname' : objname, 'tagname' : 'exempt_node_until'})
286 try: slices = api.GetSliceTags({'name' : objname, 'tagname' : 'exempt_slice_until'})
289 for (header,objlist) in [("Sites:",sites), ("Nodes:", nodes), ("Slices:", slices)]:
290 if len(objlist) > 0: print header
293 o.list(target, action, t['value'])
295 if opt.remove or opt.expires:
296 obj = PlcObj(objname)
297 # if opt.expires == None, the exemption will be removed.
298 obj.exempt(api,opt.expires)
300 if action == "disableall":
301 if objname == None: raise Exception("Provide a site name to disable")
302 # disable site, disable slices,
304 slices = api.GetSlices(api.GetSites(objname, ['slice_ids'])[0]['slice_ids'])
307 obj = PlcObj(objname)
308 obj.enable(api,False)
310 obj = PlcObj(sl['name'])
311 obj.enable(api,False)
313 if action == "enableall":
314 # enable site, enable slices,
315 if objname == None: raise Exception("Provide a site name to enableall")
317 slices = api.GetSlices(api.GetSites(objname, ['slice_ids'])[0]['slice_ids'])
320 obj = PlcObj(objname)
323 obj = PlcObj(sl['name'])
326 if action == "removeall":
327 # remove enable site, enable slices,
328 if objname == None: raise Exception("Provide a site name to remove")
330 slices = api.GetSlices(api.GetSites(objname, ['slice_ids'])[0]['slice_ids'])
333 obj = PlcObj(objname)
336 obj = PlcObj(sl['name'])
339 if action == "exemptall":
340 if objname == None: raise Exception("Provide a site name to exempt")
342 slices = api.GetSlices(api.GetSites(objname, ['slice_ids'])[0]['slice_ids'])
345 obj = PlcObj(objname)
346 obj.exempt(api,opt.expires)
348 obj = PlcObj(sl['name'])
349 obj.exempt(api,opt.expires)
351 if __name__ == '__main__':