f5-icontrol 0.2.7 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: '079f80f372c7d031cc361e3d1cacc3c04329c243'
4
- data.tar.gz: 8eab333591c73ee9515a4235b80a88b81598ba40
3
+ metadata.gz: 6ab74fd490e1b266c571be1645b623203f869e83
4
+ data.tar.gz: 956cab3ba87c5cbc478d3404c78a9808668ac526
5
5
  SHA512:
6
- metadata.gz: a26d7a1280542e2200747bce5c3c2d9fbfffc92eef05791d5025f6bb8bbb765e3d66303dd3c5198a779a92f76b0a92405b7c1642ff10505ab1372095a0e6d798
7
- data.tar.gz: 938eda0411523fe63108e09f9931d63c11ebb1141cc6f39619bcd1ff6158073d5a8afc3375e431d24af170cf0cd5c7fe8aa0a4799092fd1ced16fa2f0b4b77d8
6
+ metadata.gz: d6c6c35ce03680285ee8a035ba9bbd8b35b0cdb2a8591bb1305357ba16d5ceb0e3d63969d8666b2a6905135f86646f4d1124e8433be049935244ff557e24b913
7
+ data.tar.gz: a3eeaddfeed7c3a38b471e8d5ead2887100f94079a56ee1ba4c0461cf71122202784aac473b2d583a339f3776ee206f01a44f54f7cc1d7e9b30f1006c815f5f0
data/.gitignore CHANGED
@@ -16,3 +16,7 @@ test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
18
  *.swp
19
+ .direnv
20
+ .envrc
21
+
22
+ test.rb
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Build Status](https://travis-ci.org/swalberg/f5-icontrol.svg?branch=master)](https://travis-ci.org/swalberg/f5-icontrol)
4
4
 
5
- This is the F5-control gem. If you have an F5, it can use the iControl SOAP interface to automate things
5
+ This is the F5-icontrol gem. If you have an F5, it can use the iControl API to automate things
6
6
 
7
7
  This is not the official library. That one is [here](https://devcentral.f5.com/d/icontrol-ruby-library). This copy is without warranty. Heck, it probably doesn't even work.
8
8
 
@@ -13,7 +13,7 @@ I originally set out to improve the official one:
13
13
  * Support Ruby 2.0.0 and 2.1.0
14
14
  * Make the interface to the library more Ruby-esque
15
15
 
16
- But given the original one was pretty bare-bones, I started over.
16
+ But given the original one was pretty bare-bones, I started over.
17
17
 
18
18
  ## Installation
19
19
 
@@ -29,7 +29,64 @@ Or install it yourself as:
29
29
 
30
30
  $ gem install f5-icontrol
31
31
 
32
- ## Usage
32
+ ## Usage (REST interface)
33
+
34
+ *NOTE:* The REST interface is still a work in progress! The conventions may change until we release.
35
+
36
+ First configure an instance of the API to point to your F5
37
+
38
+ ```Ruby
39
+ require 'f5/icontrol'
40
+
41
+ f5 = F5::Icontrol::RAPI.new username: 'admin', password: 'admin', host: '10.1.1.1'
42
+ ```
43
+
44
+ After that, the calls should line up directly to the [API Docs](https://devcentral.f5.com/wiki/iControlREST.APIRef.ashx). For collections, use the following methods:
45
+
46
+ | HTTP Verb | Method |
47
+ |-----------|----------------|
48
+ | GET | get_collection |
49
+
50
+ Note that `get_collection` is optional if you call `#each` or some `Enumberable` method. So `foo.get_collection.each` can be shortened to `foo.each`.
51
+
52
+ For resources
53
+
54
+ | HTTP Verb | Method |
55
+ |-----------|----------------|
56
+ | GET | load |
57
+ | PUT | update |
58
+ | DELETE | delete |
59
+ | POST | create |
60
+
61
+
62
+ For example, to get all the pools:
63
+
64
+ ```Ruby
65
+ pools = f5.mgmt.tm.ltm.pool.get_collection
66
+
67
+ puts pools.map(&:name)
68
+
69
+ # shorter method
70
+ puts f5.mgmt.tm.ltm.pool.map(&:name)
71
+ ```
72
+
73
+ It'll also understand subresources:
74
+
75
+ ```Ruby
76
+ members = pools.members.load
77
+ puts members.map(&:address)
78
+
79
+ ```
80
+
81
+ Or, let's create a pool:
82
+
83
+ ```Ruby
84
+ api.mgmt.tm.ltm.pool.create(name: 'seanstestrest')
85
+ ```
86
+
87
+ ## Usage (SOAP interface)
88
+
89
+ *Note* - SOAP will likely be deprecated prior to version 1.0 of this gem. The REST interface is more intuitive and dropping SOAP makes this gem so much lighter.
33
90
 
34
91
  First, configure the gem:
35
92
 
@@ -19,13 +19,16 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_dependency "savon", "~> 2.0"
22
+ spec.add_dependency "rest-client"
22
23
  spec.add_dependency "thor"
24
+ spec.add_dependency "rack", "~> 1.6.4"
25
+ spec.add_dependency "json"
23
26
 
24
27
  spec.add_development_dependency "awesome_print"
25
28
  spec.add_development_dependency "bundler", "~> 1.3"
26
29
  spec.add_development_dependency "byebug"
27
30
  spec.add_development_dependency "rake"
28
31
  spec.add_development_dependency "rspec"
32
+ spec.add_development_dependency "webmock", "~> 2.1.0"
29
33
  spec.add_development_dependency "vcr"
30
- spec.add_development_dependency "webmock"
31
34
  end
@@ -1,4 +1,5 @@
1
1
  require "f5/icontrol/version"
2
+ require "f5/icontrol/rapi"
2
3
  require "f5/icontrol/api"
3
4
  require 'f5/icontrol/common/enabled_state'
4
5
  require 'f5/icontrol/common/enum_item'
@@ -9,6 +10,7 @@ require 'f5/icontrol/locallb/enabled_status'
9
10
  require 'f5/icontrol/locallb/profile_context_type'
10
11
  require 'f5/icontrol/locallb/profile_type'
11
12
  require 'f5/icontrol/locallb/server_ssl_certificate_mode'
13
+ require 'f5/icontrol/rapi/resource'
12
14
  require "openssl"
13
15
  require "savon"
14
16
 
@@ -0,0 +1,72 @@
1
+ require 'rest-client'
2
+ require 'json'
3
+
4
+ module F5
5
+ module Icontrol
6
+ class RAPI
7
+ include Enumerable
8
+
9
+ def initialize(method_chain = nil, **args)
10
+ @method_chain = method_chain || ''
11
+ @args = args
12
+ end
13
+
14
+ def load(resource = nil)
15
+ tail = resource.nil? ? '' : "/#{resource}"
16
+ response = RestClient::Request.execute(method: :get,
17
+ url: "#{url}#{tail}",
18
+ user: @args[:username],
19
+ password: @args[:password],
20
+ verify_ssl: OpenSSL::SSL::VERIFY_NONE
21
+ )
22
+ object = JSON.parse response.body
23
+
24
+ if object.has_key? 'items'
25
+ object['items'].map { |r| Resource.new r, @args }
26
+ else
27
+ Resource.new object, @args
28
+ end
29
+ end
30
+
31
+ def get_collection
32
+ response = RestClient::Request.execute(method: :get,
33
+ url: "#{url}/",
34
+ user: @args[:username],
35
+ password: @args[:password],
36
+ verify_ssl: OpenSSL::SSL::VERIFY_NONE
37
+ )
38
+ objects = JSON.parse response.body
39
+
40
+ objects['items'].map { |r| Resource.new r, @args }
41
+ end
42
+
43
+ def create(options = {})
44
+ response = RestClient::Request.execute(method: :post,
45
+ url: url,
46
+ user: @args[:username],
47
+ password: @args[:password],
48
+ verify_ssl: OpenSSL::SSL::VERIFY_NONE,
49
+ payload: options.to_json,
50
+ headers: { "content-type" => "application/json" }
51
+ )
52
+ JSON.parse response.body
53
+ end
54
+
55
+ def method_missing(method, *args, &block)
56
+ new_method_chain = @method_chain == '/' ? '': "#{@method_chain}/"
57
+ F5::Icontrol::RAPI.new("#{new_method_chain}#{method}", @args)
58
+ end
59
+
60
+ def each(&block)
61
+ get_collection.each &block
62
+ end
63
+
64
+ private
65
+ def url
66
+ method_chain = @method_chain.gsub /_/, '-'
67
+ method_chain.gsub! %r{^/}, ''
68
+ "https://#{@args[:host]}/#{method_chain}"
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,25 @@
1
+ module F5
2
+ module Icontrol
3
+ class RAPI
4
+ class Resource
5
+ def initialize(args, credentials)
6
+ @args = args
7
+ @credentials = credentials
8
+ end
9
+
10
+ def method_missing(method, *args, &block)
11
+ if @args.key? method.to_s
12
+ return @args[method.to_s]
13
+ end
14
+
15
+ potential_collection = "#{method}Reference"
16
+ if @args.key? potential_collection
17
+ link = @args[potential_collection]["link"]
18
+ link.gsub! /^http?s:\/\/localhost\//, ""
19
+ return F5::Icontrol::RAPI.new(link, @credentials)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,5 +1,5 @@
1
1
  module F5
2
2
  module Icontrol
3
- VERSION = '0.2.7'
3
+ VERSION = '0.3.0'
4
4
  end
5
5
  end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe F5::Icontrol::RAPI::Resource do
4
+ subject { described_class.new pool, {} }
5
+ describe "#initialize" do
6
+ end
7
+
8
+ describe "get attribute" do
9
+ it 'returns an attribute that exists' do
10
+ expect(subject.ipTosToServer).to eq 'pass-through'
11
+ end
12
+ end
13
+
14
+ describe "get subcollection" do
15
+ it 'returns an api reference' do
16
+ expect(subject.members).to be_an_instance_of F5::Icontrol::RAPI
17
+ end
18
+ end
19
+
20
+ def pool
21
+ JSON.parse '{"kind":"tm:ltm:pool:poolstate","name":"reallybasic","partition":"Common","fullPath":"/Common/reallybasic","generation":845,"selfLink":"https://localhost/mgmt/tm/ltm/pool/~Common~reallybasic?ver=11.5.1","allowNat":"yes","allowSnat":"yes","ignorePersistedWeight":"disabled","ipTosToClient":"pass-through","ipTosToServer":"pass-through","linkQosToClient":"pass-through","linkQosToServer":"pass-through","loadBalancingMode":"round-robin","minActiveMembers":0,"minUpMembers":0,"minUpMembersAction":"failover","minUpMembersChecking":"disabled","queueDepthLimit":0,"queueOnConnectionLimit":"disabled","queueTimeLimit":0,"reselectTries":0,"slowRampTime":10,"membersReference":{"link":"https://localhost/mgmt/tm/ltm/pool/~Common~reallybasic/members?ver=11.5.1","isSubcollection":true}}'
22
+ end
23
+ end
24
+
@@ -0,0 +1,106 @@
1
+ require 'spec_helper'
2
+
3
+ describe F5::Icontrol::RAPI do
4
+ let(:host) { 'somehost' }
5
+ let(:username) { 'me' }
6
+ let(:password) { 'secret' }
7
+ let(:base64) { Base64.encode64("#{username}:#{password}") }
8
+
9
+ let(:baseurl) { "https://#{host}" }
10
+
11
+ before(:all) { VCR.turn_off! }
12
+
13
+ subject { F5::Icontrol::RAPI.new(username: username, password: password, host: host) }
14
+
15
+ describe "asking for a collection" do
16
+ context "collections" do
17
+ it "calls the url based on the method chain" do
18
+ stub_request(:get, "#{baseurl}/foo/bar/").
19
+ to_return(body: pool_collection)
20
+ subject.foo.bar.get_collection
21
+ expect(WebMock).to have_requested(:get, "#{baseurl}/foo/bar/")
22
+ end
23
+
24
+ it "returns an array of resources" do
25
+ stub_request(:get, "#{baseurl}/foo/bar/").
26
+ to_return(body: pool_collection)
27
+
28
+ bars = subject.foo.bar.get_collection
29
+
30
+ expect(bars).to be_an_instance_of(Array)
31
+ expect(bars.first).to be_an_instance_of(F5::Icontrol::RAPI::Resource)
32
+ end
33
+
34
+ it "converts underscores to dashes in the call" do
35
+ stub_request(:get, "#{baseurl}/foo-bar/").
36
+ to_return(body: pool_collection)
37
+
38
+ subject.foo_bar.get_collection
39
+
40
+ expect(WebMock).to have_requested(:get, "#{baseurl}/foo-bar/")
41
+ end
42
+
43
+ it "understands `each` implicitly calls `get_collection`" do
44
+ stub_request(:get, "#{baseurl}/foo/bar/").
45
+ to_return(body: pool_collection)
46
+
47
+ bars = subject.foo.bar.each
48
+
49
+ expect(bars).to be_an_instance_of(Enumerator)
50
+ end
51
+
52
+ it "handles a block passed to an enumerable method" do
53
+ stub_request(:get, "#{baseurl}/foo/bar/").
54
+ to_return(body: pool_collection)
55
+
56
+ pools = subject.foo.bar.map(&:name)
57
+
58
+ expect(pools).to eq %w{reallybasic reallybasic2}
59
+ end
60
+ end
61
+
62
+ context "subcollections" do
63
+ it "resolves the url based on the method chain and object id" do
64
+ stub_request(:get, "#{baseurl}/mgmt/tm/ltm/pool/").
65
+ to_return(body: pool_collection)
66
+
67
+ pools = subject.mgmt.tm.ltm.pool.get_collection
68
+
69
+ stub_request(:get, "#{baseurl}/mgmt/tm/ltm/pool/~Common~reallybasic/members?ver=11.5.1/").
70
+ to_return(body: pool_collection)
71
+
72
+ pool = pools.first.members.get_collection
73
+
74
+ expect(WebMock).to have_requested(:get, "#{baseurl}/mgmt/tm/ltm/pool/~Common~reallybasic/members?ver=11.5.1/")
75
+ end
76
+
77
+ end
78
+ end
79
+
80
+ describe 'creating a new resource' do
81
+ before do
82
+ stub_request(:post, "#{baseurl}/mgmt/tm/ltm/pool").
83
+ with(body: /"name":"foobar"/, headers: { "Content-Type" => "application/json" }).
84
+ to_return(body: new_pool_response)
85
+ end
86
+
87
+ it 'posts to the api' do
88
+ new_pool = subject.mgmt.tm.ltm.pool.create(name: 'foobar')
89
+ expect(WebMock).to have_requested(:post, "#{baseurl}/mgmt/tm/ltm/pool")
90
+ end
91
+
92
+ it 'returns the hash containing the data' do
93
+ new_pool = subject.mgmt.tm.ltm.pool.create(name: 'foobar')
94
+
95
+ expect(new_pool).to be_an_kind_of Hash
96
+ end
97
+ end
98
+
99
+ def pool_collection
100
+ '{"kind":"tm:ltm:pool:poolcollectionstate","selfLink":"https://localhost/mgmt/tm/ltm/pool?ver=11.5.1","items":[{"kind":"tm:ltm:pool:poolstate","name":"reallybasic","partition":"Common","fullPath":"/Common/reallybasic","generation":845,"selfLink":"https://localhost/mgmt/tm/ltm/pool/~Common~reallybasic?ver=11.5.1","allowNat":"yes","allowSnat":"yes","ignorePersistedWeight":"disabled","ipTosToClient":"pass-through","ipTosToServer":"pass-through","linkQosToClient":"pass-through","linkQosToServer":"pass-through","loadBalancingMode":"round-robin","minActiveMembers":0,"minUpMembers":0,"minUpMembersAction":"failover","minUpMembersChecking":"disabled","queueDepthLimit":0,"queueOnConnectionLimit":"disabled","queueTimeLimit":0,"reselectTries":0,"slowRampTime":10,"membersReference":{"link":"https://localhost/mgmt/tm/ltm/pool/~Common~reallybasic/members?ver=11.5.1","isSubcollection":true}},{"kind":"tm:ltm:pool:poolstate","name":"reallybasic2","partition":"Common","fullPath":"/Common/reallybasic2","generation":819,"selfLink":"https://localhost/mgmt/tm/ltm/pool/~Common~reallybasic2?ver=11.5.1","allowNat":"yes","allowSnat":"yes","ignorePersistedWeight":"disabled","ipTosToClient":"pass-through","ipTosToServer":"pass-through","linkQosToClient":"pass-through","linkQosToServer":"pass-through","loadBalancingMode":"round-robin","minActiveMembers":0,"minUpMembers":0,"minUpMembersAction":"failover","minUpMembersChecking":"disabled","queueDepthLimit":0,"queueOnConnectionLimit":"disabled","queueTimeLimit":0,"reselectTries":0,"slowRampTime":10,"membersReference":{"link":"https://localhost/mgmt/tm/ltm/pool/~Common~reallybasic2/members?ver=11.5.1","isSubcollection":true}}]}'
101
+ end
102
+
103
+ def new_pool_response
104
+ '{"kind":"tm:ltm:pool:poolstate","name":"foobar","fullPath":"foobar","generation":848,"selfLink":"https://localhost/mgmt/tm/ltm/pool/foobar?ver=11.5.1","allowNat":"yes","allowSnat":"yes","ignorePersistedWeight":"disabled","ipTosToClient":"pass-through","ipTosToServer":"pass-through","linkQosToClient":"pass-through","linkQosToServer":"pass-through","loadBalancingMode":"round-robin","minActiveMembers":0,"minUpMembers":0,"minUpMembersAction":"failover","minUpMembersChecking":"disabled","queueDepthLimit":0,"queueOnConnectionLimit":"disabled","queueTimeLimit":0,"reselectTries":0,"slowRampTime":10,"membersReference":{"link":"https://localhost/mgmt/tm/ltm/pool/~Common~foobar/members?ver=11.5.1","isSubcollection":true}}'
105
+ end
106
+ end
@@ -1,4 +1,5 @@
1
1
  require 'f5/icontrol'
2
+ require 'webmock/rspec'
2
3
  require 'vcr'
3
4
 
4
5
  RSpec.configure do |c|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: f5-icontrol
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.7
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Walberg
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-05 00:00:00.000000000 Z
11
+ date: 2017-09-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: savon
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rest-client
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: thor
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +52,34 @@ dependencies:
38
52
  - - ">="
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rack
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.6.4
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.6.4
69
+ - !ruby/object:Gem::Dependency
70
+ name: json
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
41
83
  - !ruby/object:Gem::Dependency
42
84
  name: awesome_print
43
85
  requirement: !ruby/object:Gem::Requirement
@@ -109,21 +151,21 @@ dependencies:
109
151
  - !ruby/object:Gem::Version
110
152
  version: '0'
111
153
  - !ruby/object:Gem::Dependency
112
- name: vcr
154
+ name: webmock
113
155
  requirement: !ruby/object:Gem::Requirement
114
156
  requirements:
115
- - - ">="
157
+ - - "~>"
116
158
  - !ruby/object:Gem::Version
117
- version: '0'
159
+ version: 2.1.0
118
160
  type: :development
119
161
  prerelease: false
120
162
  version_requirements: !ruby/object:Gem::Requirement
121
163
  requirements:
122
- - - ">="
164
+ - - "~>"
123
165
  - !ruby/object:Gem::Version
124
- version: '0'
166
+ version: 2.1.0
125
167
  - !ruby/object:Gem::Dependency
126
- name: webmock
168
+ name: vcr
127
169
  requirement: !ruby/object:Gem::Requirement
128
170
  requirements:
129
171
  - - ">="
@@ -165,6 +207,8 @@ files:
165
207
  - lib/f5/icontrol/locallb/profile_type.rb
166
208
  - lib/f5/icontrol/locallb/server_ssl_certificate_mode.rb
167
209
  - lib/f5/icontrol/locallb/virtual_server/source_address_translation.rb
210
+ - lib/f5/icontrol/rapi.rb
211
+ - lib/f5/icontrol/rapi/resource.rb
168
212
  - lib/f5/icontrol/version.rb
169
213
  - lib/wsdl/ASM.LoggingProfile.wsdl
170
214
  - lib/wsdl/ASM.ObjectParams.wsdl
@@ -443,6 +487,8 @@ files:
443
487
  - spec/cassettes/F5_Icontrol_System_SystemInfo/retrieves_the_version.yml
444
488
  - spec/cli/pool_spec.rb
445
489
  - spec/models/api_spec.rb
490
+ - spec/models/rapi/resource_spec.rb
491
+ - spec/models/rapi_spec.rb
446
492
  - spec/spec_helper.rb
447
493
  homepage: https://github.com/swalberg/f5-icontrol
448
494
  licenses:
@@ -464,7 +510,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
464
510
  version: '0'
465
511
  requirements: []
466
512
  rubyforge_project:
467
- rubygems_version: 2.6.11
513
+ rubygems_version: 2.5.1
468
514
  signing_key:
469
515
  specification_version: 4
470
516
  summary: A gem to manage F5 BigIP devices using the iControl API
@@ -475,4 +521,6 @@ test_files:
475
521
  - spec/cassettes/F5_Icontrol_System_SystemInfo/retrieves_the_version.yml
476
522
  - spec/cli/pool_spec.rb
477
523
  - spec/models/api_spec.rb
524
+ - spec/models/rapi/resource_spec.rb
525
+ - spec/models/rapi_spec.rb
478
526
  - spec/spec_helper.rb