icontrol 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.markdown +9 -0
- data/README.rdoc +16 -0
- data/lib/icontrol.rb +17 -0
- data/lib/icontrol/attributable.rb +36 -0
- data/lib/icontrol/base.rb +197 -0
- data/lib/icontrol/common.rb +109 -0
- data/lib/icontrol/local_lb/common.rb +47 -0
- data/lib/icontrol/local_lb/monitor.rb +17 -0
- data/lib/icontrol/local_lb/monitor_rule.rb +22 -0
- data/lib/icontrol/local_lb/pool.rb +76 -0
- data/lib/icontrol/local_lb/pool_member.rb +5 -0
- data/lib/icontrol/local_lb/profile_auth.rb +6 -0
- data/lib/icontrol/local_lb/profile_http_class.rb +242 -0
- data/lib/icontrol/local_lb/rate_class.rb +4 -0
- data/lib/icontrol/local_lb/rule.rb +4 -0
- data/lib/icontrol/local_lb/snat_pool.rb +4 -0
- data/lib/icontrol/local_lb/virtual_server.rb +611 -0
- data/lib/icontrol/mappings.rb +62 -0
- data/lib/icontrol/statistic_type.rb +508 -0
- data/spec/fixtures/endpoint_helper.rb +14 -0
- data/spec/fixtures/soap/soap_fixture.rb +14 -0
- data/spec/fixtures/wsdl/wsdl_fixture.rb +15 -0
- data/spec/icontrol_local_lb_pool_spec.rb +119 -0
- data/spec/icontrol_local_lb_profile_http_class_spec.rb +271 -0
- data/spec/icontrol_local_lb_rule_spec.rb +22 -0
- data/spec/icontrol_local_lb_virtual_server_spec.rb +664 -0
- data/spec/icontrol_spec.rb +79 -0
- data/spec/spec_helper.rb +54 -0
- metadata +150 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Jose Fernandez (magec)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# IControl: F5 BigIP SOAP API Client
|
2
|
+
|
3
|
+
IControl allows you to easily connect to a BigIP F5 load balancer and by means of the SOAP API. You can programmatically do almost the same as in the Web Interface.
|
4
|
+
|
5
|
+
## Installing
|
6
|
+
|
7
|
+
# Install the gem:
|
8
|
+
gem install icontrol
|
9
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
= icontrol
|
2
|
+
|
3
|
+
|
4
|
+
== Note on Patches/Pull Requests
|
5
|
+
|
6
|
+
* Fork the project.
|
7
|
+
* Make your feature addition or bug fix.
|
8
|
+
* Add tests for it. This is important so I don't break it in a
|
9
|
+
future version unintentionally.
|
10
|
+
* Commit, do not mess with rakefile, version, or history.
|
11
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
12
|
+
* Send me a pull request. Bonus points for topic branches.
|
13
|
+
|
14
|
+
== Copyright
|
15
|
+
|
16
|
+
Copyright (c) 2010 Jose Fernandez (magec). See LICENSE for details.
|
data/lib/icontrol.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
$LOAD_PATH << File.dirname(__FILE__)
|
2
|
+
require 'icontrol/attributable'
|
3
|
+
require 'icontrol/base'
|
4
|
+
require 'icontrol/mappings'
|
5
|
+
require 'icontrol/common'
|
6
|
+
require 'icontrol/statistic_type'
|
7
|
+
require 'icontrol/local_lb/profile_http_class'
|
8
|
+
require 'icontrol/local_lb/profile_auth'
|
9
|
+
require 'icontrol/local_lb/rate_class'
|
10
|
+
require 'icontrol/local_lb/snat_pool'
|
11
|
+
require 'icontrol/local_lb/common'
|
12
|
+
require 'icontrol/local_lb/pool'
|
13
|
+
require 'icontrol/local_lb/pool_member'
|
14
|
+
require 'icontrol/local_lb/rule'
|
15
|
+
require 'icontrol/local_lb/virtual_server'
|
16
|
+
require 'icontrol/local_lb/monitor'
|
17
|
+
require 'icontrol/local_lb/monitor_rule'
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Attributable
|
2
|
+
|
3
|
+
def self.included(klass)
|
4
|
+
klass.class_eval do
|
5
|
+
extend ClassMethods
|
6
|
+
include InstanceMethods
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def set_id_name(id_name)
|
12
|
+
@id_name = id_name
|
13
|
+
end
|
14
|
+
|
15
|
+
def id_name
|
16
|
+
@id_name
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module InstanceMethods
|
21
|
+
def initialize(attributes)
|
22
|
+
id = attributes.delete(self.class.id_name) if attributes && attributes[self.class.id_name]
|
23
|
+
@attributes = attributes || {}
|
24
|
+
@attributes[:id] ||= id
|
25
|
+
end
|
26
|
+
|
27
|
+
def method_missing(method_name,*args,&block)
|
28
|
+
if @attributes && @attributes.has_key?(method_name)
|
29
|
+
return @attributes[method_name]
|
30
|
+
else
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'savon'
|
3
|
+
require 'net/https'
|
4
|
+
require 'digest/md5'
|
5
|
+
|
6
|
+
# The idea is to create an object proxy to the web service client with the same structure
|
7
|
+
# than the IControl stuff
|
8
|
+
|
9
|
+
module IControl
|
10
|
+
|
11
|
+
class NotConfiguredException < Exception; end
|
12
|
+
|
13
|
+
module Common
|
14
|
+
class IPPortDefinition
|
15
|
+
attr_accessor :address,:port
|
16
|
+
def initialize(options)
|
17
|
+
@address = options[:address]
|
18
|
+
@port = options[:port]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
class Base
|
25
|
+
|
26
|
+
def default_body
|
27
|
+
{ self.class.id_name.to_s + "s" => {:value => [@attributes[:id]] } }
|
28
|
+
end
|
29
|
+
|
30
|
+
# Generic type mapping
|
31
|
+
def self.map_response(response)
|
32
|
+
response_key = response.keys.first
|
33
|
+
if response_key
|
34
|
+
if response[response_key].has_key? :return
|
35
|
+
return IControl::Mappings.map_object(response[response_key][:return])
|
36
|
+
else
|
37
|
+
return nil
|
38
|
+
end
|
39
|
+
else
|
40
|
+
raise "Invalid Response #{response.inspect}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.find(name)
|
45
|
+
return self.find_all if name == :all
|
46
|
+
if name == :first
|
47
|
+
all = self.find_all
|
48
|
+
return all[0] if all.length >0
|
49
|
+
end
|
50
|
+
if self.get_list.include?(name)
|
51
|
+
return self.new(:id => name)
|
52
|
+
else
|
53
|
+
return nil
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.find_all
|
58
|
+
self.get_list.map {|i| self.new(:id => i)}
|
59
|
+
end
|
60
|
+
|
61
|
+
def method_missing(method_name,*args,&block)
|
62
|
+
|
63
|
+
return super if @attributes.has_key? method_name
|
64
|
+
|
65
|
+
if self.class.wsdl.operations.keys.include?("get_#{method_name}".to_sym)
|
66
|
+
return self.class.send("get_#{method_name}".to_sym) do |soap|
|
67
|
+
soap.body = default_body
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
if self.class.wsdl.operations.keys.include?("#{method_name}".to_sym)
|
72
|
+
return self.class.send("#{method_name}".to_sym) do |soap|
|
73
|
+
soap.body = default_body
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
return super
|
78
|
+
end
|
79
|
+
|
80
|
+
include Attributable
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
class << self
|
85
|
+
attr_accessor :config
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.configured?
|
89
|
+
return @config[:user] != "" && @config[:password] != "" && @config[:base_url] != ""
|
90
|
+
end
|
91
|
+
|
92
|
+
@config = {
|
93
|
+
:user => "", # username and password
|
94
|
+
:password => "",
|
95
|
+
:base_url => "", # base url for the web-service (https://example.com/iControl/iControlPortal.cgi)
|
96
|
+
:test_mode => false, # When test mode is set, the soap responses are saved (this is done to ease the testing fixtures generation)
|
97
|
+
:test_path => File.join(File.dirname(__FILE__),"..","..","spec","fixtures"),
|
98
|
+
:test_file_prefix => ""
|
99
|
+
}
|
100
|
+
|
101
|
+
def self.save_test_info(request,response,wsdl,class_name,method_name)
|
102
|
+
|
103
|
+
request_md5 = Digest::MD5.hexdigest(request)
|
104
|
+
request_file_name = request_raw_file_name = File.join(IControl.config[:test_path],"soap","xml","#{class_name}.#{method_name}_#{request_md5}_request")
|
105
|
+
response_file_name = response_raw_file_name = File.join(IControl.config[:test_path],"soap","xml","#{class_name}.#{method_name}_#{request_md5}_response")
|
106
|
+
|
107
|
+
timestamp = Time.now.strftime("%Y%m%d%m%S") + Time.now.usec.to_s
|
108
|
+
|
109
|
+
while File.exists?(response_file_name + ".xml") || File.exists?(request_file_name + ".xml")
|
110
|
+
response_file_name = response_raw_file_name + "." + timestamp
|
111
|
+
request_file_name = request_raw_file_name + "." + timestamp
|
112
|
+
end
|
113
|
+
|
114
|
+
File.open(response_file_name + ".xml","w") { |file| file << response.to_xml }
|
115
|
+
File.open(request_file_name + ".xml","w") { |file| file << request }
|
116
|
+
|
117
|
+
wsdl_file_name = File.join(IControl.config[:test_path],"wsdl","xml","#{class_name}.xml")
|
118
|
+
unless File.exists?(wsdl_file_name)
|
119
|
+
File.open(wsdl_file_name,"w") do |file|
|
120
|
+
file << wsdl
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
|
127
|
+
def IControl.const_missing(name)
|
128
|
+
|
129
|
+
# Whenever we find a new module subclassing IControl we create it
|
130
|
+
|
131
|
+
modulo = Module.new do
|
132
|
+
|
133
|
+
# This new module has the ability to automatically create classes when subclassing from it
|
134
|
+
|
135
|
+
(class << self; self; end).instance_eval do # We do it in the eigenclass
|
136
|
+
|
137
|
+
define_method("const_missing") do |class_name|
|
138
|
+
|
139
|
+
# Whenever we are called with a class name we create a new one based on the configuration we have
|
140
|
+
|
141
|
+
klass = Class.new(IControl::Base) do
|
142
|
+
|
143
|
+
(class << self; self; end).instance_eval do
|
144
|
+
|
145
|
+
define_method("client") do
|
146
|
+
return @client if @client
|
147
|
+
@client = nil
|
148
|
+
if IControl.configured?
|
149
|
+
@client = Savon::Client.new IControl.config[:base_url] + "?WSDL=#{name.to_s}.#{class_name.to_s}"
|
150
|
+
@client.request.basic_auth IControl.config[:user],IControl.config[:password]
|
151
|
+
@client.request.http.ssl_client_auth( :verify_mode => OpenSSL::SSL::VERIFY_NONE )
|
152
|
+
else
|
153
|
+
raise IControl::NotConfiguredException
|
154
|
+
end
|
155
|
+
return @client
|
156
|
+
end
|
157
|
+
|
158
|
+
# This is done this way cause I need access to name and class_name
|
159
|
+
# metaprogramming to cross the scope, don't really like it actually.
|
160
|
+
# TODO: Make use of the delegation patern much more clean I think
|
161
|
+
|
162
|
+
define_method("method_missing") do |method_name,*args,&block|
|
163
|
+
|
164
|
+
raise IControl::NotConfiguredException unless IControl.configured?
|
165
|
+
if client
|
166
|
+
if client.wsdl.operations.keys.include?(method_name)
|
167
|
+
# When calling a soap method we transparently add the ns
|
168
|
+
request = ""
|
169
|
+
response = client.send(method_name,*args) do |soap|
|
170
|
+
soap.namespaces["xmlns:wsdl"] = "urn:iControl:#{name}/#{class_name}"
|
171
|
+
block.call(soap) if block
|
172
|
+
request = soap.to_xml
|
173
|
+
end
|
174
|
+
# In case we save test fixtures
|
175
|
+
IControl.save_test_info(request,response, client.wsdl ,"IControl.#{name}.#{class_name}#{IControl.config[:test_file_prefix]}",method_name) if IControl.config[:test]
|
176
|
+
return self.map_response(response.to_hash)
|
177
|
+
else
|
178
|
+
client.send(method_name,*args,&block)
|
179
|
+
end
|
180
|
+
else
|
181
|
+
super(method_name,*args,&block)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
self.const_set(class_name,klass)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
self.const_set(name,modulo)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# If we are going to redefine any give class we need to force the const_missing for the
|
195
|
+
# initial declaration to be made
|
196
|
+
|
197
|
+
|
@@ -0,0 +1,109 @@
|
|
1
|
+
|
2
|
+
# This is a helper for defining constants
|
3
|
+
module IControl
|
4
|
+
|
5
|
+
class NoSuchPoolException < Exception; end
|
6
|
+
class MethodNotImplementedException < Exception; end
|
7
|
+
|
8
|
+
module ConstDefiner
|
9
|
+
|
10
|
+
def self.included(base)
|
11
|
+
base.extend(ClassMethods)
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
|
16
|
+
def class_name
|
17
|
+
self.name.split("::").last
|
18
|
+
end
|
19
|
+
|
20
|
+
def declare_constants(constants,parent)
|
21
|
+
constants.each do |const_name|
|
22
|
+
klass = Class.new(parent)
|
23
|
+
self.const_set(const_name,klass)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def from_string(klass)
|
28
|
+
return eval("::#{self}::#{klass}")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
module Common
|
35
|
+
|
36
|
+
class VLANFilterList
|
37
|
+
attr_accessor :state,:vlans
|
38
|
+
def initialize(attributes)
|
39
|
+
@state = IControl::Common::EnabledState.from_string(attributes[:state])
|
40
|
+
@vlans = [attributes[:vlans][:item]].flatten.compact
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
class ULong64
|
46
|
+
attr_accessor :high,:low
|
47
|
+
|
48
|
+
def initialize(options)
|
49
|
+
@high = options[:high].to_i
|
50
|
+
@low = options[:low].to_i
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_f
|
54
|
+
retVal = 0.0
|
55
|
+
if @high >=0
|
56
|
+
retVal = @high << 32 & 0xffff0000
|
57
|
+
else
|
58
|
+
retVal = ((@high & 0x7fffffff) << 32) + (0x80000000 << 32)
|
59
|
+
end
|
60
|
+
if @low >=0
|
61
|
+
retVal += @low
|
62
|
+
else
|
63
|
+
retVal += ((@low & 0x7fffffff) + 0x7fffffff)
|
64
|
+
end
|
65
|
+
return retVal;
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_hash
|
69
|
+
return {:high => @high,:low => @low}
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
class SourcePortBehavior
|
75
|
+
include ConstDefiner
|
76
|
+
declare_constants [:SOURCE_PORT_PRESERVE,:SOURCE_PORT_PRESERVE_STRICT,:SOURCE_PORT_CHANGE],SourcePortBehavior
|
77
|
+
end
|
78
|
+
|
79
|
+
class EnabledState
|
80
|
+
include ConstDefiner
|
81
|
+
declare_constants [:STATE_ENABLED,:STATE_DISABLED],EnabledState
|
82
|
+
end
|
83
|
+
|
84
|
+
class TMOSModule
|
85
|
+
include ConstDefiner
|
86
|
+
declare_constants [:TMOS_MODULE_ASM, :TMOS_MODULE_SAM, :TMOS_MODULE_WAM], TMOSModule
|
87
|
+
end
|
88
|
+
|
89
|
+
class ProtocolType
|
90
|
+
|
91
|
+
include ConstDefiner
|
92
|
+
|
93
|
+
valid_consts = [:PROTOCOL_ANY,:PROTOCOL_IPV6,:PROTOCOL_ROUTING,:PROTOCOL_NONE,
|
94
|
+
:PROTOCOL_FRAGMENT,:PROTOCOL_DSTOPTS,:PROTOCOL_TCP,:PROTOCOL_UDP,
|
95
|
+
:PROTOCOL_ICMP,:PROTOCOL_ICMPV6,:PROTOCOL_OSPF,:PROTOCOL_SCTP]
|
96
|
+
|
97
|
+
declare_constants valid_consts,ProtocolType
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
class IPPortDefinition
|
102
|
+
attr_accessor :address,:port
|
103
|
+
def initialize(options)
|
104
|
+
@address = options[:address]
|
105
|
+
@port = options[:port]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module IControl
|
2
|
+
module LocalLB
|
3
|
+
|
4
|
+
class HardwareAccelerationMode
|
5
|
+
include IControl::ConstDefiner
|
6
|
+
declare_constants [:HW_ACCELERATION_MODE_NONE, :HW_ACCELERATION_ASSIST, :HW_ACCELERATION_MODE_FULL ], HardwareAccelerationMode
|
7
|
+
end
|
8
|
+
|
9
|
+
class ClonePoolType
|
10
|
+
include IControl::ConstDefiner
|
11
|
+
declare_constants [ :CLONE_POOL_TYPE_UNDEFINED, :CLONE_POOL_TYPE_CLIENTSIDE, :CLONE_POOL_TYPE_SERVERSIDE ], ClonePoolType
|
12
|
+
end
|
13
|
+
|
14
|
+
class ProfileContextType
|
15
|
+
include IControl::ConstDefiner
|
16
|
+
declare_constants [ :PROFILE_CONTEXT_TYPE_ALL, :PROFILE_CONTEXT_TYPE_CLIENT, :PROFILE_CONTEXT_TYPE_SERVER], ProfileContextType
|
17
|
+
end
|
18
|
+
|
19
|
+
class SnatType
|
20
|
+
include IControl::ConstDefiner
|
21
|
+
declare_constants [ :SNAT_TYPE_NONE, :SNAT_TYPE_TRANSLATION_ADDRESS, :SNAT_TYPE_SNATPOOL, :SNAT_TYPE_AUTOMAP ], SnatType
|
22
|
+
end
|
23
|
+
|
24
|
+
class ProfileType
|
25
|
+
include IControl::ConstDefiner
|
26
|
+
|
27
|
+
declare_constants [ :PROFILE_TYPE_TCP, :PROFILE_TYPE_UDP, :PROFILE_TYPE_FTP, :PROFILE_TYPE_FAST_L4,
|
28
|
+
:PROFILE_TYPE_HTTP, :PROFILE_TYPE_SERVER_SSL, :PROFILE_TYPE_CLIENT_SSL,
|
29
|
+
:PROFILE_TYPE_AUTH, :PROFILE_TYPE_PERSISTENCE, :PROFILE_TYPE_CONNECTION_POOL,
|
30
|
+
:PROFILE_TYPE_STREAM, :PROFILE_TYPE_FAST_HTTP, :PROFILE_TYPE_IIOP,
|
31
|
+
:PROFILE_TYPE_RTSP, :PROFILE_TYPE_STATISTICS, :PROFILE_TYPE_HTTPCLASS, :PROFILE_TYPE_SCTP,
|
32
|
+
:PROFILE_TYPE_INSTANCE, :PROFILE_TYPE_SIPP ], ProfileType
|
33
|
+
end
|
34
|
+
|
35
|
+
class PersistenceMode
|
36
|
+
include IControl::ConstDefiner
|
37
|
+
|
38
|
+
declare_constants [ :PERSISTENCE_MODE_NONE, :PERSISTENCE_MODE_SOURCE_ADDRESS_AFFINITY,
|
39
|
+
:PERSISTENCE_MODE_DESTINATION_ADDRESS_AFFINITY, :PERSISTENCE_MODE_COOKIE,
|
40
|
+
:PERSISTENCE_MODE_MSRDP, :PERSISTENCE_MODE_SSL_SID, :PERSISTENCE_MODE_SIP,
|
41
|
+
:PERSISTENCE_MODE_UIE, :PERSISTENCE_MODE_HASH ], PersistenceMode
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|