These examples show how to use the F5 iControl interface with the Ruby SOAP4R library. Please note that this code requires at least SOAP4R version 1.5.8.
A blog post about this library can be found at F5 DevCentral.
#!/usr/bin/env ruby # vim:expandtab shiftwidth=2 softtabstop=2require 'rubygems' require 'getoptions'$: << File.join(ENV['HOME'], '/ops/lib') << '/cc/ops/lib' require 'f5'PROGNAME = File.basename($0)def usage Kernel.abort "Usage: #{PROGNAME} [--config_file <filename>] <hostname> ..." endopts = GetOptions.new(%w(config_file=s connect_timeout=i))usage if ARGV.empty?hostnames = ARGVoptions = { :config_file => opts.config_file, :connect_timeout => opts.connect_timeout, }hostnames.each do |hostname| lb = F5::LoadBalancer.new(hostname, options) begin puts lb.nodes rescue HTTPClient::ConnectTimeoutError => exc abort "#{PROGNAME}: connect to #{hostname}: #{exc.message}" end endexit 0
Output:
$ node-list 192.168.160.22 192.168.191.32 192.168.191.31 192.168.190.21 192.168.190.22 $
#!/usr/bin/env ruby # vim:expandtab shiftwidth=2 softtabstop=2PROGNAME = File.basename($0)require 'rubygems' require 'getoptions' require 'yaml'$: << File.join(ENV['HOME'], '/ops/lib') << '/cc/ops/lib' require 'f5'def usage Kernel.abort "Usage: #{PROGNAME} [--config_file <filename>] <hostname> [poolname ...]" endopts = GetOptions.new(%w(config_file=s connect_timeout=i))usage if ARGV.empty?hostname, *pools = ARGVoptions = { :config_file => opts.config_file, :connect_timeout => opts.connect_timeout, }lb = F5::LoadBalancer.new(hostname, options)begin pools = pools.empty? ? lb.pools : pools.map {|pool| F5::LoadBalancer::Pool.new(lb, pool)}h = {} pools.each do |pool| members = pool.members unless members.empty? h[pool.name] = members.map {|member| member.refresh { 'address' => member.address, 'port' => member.port, 'state' => { 'session_enabled_state' => member.session_enabled_state, 'session_status' => member.session_status, 'monitor_status' => member.monitor_status, } } } end end rescue HTTPClient::ConnectTimeoutError => exc abort "#{PROGNAME}: connect to #{hostname}: #{exc.message}" endputs h.to_yamlexit 0
Output:
$ members-per-pool 192.168.160.22
---
sipproxy_5060:
- port: 5060
address: 192.168.190.21
- port: 5060
address: 192.168.190.22
redirect_5050:
- port: 5050
address: 192.168.191.31
- port: 5050
address: 192.168.191.32
sipproxy_tcp_pool:
- port: 0
address: 192.168.190.21
- port: 0
address: 192.168.190.22
redirect_tcp_pool:
- port: 0
address: 192.168.191.31
- port: 0
address: 192.168.191.32
eilab-test-keith-pool:
- port: 2380
address: 192.168.214.108
$ members-per-pool 192.168.160.22 redirect_5050
---
redirect_5050:
- port: 5050
address: 192.168.191.31
- port: 5050
address: 192.168.191.32
$
#!/usr/bin/env ruby # vim:expandtab shiftwidth=2 softtabstop=2require 'f5'Member = Struct.new(:address, :port) do def to_hash { 'address' => self.address, 'port' => self.port } end enddef get_pool_member_states(lb, pools) lb.icontrol.locallb.pool_member.get_session_enabled_state(pools.map {|pool| pool.name}) enddef show_pool_members(lb, pools) member_state_lists = get_pool_member_states(lb, pools)puts "Available pool members" puts "======================" pools.each_with_index do |pool, i| puts "pool #{pool.name}" puts '{' member_state_list = member_state_lists[i] member_state_list.each do |member_state| member = member_state["member"] addr = member["address"] port = member["port"]session_state = member_state["session_state"]puts " #{addr}:#{port} (#{session_state})" end puts '}' end enddef toggle_pool_member(lb, pool, member_def) node_ip, node_port = member_def.split(/:/, 2) node_port = 0 if node_port.nil? or node_port.empty? member = Member.new(node_ip, node_port)pool_member_state = get_pool_member_state(lb, pool, member)# Set the state to be toggled to. toggle_state = 'STATE_DISABLED' toggle_state = case pool_member_state when 'STATE_DISABLED'; 'STATE_ENABLED' when 'STATE_ENABLED'; 'STATE_DISABLED' else raise "unable to find member #{member_def} in pool #{pool.name}" endmember_session_state = { 'member' => member.to_hash, 'session_state' => toggle_state, } member_session_state_list = [member_session_state] member_session_state_lists = [member_session_state_list]lb.icontrol.locallb.pool_member.set_session_enabled_state([pool.name], member_session_state_lists) puts "Pool #{pool.name} member {#{node_ip}:#{node_port}} state set from '#{pool_member_state}' to '#{toggle_state}'" enddef get_pool_member_state(lb, pool, member_def) state = nil member_state_lists = get_pool_member_states(lb, [pool]) member_state_list = member_state_lists.first member_state_list.each do |member_state| member = member_state['member'] if member.address == member_def.address and member.port.to_i == member_def.port.to_i state = member_state['session_state'] end end state endKernel.abort "Usage: #{$0} <hostname> [pool [member]]" if ARGV.empty?hostname, pool, member = ARGVlb = F5::LoadBalancer.new(hostname, :config_file => 'config-admin.yaml', :connect_timeout => 10)if pool pool = F5::LoadBalancer::Pool.new(lb, pool) if member toggle_pool_member(lb, pool, member) else show_pool_members(lb, [pool]) end else show_pool_members(lb, lb.pools) endexit
#!/usr/bin/env ruby # vim:expandtab shiftwidth=2 softtabstop=2require 'rubygems' require 'yaml' require 'getoptions'$: << File.join(ENV['HOME'], '/ops/lib') << '/cc/ops/lib' require 'f5'PROGNAME = File.basename($0)def usage(msg=nil) Kernel.warn "#{PROGNAME}: #{msg}" if msg Kernel.abort "Usage: #{PROGNAME} [--config_file <filename>] <hostname> show|enable|disable <node> [...]" endopts = GetOptions.new(%w(config_file=s connect_timeout=i))hostname, cmd, *nodes = ARGVusage if nodes.empty?options = { :config_file => opts.config_file, :connect_timeout => opts.connect_timeout, }ObjectStatus = [:availability_status, :enabled_status, :status_description] CMD_MAP = { 'disable' => lambda {|node| state = 'STATE_DISABLED' node.set_state('STATE_DISABLED') }, 'enable' => lambda {|node| node.set_state('STATE_ENABLED') }, 'show' => lambda {|node| h = { node.address => { 'monitor_status' => node.get_monitor_status, 'session_enabled_state' => node.get_session_enabled_state, 'object_status' => Hash[ *ObjectStatus.map {|what| [what.to_s, node.get_object_status.send(what)] }.flatten ], } } puts h.to_yaml }, }usage("invalid command: #{cmd}") unless CMD_MAP.has_key? cmdlb = F5::LoadBalancer.new(hostname, options)nodes.map {|node| F5::LoadBalancer::Node.new(lb, node)}.each do |node| begin CMD_MAP[cmd].call(node) rescue HTTPClient::ConnectTimeoutError => exc abort "#{PROGNAME}: connect to #{hostname}: #{exc.message}" end endexit 0
username: guest
password: 'pass'
wsdl:
LocalLB:
pool: LocalLB.Pool.wsdl
pool_member: LocalLB.PoolMember.wsdl
virtual_server: LocalLB.VirtualServer.wsdl
node_address: LocalLB.NodeAddress.wsdl
Make sure the `wsdl’ key values point to the required WSDL files.
# vim:expandtab shiftwidth=2 softtabstop=2require 'icontrol'module F5class LoadBalancer attr_reader :hostname, :icontrolclass Node attr_reader :address attr_reader :session_enabled_state, :monitor_statusdef initialize(lb, address) @lb, @address = lb, address clear enddef clear @session_enabled_state = 'UNKNOWN' @monitor_status = 'UNKNOWN' enddef refresh get_session_enabled_state get_monitor_status enddef to_s "#{@address} (#{@session_enabled_state},#{@monitor_status})" enddef set_monitor_state(what) @lb.icontrol.locallb.node_address.set_monitor_state([@address], [what]) enddef get_monitor_status @monitor_status = @lb.icontrol.locallb.node_address.get_monitor_status([@address]).first enddef set_session_enabled_state(what) @lb.icontrol.locallb.node_address.set_session_enabled_state([@address], [what]) enddef get_session_enabled_state @session_enabled_state = @lb.icontrol.locallb.node_address.get_session_enabled_state([@address]).first enddef get_object_status @lb.icontrol.locallb.node_address.get_object_status([@address]).first enddef set_state(what) set_session_enabled_state(what) set_monitor_state(what) end end # class Nodeclass Pool attr_reader :name, :lbdef initialize(lb, name) @lb, @name = lb, name @members = nil enddef to_s "#{@lb} #{@name}" enddef members @members ||= @lb.icontrol.locallb.pool.get_member(@name).first.map do |member| PoolMember.new(self, member.address, member.port) end enddef find_member(a_member) members.find {|member| member.address == a_member.address and member.port == a_member.port} enddef find_node(a_node) members.find {|member| member.address == a_node.address} enddef clear_members members.each do |member| member.clear end end# Fetch/update session_enabled_state for the PoolMembers in this Pool def get_session_enabled_state @lb.icontrol.locallb.pool_member.get_session_enabled_state(@name).first.each do |member_state| if m = find_member(member_state.member) m.session_enabled_state = member_state.session_state end end end# Fetch/update session_status for the PoolMembers in this Pool def get_session_status @lb.icontrol.locallb.pool_member.get_session_status(@name).first.each do |member_status| if m = find_member(member_status.member) m.session_status = member_status.session_status end end enddef get_monitor_instance @lb.icontrol.locallb.pool.get_monitor_instance([@name]).first enddef get_monitor_association @lb.icontrol.locallb.pool.get_monitor_association([@name]).first enddef get_object_status @lb.icontrol.locallb.pool.get_object_status([@name]).first endend # class Poolclass PoolMember attr_accessor :address, :port attr_reader :session_enabled_state, :session_status, :monitor_statusdef initialize(pool, address, port) @pool, @address, @port = pool, address, port clear enddef clear @session_enabled_state = 'UNKNOWN' @session_status = 'UNKNOWN' @monitor_status = 'UNKNOWN' enddef refresh get_session_status get_session_enabled_state get_monitor_status enddef enabled? @session_enabled_state == 'STATE_ENABLED' and @session_status == 'SESSION_STATUS_ENABLED' and @monitor_status == 'MONITOR_STATUS_UP' enddef to_s "#{@address}:#{@port} (#{@session_enabled_state},#{@session_status},#{@monitor_status})" enddef to_hash { 'address' => @address, 'port' => @port, 'state' => { 'session_enabled_state' => @session_enabled_state, 'session_status' => @session_status, 'monitor_status' => @monitor_status, } } enddef get_session_status @session_status = @pool.lb.icontrol.locallb.pool_member.get_session_status([@pool.name]).first.select {|state| state.member.address == @address and state.member.port == @port }.map {|state| state.session_status}.first enddef get_session_enabled_state @session_enabled_state = @pool.lb.icontrol.locallb.pool_member.get_session_enabled_state([@pool.name]).first.select {|state| state.member.address == @address and state.member.port == @port }.map {|state| state.session_state}.first enddef set_session_enabled_state(what) @pool.lb.icontrol.locallb.pool_member.set_session_enabled_state([@pool.name], [[{'member' => to_hash, 'session_state' => what}]]) get_session_enabled_state enddef get_monitor_status @monitor_status = @pool.lb.icontrol.locallb.pool_member.get_monitor_status([@pool.name]).first.select {|state| state.member.address == @address and state.member.port == @port }.map {|state| state.monitor_status}.first enddef set_monitor_state(what) @pool.lb.icontrol.locallb.pool_member.set_monitor_state([@pool.name], [[{'member' => to_hash, 'monitor_state' => what}]]) get_monitor_status enddef set_state(what) set_session_enabled_state(what) set_monitor_state(what) enddef get_monitor_instance @pool.lb.icontrol.locallb.pool_member.get_monitor_instance([@pool.name]).first enddef get_monitor_association @pool.lb.icontrol.locallb.pool_member.get_monitor_association([@pool.name]).first endend # class PoolMemberdef initialize(hostname, opts={}) @hostname = hostname @icontrol = F5::IControl.new(@hostname, opts) enddef to_s "#{@hostname}" enddef nodes @icontrol.locallb.node_address.get_list.map {|node| Node.new(self, node)} enddef pools @icontrol.locallb.pool.get_list.map {|pool| Pool.new(self, pool)} enddef pool(pool) pools.find {|p| p.name == pool.name} enddef pool_by_name(pool_name) pools.find {|p| p.name == pool_name} endclass Managementclass DBVariable attr_reader :namedef initialize(name) @name = name enddef query(lb) lb.icontrol.management.db_variable.query([@name]).first.value enddef modify(lb, value) lb.icontrol.management.db_variable.modify([ { 'name' => @name, 'value' => value } ]) enddef delete(lb) lb.icontrol.management.db_variable.delete_variable([@name]) enddef available?(lb) lb.icontrol.management.db_variable.is_variable_available([@name]).first endend # class DBVariableend # class Managementclass Systemclass Failoverdef get_failover_mode(lb) lb.icontrol.system.failover.get_failover_mode enddef get_failover_state(lb) lb.icontrol.system.failover.get_failover_state enddef get_peer_address(lb) lb.icontrol.system.failover.get_peer_address enddef get_version(lb) lb.icontrol.system.failover.get_version enddef is_redundant?(lb) lb.icontrol.system.failover.is_redundant endend # class Failoverend # class Systemend # class LoadBalancerend # module F5
# vim:expandtab shiftwidth=2 softtabstop=2require 'rubygems' require 'soap/wsdlDriver'module F5DEBUG = falseclass IControl attr_reader :endpoint_url, :basic_auth, :connect_timeoutdef initialize(endpoint, opts={}) config_file = opts[:config_file] || 'config.yaml' method = opts[:method] || 'https' @connect_timeout = opts[:connect_timeout]begin configuration = YAML::load_file(config_file) rescue Exception => exc raise "error loading configuration from '#{config_file}': #{exc.message}" end@wsdl = configuration['wsdl'] username = configuration['username'] password = configuration['password']@endpoint_url = "#{method}://#{endpoint}/iControl/iControlPortal.cgi" @basic_auth = [@endpoint_url, username, password]@modules = {} @wsdl.each do |module_name, interfaces| @modules[module_name] = Module.new(self, module_name, interfaces) class << self; self; end.module_eval do define_method(module_name.downcase) { @modules[module_name] } end end endclass Module attr_reader :basedef initialize(base, name, interfaces) puts "loading module #{name}" if DEBUG @base, @name = base, name @interfaces = Hash.new {|h, interface_name| if interfaces.has_key? interface_name h[interface_name] = Interface.new(self, interface_name, interfaces[interface_name]) class << self; self; end.module_eval do define_method(interface_name) { h[interface_name] } end h[interface_name] else raise "module #{@name}: unknown interface: #{interface_name}" end } enddef method_missing(meth, *args) @interfaces[meth.to_s] # Instantiate RPC driver endclass Interface def initialize(mod, name, wsdl_name) puts "loading interface #{name}" if DEBUG @name = name @driver = SOAP::WSDLDriverFactory.new(wsdl_name).create_rpc_driververify_mode = OpenSSL::SSL::VERIFY_NONE @driver.options['protocol.http.ssl_config.verify_mode'] = verify_mode @driver.options['protocol.http.ssl_config.verify_callback'] = lambda {|is_ok, ctx| true} @driver.options['protocol.http.basic_auth'] << mod.base.basic_auth if mod.base.connect_timeout @driver.options["protocol.http.connect_timeout"] = mod.base.connect_timeout end# Override WSDL service endpoint @driver.endpoint_url = mod.base.endpoint_url enddef method_missing(meth, *args) if @driver.respond_to? meth @driver.send(meth, *args) else raise "interface #{@name}: unknown method: #{meth}" end end end # class Interfaceend # class Moduleend # class IControlend # module F5