From f0b0b138c0a466eb3c15ae172ae77597dc376159 Mon Sep 17 00:00:00 2001 From: Stephen Soltesz Date: Fri, 23 Dec 2011 23:14:00 +0000 Subject: [PATCH] adding myops.py a CLI to add/remove exemptions to sites/nodes/slices --- commands/myops.py | 339 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100755 commands/myops.py diff --git a/commands/myops.py b/commands/myops.py new file mode 100755 index 0000000..9f7b88b --- /dev/null +++ b/commands/myops.py @@ -0,0 +1,339 @@ +#!/usr/bin/python + +import os +import sys +import string +import time + +import getopt +import sys +import os +import xml, xmlrpclib +from getpass import getpass +from operator import attrgetter, itemgetter + +def get_plc_api(target, url, username, password, expires, debug_mode): + # Either read session from disk or create it and save it for later + metasession = "%s/%s_%s" % (os.environ['HOME'], ".metasession", target) + if os.path.exists(metasession): + (localurl,session) = open(metasession, 'r').read().strip().split() + plc = xmlrpclib.Server(localurl, verbose=False, allow_none=True) + else: + plc = xmlrpclib.Server(url, verbose=False, allow_none=True) + if password == None: password = getpass() + auth = {'Username' : username, + 'AuthMethod' : 'password', + 'AuthString' : password} + session = plc.GetSession(auth, expires*(60*60*24)) + with open(metasession, 'w') as f: f.write("%s %s\n" % (url,session)) # 'with' auto-closes + + auth = {'AuthMethod' : 'session', 'session' : session} + + class PLC: + def __init__(self, plc, auth): + self.plc = plc + self.auth = auth + + def __getattr__(self, name): + method = getattr(self.plc, name) + if method is None: + raise AssertionError("Method does not exist: %s" % method) + if not debug_mode or ('Get' in name or 'AuthCheck' in name): + return lambda *params : method(self.auth, *params) + else: + def call(name,*params): + print "DEBUG not running: %s(%s)" % (name, params) + return lambda *params : call(name,*params) + + plc_api = PLC(plc, auth) + try: + # make sure the session is working + plc_api.AuthCheck() + except: + # everything worked except the auth check. try again asking for passwd. + plc_api = get_plc_api(target, url, username, None, expires, debug_mode) + + return plc_api + +def usage(parser): + print """ +myops.py [] [args] + MYOPS CLI uses sessions to avoid storing passwords. + You choose the session expiration via --expires days. + +TARGET: + When your session is saved it is identified by your given 'target' + name. This is a unique string you chose to identify the --url. + For example, one might use: + plc + vicci + test + vini + +ACTION: + Connect to TARGET and perform ACTION. The current actions are: + enabled -- Manage site, node, and slice 'enabled' states. + + exempt -- Manage site, node, and slice exemptions from + myops policy actions. + + removeall -- Remove all exemptions at site from site, nodes, slices + addall -- Add exemptions at site to site, nodes, slices + + freeze -- Clamp down everything at a site: + disable site, + disable slices, + + release -- Release everything at a site: + re-enable site, + re-enable slices, +EXAMPLES: + # setup session and save target name 'plc' for future calls + myops.py plc --apiurl https://boot.planet-lab.org/PLCAPI/ \\ + --username soltesz@cs.princeton.edu + + # list current exemptions at plc + myops.py plc exempt + + # to list only one site (nothing will show if no exemption is present) + myops.py plc exempt princeton + + # add a new exemption to site 'princeton' for a day + myops.py plc exempt princeton -a + + # add a new exemption to a specific date + myops.py plc exempt princeton -a --expires 20120131 + +""" + parser.print_help() + +def unparse_expire_str(value): + expires = time.mktime(time.strptime(value, "%Y%m%d")) - time.time() + return int(expires) + +def parse_expire_str(value): + import optparse + if value == "0": + value = "20990101" + elif len(value) <= 3: + # days from now + value = time.strftime("%Y%m%d", time.localtime(time.time()+int(value)*60*60*24)) + elif len(value) != 8 and value[:3] != "201": # 201 == this decade. + # flip out + raise optparse.OptionValueError + return value + +class PlcObj(object): + def __init__(self, name): + if type(name) == type(""): + self.name = name + elif type(name) == type({}): + if 'login_base' in name: + self.name = name['login_base'] + elif 'hostname' in name: + self.name = name['hostname'] + elif 'name' in name: + self.name = name['name'] + + self.kind = None + if '_' in self.name: + kind = 'Slice' + elif '.' in self.name: + kind='Node' + else: + kind='Site' + self.kind = kind + + def list(self,target,action,*vals): + if action == "enabled": + print ("\t%s %s %s" % (sys.argv[0],target,action)) + (" %-20s --disable" % self.name) + elif action == "exempt": + print ("\t%s %s %s" % (sys.argv[0],target,action)) + (" %-20s -a --expires %s" % ((self.name,)+ vals)) + + def enable(self,api,state): + if self.kind == 'Slice': + # change value of existing slice tag, if it exists. + tl = api.GetSliceTags({'name' : self.name, 'tagname' : 'enabled', 'value' : '0' if state else '1'}) + if len(tl) == 0: + api.AddSliceTag(self.name, 'enabled', '1' if state else '0') + else: + for t in tl: + api.UpdateSliceTag(t['slice_tag_id'], {'value' : '1' if state else '0'}) + elif self.kind == 'Node': + if state == True: + api.UpdateNode(self.name, {'boot_state' : 'boot'}) + else: + api.UpdateNode(self.name, {'boot_state' : 'disabled'}) + elif self.kind == 'Site': + api.UpdateSite(self.name, {'enabled' : state}) + + def exempt(self,api,expires): + if expires != None: + if self.kind == 'Slice': + try: api.AddSliceTag(self.name, 'exempt_slice_until', expires) + except: api.UpdateSliceTag(self.name, expires) + elif self.kind == 'Node': + try: api.AddNodeTag(self.name, 'exempt_node_until', expires) + except: api.UpdateNodeTag(self.name, expires) + elif self.kind == 'Site': + try: api.AddSiteTag(api.GetSites(self.name, ['site_id'])[0]['site_id'], 'exempt_site_until', expires) + except: api.UpdateSiteTag(api.GetSiteTags({'login_base' : self.name, 'tagname' : 'exempt_site_until'})[0]['site_tag_id'], expires) + else: + # remove + if self.kind == 'Slice': + tag_id_l = api.GetSliceTags({'name' : self.name, 'tagname' : 'exempt_slice_until'}, ['slice_tag_id']) + if len(tag_id_l) > 0: + tag_id = tag_id_l[0]['slice_tag_id'] + api.DeleteSliceTag(tag_id) + elif self.kind == 'Node': + tag_id_l = api.GetNodeTags({'hostname' : self.name, 'tagname' : 'exempt_node_until'}, ['node_tag_id']) + if len(tag_id_l) > 0: + tag_id = tag_id_l[0]['node_tag_id'] + api.DeleteNodeTag(tag_id) + elif self.kind == 'Site': + tag_id_l = api.GetSiteTags({'login_base' : self.name, 'tagname' : 'exempt_site_until'}, ['site_tag_id']) + if len(tag_id_l) > 0: + tag_id = tag_id_l[0]['site_tag_id'] + api.DeleteSiteTag(tag_id) + + +def main(): + from optparse import OptionParser + copy = False + parser = OptionParser() + + parser.add_option("-d", "--debug", dest="debug", action="store_true", default=False, help="") + parser.add_option("-v", "--verbose", dest="verbose", default=False, help="") + parser.add_option("-u", "--apiurl", dest="url", default="https://www.planet-lab.org/PLCAPI/", help="Set PLC URL for action") + parser.add_option("-U", "--username", dest="username", default=None, help="Login as username") + parser.add_option("-P", "--password", dest="password", default=None, help="Use provided password; otherwise prompt for password") + parser.add_option("-e", "--expires", dest="expires", default="1", help="Set expiration date YYYYMMDD; default is 1 day from now.") + parser.add_option("", "--disable", dest="disable", default=False, action="store_true", help="Disable object.") + parser.add_option("-r", "--remove", dest="remove", action="store_true", default=False, help="Remove object from exemption" ) + parser.add_option("-l", "--list", dest="list", action="store_true", default=False, help="List objects with command used to generate them") + parser.add_option("-a", "--add", dest="add", action="store_true", default=False, help="Add exempt object") + parser.add_option("-S", "--site", dest="login_base", default=None, help="Act on this site") + parser.add_option("-H", "--host", dest="hostname", default=None, help="Act on this node") + parser.add_option("-s", "--slice", dest="slicename", default=None, help="Act on this site") + + (opt, args) = parser.parse_args() + opt.expires = parse_expire_str(opt.expires) + + if len(args) == 0: + usage(parser) + sys.exit(1) + + target = args[0]; + api = get_plc_api(target, opt.url, opt.username, opt.password, unparse_expire_str(opt.expires), opt.debug) + + action_list = ['enabled', 'exempt', 'removeall', 'addall', 'release', 'freeze'] + + for i,action in enumerate(args[1:]): + if action in action_list: + if len(args) > i+2 and args[i+2] not in action_list: + objname = args[i+2] + else: + objname = None + + if action == "enabled": + + if not opt.list and not opt.hostname and not opt.slicename and not opt.login_base: + opt.list = True + if opt.list: + print "Listing only *disabled* objects" + sites = api.GetSites({'peer_id' : None, 'enabled': False}) + nodes = api.GetNodes({'peer_id' : None, 'boot_state' : 'disabled'}) + slices= api.GetSliceTags({'tagname' : 'enabled'}) + + for (header,objlist) in [("Sites:",sites), ("Nodes:", nodes), ("Slices:", slices)]: + if len(objlist) > 0: print header + for t in objlist: + o = PlcObj(t) + o.list(target, action) + + if action == "exempt": + if not opt.list and not opt.remove and not opt.add: + opt.list = True + + if opt.list: + if objname == None: + sites = api.GetSiteTags({'tagname' : 'exempt_site_until'}) + nodes = api.GetNodeTags({'tagname' : 'exempt_node_until'}) + slices = api.GetSliceTags({'tagname' : 'exempt_slice_until'}) + else: + try: sites = api.GetSiteTags({'login_base': objname, 'tagname' : 'exempt_site_until'}) + except: sites = [] + try: nodes = api.GetNodeTags({'hostname' : objname, 'tagname' : 'exempt_node_until'}) + except: nodes = [] + try: slices = api.GetSliceTags({'name' : objname, 'tagname' : 'exempt_slice_until'}) + except: slices = [] + + for (header,objlist) in [("Sites:",sites), ("Nodes:", nodes), ("Slices:", slices)]: + if len(objlist) > 0: print header + for t in objlist: + o = PlcObj(t) + o.list(target, action, t['value']) + + if opt.remove: + if objname == None: raise Exception("provide an object name to remove exemption") + obj = PlcObj(objname) + obj.exempt(api,None) + + if opt.add: + if objname == None: raise Exception("provide an object name to add exemption") + obj = PlcObj(objname) + obj.exempt(api,opt.expires) + + if action == "freeze": + if objname == None: raise Exception("Provide a site name to freeze") + # disable site, disable slices, + try: + slices = api.GetSlices(api.GetSites(objname, ['slice_ids'])[0]['slice_ids']) + except: + slices = [] + obj = PlcObj(objname) + obj.enable(api,False) + for sl in slices: + obj = PlcObj(sl['name']) + obj.enable(api,False) + + if action == "release": + # enable site, enable slices, + if objname == None: raise Exception("Provide a site name to release") + try: + slices = api.GetSlices(api.GetSites(objname, ['slice_ids'])[0]['slice_ids']) + except: + slices = [] + obj = PlcObj(objname) + obj.enable(api,True) + for sl in slices: + obj = PlcObj(sl['name']) + obj.enable(api,True) + + if action == "removeall": + # remove enable site, enable slices, + if objname == None: raise Exception("Provide a site name to release") + try: + slices = api.GetSlices(api.GetSites(objname, ['slice_ids'])[0]['slice_ids']) + except: + slices = [] + obj = PlcObj(objname) + obj.exempt(api,None) + for sl in slices: + obj = PlcObj(sl['name']) + obj.exempt(api,None) + + if action == "addall": + if objname == None: raise Exception("Provide a site name to release") + try: + slices = api.GetSlices(api.GetSites(objname, ['slice_ids'])[0]['slice_ids']) + except: + slices = [] + obj = PlcObj(objname) + obj.exempt(api,opt.expires) + for sl in slices: + obj = PlcObj(sl['name']) + obj.exempt(api,opt.expires) + +if __name__ == '__main__': + main() -- 2.43.0