icontrol 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|