#!/usr/bin/env python # 2014 (c) Paul C. Buetow import argparse import base64 import bigsuds import getpass import os import pprint import re import readline import socket import sys import ConfigParser from inspect import isfunction from colorama import Fore, Back, Style __program__ = 'fapi' __version__ = 'VERSION_DEVEL' # Replaced by a Makefile target __prompt__ = '> ' # Default prompt __interactive__ = False class FapiBase(object): ''' Some base functionality used by any Fapi class ''' def __init__(self, args ): ''' Initializes the base ''' self._args = args def verbose(self, message): ''' Prints an informational message to stderr ''' if self._args.v: self.info(message, Style.DIM) def info(self, message, color=''): ''' Prints an informational message to stderr ''' if self._args.d: color = reset = '' else: reset = '' if color == '' else Style.RESET_ALL print >> sys.stderr, (color+'%s'+reset) % message def out(self, result, color=''): ''' Prints an iControl result to stdout ''' if result != None: if self._args.l and isinstance(result, (list, tuple)): print"\n".join(result) else: pp = pprint.PrettyPrinter(indent=4) pp.pprint(result) def print_version(self): ''' Prints out the version ''' self.info('This is %s version %s' % (__program__, __version__), Fore.RED) def print_synopsis(self): ''' Prints the full Synopsis string ''' if self._args.d: style = reset = '' else: style = Style.DIM reset = Style.RESET_ALL self.print_version() print "\n".join([ '', 'Synopsis:', style + ' monitor', ' monitor NAME get desc|state', ' node', ' node NODENAME create|delete', ' node NODENAME get detail|status', ' pool', ' pool NAME add member MEMBER:PORT', ' pool NAME add monitor MONITOR', ' pool NAME create [LIST,OF,POOL,MEMBERS:PORT]', ' pool NAME delete', ' pool NAME del member MEMBER:PORT', ' pool NAME del monitors', ' pool NAME get detail|lbmethod|members|monitor|status', ' pool NAME set lbmethod LBMETHOD', ' vip', ' vip NAME create NETMASK', ' vip NAME get arp|detail|status|tgroup', ' vip NAME set arp enabled|disabled', ' vip NAME set tgroup TGROUP', ' vserver', ' vserver NAME create [protocol] [profile] [poolname] [mask]', ' vserver NAME delete', ' vserver NAME get brief|detail|status|vlan', ' vserver NAME set nat|pat disabled|enabled', ' vserver NAME set pool POOLNAME', ' vserver NAME set snat automap|none', ' vserver NAME set vlan [list,of,vlans,to,allow]' + reset, 'The following partially needs admininstrator privileges on / and /Common', style + ' -f Common -b balancer.example.com selfip', ' -f Common -b balancer.example.com selfip NAME create NETMASK VLANNAME [TGROUP]', ' -f Common -b balancer.example.com selfip NAME delete', ' -f Common -b balancer.example.com selfip NAME get detail|tgroup', ' -f Common -b balancer.example.com selfip NAME set tgroup TGROUP', ' -f Common tgroup', ' -f Common tgroup NAME add ha_order DEVICE ORDER', ' -f Common tgroup NAME create', ' -f Common tgroup NAME delete', ' -f Common tgroup NAME get detail', ' -f Common tgroup NAME get ha_order', ' -f Common tgroup NAME remove all_ha_orders', ' -f Common tgroup NAME remove ha_order DEVICE ORDER', ' -f Common vlan', ' -f Common vlan NAME create tagged VLANID internal|external|...', ' -f Common vlan NAME delete', ' -f Common vlan NAME get detail', ' -f / folder', ' -f / folder NAME create|delete', ' -f / folder NAME get detail|dgroup|tgroup', ' -f / folder NAME set dgroup|tgroup DGROUP|TGROUP' + reset, 'Please consult the manpage for examples.', ]) if __interactive__: print '(To exit interactive shell use \'Ctrl+d\')' class ArgumentParser(FapiBase): ''' The argument parser class ''' def __init__(self): ''' Initialize the argument parser ''' self._parser = parser = argparse.ArgumentParser(add_help=False) parser.add_argument('-b', action='store', help='Forces to use the secified loadbalancer (overwrites -e)') parser.add_argument('-d', action='store_true', help='Disable colorful output') parser.add_argument('-e', action='store', help='Env to use, e.g. dev,qa,live', default='qa') parser.add_argument('-f', action='store', help='Overwrite partition/folder from fapi.conf') parser.add_argument('-h', action='store_true', help='Print this help') parser.add_argument('-i', action='store_true', help='Interactive shell') parser.add_argument('-l', action='store_true', help='Use list output') parser.add_argument('-n', action='store_true', help='No-op (don\'t do actual stuff)') parser.add_argument('-v', action='store_true', help='Verbose') parser.add_argument('-V', action='store_true', help='Print program version') parser.add_argument('-C', action='store', help='Config file', default=os.path.expanduser('~') + '/.fapi.conf') parser.add_argument('what', nargs='?', help='node|pool|monitor|vserver|...') parser.add_argument('name', nargs='?', help='The object name to operate on') parser.add_argument('sub', nargs='?', help='First sub command') parser.add_argument('sub2', nargs='?', help='Second sub command') parser.add_argument('sub3', nargs='?', help='Third sub command') parser.add_argument('sub4', nargs='?', help='Fourth sub command') parser.add_argument('sub5', nargs='?', help='Fith sub command') def parse(self, arguments = []): ''' Parse the arguments ''' if not arguments: args = self._args = self._parser.parse_args() else: args = self._args = self._parser.parse_args(arguments) if args.h: self._parser.print_help() print '' self.print_synopsis() return None elif args.V: self.print_version() return None return args class Fapi(FapiBase): ''' The main F5 API Tool Object ''' __loggedin = False def __init__(self, args): ''' Initialize the config file, username and password ''' FapiBase.__init__(self, args) self._config = ConfigParser.ConfigParser() self._config.read(args.C) if args.f != None: self._folder = args.f else: self._folder = self._config.get('fapi', 'folder') def __login(self): ''' Logs into the F5 BigIP SOAP API and changes the folder/adm. partition''' if __loggedin: return c = self._config a = self._args if c.has_option('fapi', 'username'): username = c.get('fapi', 'username') else: username = getpass.getuser() if c.has_option('fapi', 'password64'): password = base64.decodestring(c.get('fapi', 'password64')) else: prompt = 'Enter API password for user %s: ' % username password = getpass.getpass(prompt) self.verbose('Login to BigIP API with user %s' % username) # Try a comma separated lists of F5 boxes, use the first one loadbalancers = a.b if a.b else c.get('fapi', 'loadbalancers_' + a.e) err = None for loadbalancer in loadbalancers.split(','): try: self.verbose('Trying to login to \'%s\'' % loadbalancer) self._f5 = bigsuds.BIGIP(hostname = loadbalancer, username = username, password = password) self._f5.Management.Partition.set_active_partition(self._folder) self.verbose('Set folder/adm. partition to \'%s\'' % self._folder) err = None break except Exception, e: err = '%s:%s' % (loadbalancer, e) pass if err: raise Exception(err) __loggedin = True def lookup(self, what): ''' Does a DNS lookup to fetch the name (mostly FQDN) and the IPs hostname will be looked up to (fqdn,ip,80) fqdn will be looked up to (fqdn,ip,80) ip will be looked up to (ip,ip,80) hostname:port will be looked up to (fqdn,ip,port) fqdn:port will be looked up to (fqdn,ip,port) ip:port will be looked up to (ip,ip,port) hostname:ip:port will be looked up to (fqdn,ip,port) fqdn:ip:port will be looked up to (fqdn,ip,port) ip1:ip2:port will be looked up to (ip1,ip2,port) ''' a = self._args tmp = what.split(':') if 1 == len(tmp): what = tmp[0] ip = None port = 80 elif 2 == len(tmp): what = tmp[0] ip = None port = tmp[1] elif 3 == len(tmp): what = tmp[0] ip = tmp[1] port = tmp[2] else: raise Exception('\'%s\' can have at most three kolons' % what) if ip: name = what else: data = socket.gethostbyname_ex(what) name = data[0] ips = data[2] if len(ips) > 1: raise Exception('\'%s\' resolves to multiple ips \'%s\'' % (name, ips)) ip = ips[0] self.verbose("Looked \'%s\' up to name:\'%s\', ip:\'%s\', port:\'%s\'" % (what, name, ip, port)) return (name, ip, port) def li(self, name): ''' Checks if name is a list and returns a list if not. ''' return name if isinstance(name, list) else [name] def pa(self, length, params): ''' Checks if name is a list and returns a list of params if so ''' paramlist = [] if length > 1: for _ in xrange(length): paramlist.append(params) else: paramlist.append(params) return paramlist def __do_node(self, f5): ''' Do stuff concerning nodes ''' a = self._args if not a.name: return lambda: f5().get_list() if a.sub == 'get': if a.sub2 == 'detail': def detail(f5): d = {} d['connection_limit'] = f5().get_connection_limit(li(a.name)) d['default_node_monitor'] = f5().get_default_node_monitor() d['description'] = f5().get_description(li(a.name)) d['dynamic_ratio'] = f5().get_dynamic_ratio_v2(li(a.name)) d['monitor_instance'] = f5().get_monitor_instance(li(a.name)) d['monitor_rule'] = f5().get_monitor_rule(li(a.name)) d['monitor_status'] = f5().get_monitor_status(li(a.name)) d['object_status'] = f5().get_object_status(li(a.name)) d['rate_limit'] = f5().get_rate_limit(li(a.name)) d['ratio'] = f5().get_ratio(li(a.name)) d['session_status'] = f5().get_session_status(li(a.name)) return d return lambda: detail(f5) if a.sub2 == 'status': return lambda: f5().get_monitor_status(li(a.name)) elif a.sub == 'create': fqdn_or_ip, ip, _ = self.lookup(a.name) return lambda: f5().create(li(fqdn_or_ip), [ip], pa(len(fqdn_or_ip),0)) elif a.sub == 'delete': fqdn_or_ip, _, _ = self.lookup(a.name) return lambda: f5().delete_node_address([fqdn_or_ip]) def __do_monitor(self, f5): ''' Do stuff concerning monitor templates ''' a = self._args if not a.name: return lambda: f5().get_template_list() if a.sub == 'get': monitorname = a.sub3 if a.sub2 == 'desc': return lambda: f5().get_description([monitorname]) if a.sub2 == 'state': return lambda: f5().get_template_state([monitorname]) def __do_pool(self, f5): ''' Do stuff concerning pools ''' a = self._args if not a.name: return lambda: f5().get_list() if a.sub == 'get': if a.sub2 == 'detail': def detail(f5): d = {} d['allow_nat_state'] = f5().get_allow_nat_state([a.name]) d['allow_snat_state'] = f5().get_allow_snat_state([a.name]) d['description'] = f5().get_description([a.name]) d['lb_method'] = f5().get_lb_method([a.name]) d['member'] = f5().get_member_v2([a.name]) d['object_status'] = f5().get_object_status([a.name]) d['profile'] = f5().get_profile([a.name]) return d return lambda: detail(f5) elif a.sub2 == 'lbmethod': return lambda: f5().get_lb_method([a.name]) elif a.sub2 == 'members': return lambda: f5().get_member_v2([a.name]) elif a.sub2 == 'monitor': return lambda: f5().get_monitor_instance([a.name]) elif a.sub2 == 'status': return lambda: f5().get_object_status([a.name]) elif a.sub == 'set': if a.sub2 == 'lbmethod': lbmethod = a.sub3 return lambda: f5().set_lb_method([a.name], [lbmethod]) elif a.sub == 'create': poolmembers = [] method = 'LB_METHOD_ROUND_ROBIN' if a.sub3: for x in a.sub3.split(','): fqdn_or_ip, ip, port = self.lookup(x) pm = { 'address' : fqdn_or_ip, 'port' : port } poolmembers.append(pm) return lambda: f5().create_v2([a.name],[method],[poolmembers]) elif a.sub == 'delete': return lambda: f5().delete_pool([a.name]) elif a.sub == 'add': if a.sub2 == 'member': fqdn_or_ip, _, port = self.lookup(a.sub3) member = [{ 'address' : fqdn_or_ip, 'port' : port }] return lambda: f5().add_member_v2([a.name], [member]) elif a.sub2 == 'monitor': monitorname = a.sub3 rule = { 'type': 'MONITOR_RULE_TYPE_SINGLE', 'quorum': long(0), 'monitor_templates': [ monitorname ], } association = { 'pool_name': a.name, 'monitor_rule': rule } return lambda: f5().set_monitor_association([association]) elif a.sub == 'del': if a.sub2 == 'member': fqdn_or_ip, _, port = self.lookup(a.sub3) member = [{ 'address' : fqdn_or_ip, 'port' : port }] return lambda: f5().remove_member_v2([a.name], [member]) elif a.sub2 == 'monitors': # Removes all monitor associations, not just one return lambda: f5().remove_monitor_association([a.name]) def __do_vserver(self, f5): ''' Do stuff concerning virtual servers ''' a = self._args if not a.name: return lambda: f5().get_list() # Check for Pattern like /partition/foo-bar.example.com_443 m = re.match('^(.*)_(\d+)$', a.name) if m: fqdn_or_ip = m.group(1) port = m.group(2) _, ip, _ = self.lookup(fqdn_or_ip) else: fqdn_or_ip, ip, port = self.lookup(a.name) name = fqdn_or_ip + '_' + port if a.sub == 'get': if a.sub2 == 'detail': def detail(f5): d = {} d['actual_hardware_acceleration'] = f5().get_actual_hardware_acceleration([name]) d['auto_lasthop'] = f5().get_auto_lasthop([name]) d['bw_controller_policy'] = f5().get_bw_controller_policy([name]) d['clone_pool'] = f5().get_clone_pool([name]) d['connection_limit'] = f5().get_connection_limit([name]) d['default_pool_name'] = f5().get_default_pool_name([name]) d['description'] = f5().get_description([name]) d['destination'] = f5().get_destination_v2([name]) d['enabled_state'] = f5().get_enabled_state([name]) d['fallback_persistence_profile'] = f5().get_fallback_persistence_profile([name]) d['gtm_score'] = f5().get_gtm_score([name]) d['last_hop_pool'] = f5().get_last_hop_pool([name]) d['object_status'] = f5().get_object_status([name]) d['persistence_profile'] = f5().get_persistence_profile([name]) d['profile'] = f5().get_profile([name]) d['protocol'] = f5().get_protocol([name]) d['rule'] = f5().get_rule([name]) d['snat_pool'] = f5().get_snat_pool([name]) d['snat_type'] = f5().get_snat_type([name]) d['source_address'] = f5().get_source_address([name]) d['source_address_translation_lsn_pool'] = f5().get_source_address_translation_lsn_pool([name]) d['source_address_translation_snat_pool'] = f5().get_source_address_translation_snat_pool([name]) d['source_address_translation_type'] = f5().get_source_address_translation_type([name]) d['source_port_behavior'] = f5().get_source_port_behavior([name]) d['translate_address_state'] = f5().get_translate_address_state([name]) d['translate_port_state'] = f5().get_translate_port_state([name]) d['type'] = f5().get_type([name]) d['vlan'] = f5().get_vlan([name]) return d return lambda: detail(f5) elif a.sub2 == 'brief': def brief(f5): d = {} d['actual_hardware_acceleration'] = f5().get_actual_hardware_acceleration([name]) d['default_pool_name'] = f5().get_default_pool_name([name]) d['destination'] = f5().get_destination_v2([name]) d['enabled_state'] = f5().get_enabled_state([name]) d['object_status'] = f5().get_object_status([name]) d['persistence_profile'] = f5().get_persistence_profile([name]) d['profile'] = f5().get_profile([name]) d['protocol'] = f5().get_protocol([name]) d['translate_address_state'] = f5().get_translate_address_state([name]) d['translate_port_state'] = f5().get_translate_port_state([name]) d['type'] = f5().get_type([name]) return d return lambda: brief(f5) elif a.sub2 == 'status': return lambda: f5().get_object_status([name]) elif a.sub2 == 'vlan': return lambda: f5().get_vlan([name]) elif a.sub == 'create': protocol = a.sub2 if a.sub2 else 'PROTOCOL_TCP' if a.sub3: profile = a.sub3 elif protocol == 'PROTOCOL_UDP': profile = 'udp' else: profile = 'tcp' poolname = a.sub4 netmask = a.sub5 if a.sub5 else '255.255.255.255' vserver = { 'name': name, 'address': ip, 'port': port, 'protocol': protocol, } resource = { 'type': 'RESOURCE_TYPE_POOL' } if poolname: resource['default_pool_name'] = poolname profile = { 'profile_context': 'PROFILE_CONTEXT_TYPE_ALL', 'profile_name': profile, } self.verbose("vserver:%s netmask:%s resource:%s, profile:%s" % (vserver, netmask, resource, profile)) def vserver_create(): f5().create([vserver], [netmask], [resource], [[profile]]) # Auto disable NAT and PAT if nPath if profile['profile_name'] == 'nPath': f5().set_translate_address_state([name], ['STATE_DISABLED']) f5().set_translate_port_state([name], ['STATE_DISABLED']) return lambda: vserver_create() elif a.sub == 'delete': return lambda: f5().delete_virtual_server([name]) elif a.sub == 'set': if a.sub2 == 'pool': poolname = a.sub3 return lambda: f5().set_default_pool_name([name], [poolname]) elif a.sub2 == 'nat': if a.sub3 == 'disabled': return lambda: f5().set_translate_address_state([name], ['STATE_DISABLED']) elif a.sub3 == 'enabled': return lambda: f5().set_translate_address_state([name], ['STATE_ENABLED']) elif a.sub2 == 'pat': if a.sub3 == 'disabled': return lambda: f5().set_translate_port_state([name], ['STATE_DISABLED']) elif a.sub3 == 'enabled': return lambda: f5().set_translate_port_state([name], ['STATE_ENABLED']) elif a.sub2 == 'snat': if a.sub3 == 'none': return lambda: f5().set_source_address_translation_none([name]) elif a.sub3 == 'automap': return lambda: f5().set_source_address_translation_automap([name]) elif a.sub2 == 'vlan': vlans = a.sub3.split(',') if a.sub3 else [] vlan_filter_list = { 'state': 'STATE_ENABLED', 'vlans': vlans } return lambda: f5().set_vlan([name], [vlan_filter_list]) def __do_vip(self, f5): ''' Do stuff concerning virtual addresses ''' a = self._args if not a.name: return lambda: f5().get_list() # Check for Pattern like /partition/foo-bar.example.com_443 m = re.match('^(.*)_(\d+)$', a.name) if m: fqdn_or_ip = m.group(1) _, ip, _ = self.lookup(fqdn_or_ip) else: _, ip, _ = self.lookup(a.name) name = ip # Do the actual stuff if a.sub == 'get': if a.sub2 == 'detail': def detail(f5): d = {} d['address'] = f5().get_address([name]) d['arp_state'] = f5().get_arp_state([name]) d['enabled_state'] = f5().get_enabled_state([name]) d['is_floating_state'] = f5().get_is_floating_state([name]) d['netmask'] = f5().get_netmask([name]) d['object_status'] = f5().get_object_status([name]) d['traffic_group'] = f5().get_traffic_group([name]) d['is_traffic_group_inherited'] = f5().is_traffic_group_inherited([name]) return d return lambda: detail(f5) elif a.sub2 == 'arp': return lambda: f5().get_arp-state([name]) elif a.sub2 == 'status': return lambda: f5().get_object_status([name]) elif a.sub2 == 'tgroup': return lambda: f5().get_traffic_group([name]) elif a.sub == 'create': netmask = a.sub2 return lambda: f5().create([name], [name], [netmask]) elif a.sub == 'delete': return lambda: f5().delete_virtual_address([name]) elif a.sub == 'set': if a.sub2 == 'arp': if a.sub3 == 'disabled': return lambda: f5().set_arp_state([name], ['STATE_DISABLED']) elif a.sub3 == 'enabled': return lambda: f5().set_arp_state([name], ['STATE_ENABLED']) elif a.sub2 == 'tgroup': tgroup = a.sub3 return lambda: f5().set_traffic_group([name], [tgroup]) def __do_vlan(self, f5): ''' Do stuff concerning VLANs ''' a = self._args if not a.name: return lambda: f5().get_list() if a.sub == 'get': if a.sub2 == 'detail': def detail(f5): d = {} d['auto_lasthop'] = f5().get_auto_lasthop([a.name]) d['vlan_id'] = f5().get_vlan_id([a.name]) d['member'] = f5().get_member([a.name]) d['mtu'] = f5().get_mtu([a.name]) return d return lambda: detail(f5) elif a.sub == 'create': if a.sub2 == 'tagged': vlanid = a.sub3 members = { 'member_name': a.sub4, 'member_type': 'MEMBER_TRUNK', 'tag_state': 'MEMBER_TAGGED', } return lambda: f5().create_v2([a.name], [vlanid], [[members]], ['STATE_ENABLED'], [10]) elif a.sub == 'delete': return lambda: f5().delete_vlan([a.name]) def __do_selfip(self, f5): ''' Do stuff concerning Self IPs ''' a = self._args if not a.name: return lambda: f5().get_list() if a.sub == 'get': if a.sub2 == 'detail': def detail(f5): d = {} d['address'] = f5().get_address([a.name]) d['description'] = f5().get_description([a.name]) d['floating_state'] = f5().get_floating_state([a.name]) d['traffic_group'] = f5().get_traffic_group([a.name]) return d return lambda: detail(f5) elif a.sub2 == 'tgroup': return lambda: f5().get_traffic_group([a.name]) elif a.sub2 == 'address': return lambda: f5().get_address([a.name]) if a.sub == 'set': if a.sub2 == 'tgroup': tgroup = a.sub3 return lambda: f5().set_traffic_group([a.name], [tgroup]) elif a.sub == 'create': _, ip, _ = self.lookup(a.name) netmask = a.sub2 vlanname = a.sub3 trafficgroup = a.sub4 if a.sub4 else 'traffic-group-local-only' # A floating traffic group will auto set floating state to ENABLED, regardless of the .create call. return lambda: f5().create([a.name], [vlanname], [ip], [netmask], [trafficgroup], ['STATE_DISABLED']) elif a.sub == 'delete': return lambda: f5().delete_self_ip([a.name]) def __do_folder(self, f5): ''' Do stuff concerning administrative Partitions''' a = self._args if not a.name: return lambda: f5().get_list() if a.sub == 'create': return lambda: f5().create([a.name]) elif a.sub == 'delete': return lambda: f5().delete_folder([a.name]) elif a.sub == 'get': if a.sub2 == 'detail': def detail(f5): d = {} d['description']= f5().get_description([a.name]) d['device_group']= f5().get_device_group([a.name]) d['traffic_group']= f5().get_traffic_group([a.name]) d['is_device_group_inherited']= f5().is_device_group_inherited([a.name]) d['is_traffic_group_inherited']= f5().is_traffic_group_inherited([a.name]) d['is_traffic_group_inherited']= f5().is_traffic_group_inherited([a.name]) return d return lambda: detail(f5) elif a.sub2 == 'dgroup': return lambda: f5().get_device_group([a.name]) elif a.sub2 == 'tgroup': return lambda: f5().get_traffic_group([a.name]) elif a.sub == 'set': if a.sub2 == 'dgroup': return lambda: f5().set_device_group([a.name], [a.sub3]) elif a.sub2 == 'tgroup': return lambda: f5().set_traffic_group([a.name], [a.sub3]) def __do_tgroup(self, f5): ''' Do stuff concerning TrafficGroups''' a = self._args if not a.name: return lambda: f5().get_list() if a.sub == 'get': if a.sub2 == 'detail': def detail(f5): d = {} d['description'] = f5().get_description([a.name]) d['ha_order'] = f5().get_ha_order([a.name]) d['is_floating'] = f5().get_is_floating([a.name]) d['auto_failback_enabled_state'] = f5().get_auto_failback_enabled_state([a.name]) return d return lambda: detail(f5) elif a.sub2 == 'ha_order': return lambda: f5().get_ha_order([a.name]) elif a.sub == 'create': return lambda: f5().create([a.name]) elif a.sub == 'delete': return lambda: f5().delete_traffic_group([a.name]) elif a.sub == 'add': if a.sub2 == 'ha_order': orders = { 'device': a.sub3, 'order': a.sub4 } return lambda: f5().add_ha_order([a.name], [[orders]]) elif a.sub == 'remove': if a.sub2 == 'ha_order': orders = { 'device': a.sub3, 'order': a.sub4 } return lambda: f5().remove_ha_order([a.name], [[orders]]) elif a.sub2 == 'all_ha_orders': orders = { 'device': a.sub3, 'order': a.sub4 } return lambda: f5().remove_all_ha_orders([a.name]) def _lazy(self): ''' Get the lazy code block to be executed ''' a = self._args def namify(name): # Remove the /partition/ prefix, setting default partition after # login instead name = re.sub(self._folder, '', name) name = re.sub('^/+', '', name) return name if a.name: if isinstance(a.name, list): a.name = map(namify, a.name) else: a.name = namify(a.name) if a.what == 'node': return self.__do_node(lambda: self._f5.LocalLB.NodeAddressV2) elif a.what == 'monitor': return self.__do_monitor(lambda: self._f5.LocalLB.Monitor) elif a.what == 'pool': return self.__do_pool(lambda: self._f5.LocalLB.Pool) elif a.what == 'vserver': return self.__do_vserver(lambda: self._f5.LocalLB.VirtualServer) elif a.what == 'vip': return self.__do_vip(lambda: self._f5.LocalLB.VirtualAddressV2) elif a.what == 'vlan': return self.__do_vlan(lambda: self._f5.Networking.VLAN) elif a.what == 'selfip': return self.__do_selfip(lambda: self._f5.Networking.SelfIPV2) elif a.what == 'tgroup': return self.__do_tgroup(lambda: self._f5.Management.TrafficGroup) elif a.what == 'folder': return self.__do_folder(lambda: self._f5.Management.Folder) def run(self): ''' Do the actual stuff. We are doning some lazy evaluation stuff here. The command line tool does not do anything with the slow F5 API until it is clear what to do and that there is no semantic or syntax error. ''' a = self._args # Inline bulk if a.name and ',' in a.name: a.name = a.name.split(',') lazy = self._lazy() if isfunction(lazy): if a.n: self.verbose(lazy) self.info('no-op', Fore.GREEN) else: self.verbose('Doing some stuf via the API, it may take a while') self.__login() self.out(lazy()) self.info('done', Fore.GREEN) else: self.print_synopsis() return 1 return 0 class FapiInteractive(FapiBase): ''' This is an interactive shell wrapper for Fapi ''' def __init__(self, args, parser): ''' Initialize the interactive Fapi shell''' global __interactive__ __interactive__ = True FapiBase.__init__(self, args) self._parser = parser def run(self): ''' Runs the interactive fapi shell ''' histfile = os.path.join(os.path.expanduser('~'), '.fapihist') try: readline.read_history_file(histfile) except IOError: pass self.print_version() while True: try: arguments = raw_input(__prompt__).split(' ') args = self._parser.parse(arguments) if args: Fapi(args).run() except EOFError: self.info("Good bye\n", Fore.GREEN) break return 0 if __name__ == '__main__': ''' The main function, here we will have Popcorn for free! ''' parser = ArgumentParser() args = parser.parse() if not args: sys.exit(0) fapi = FapiInteractive(args, parser) if args.i else Fapi(args) sys.exit(fapi.run()) # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4