mm_json_client 0.1.0

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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +4 -0
  6. data/Guardfile +22 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +151 -0
  9. data/Rakefile +33 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +7 -0
  12. data/examples/claim_next_free_ip.rb +73 -0
  13. data/examples/register_dns.rb +37 -0
  14. data/examples/release_ip.rb +59 -0
  15. data/examples/unregister_dns.rb +44 -0
  16. data/fixtures/vcr_cassettes/MmJsonClient_GenericType/basic_server_interaction/allows_a_user_to_log_in_and_out.yml +74 -0
  17. data/fixtures/vcr_cassettes/MmJsonClient_GenericType/basic_server_interaction/filters_a_list_of_users.yml +110 -0
  18. data/fixtures/vcr_cassettes/MmJsonClient_GenericType/basic_server_interaction/gets_a_list_of_users.yml +111 -0
  19. data/fixtures/vcr_cassettes/MmJsonClient_GenericType/basic_server_interaction/gets_a_user_by_reference.yml +110 -0
  20. data/fixtures/vcr_cassettes/MmJsonClient_GenericType/basic_server_interaction/responds_with_a_server_error_on_bad_auth.yml +39 -0
  21. data/fixtures/vcr_cassettes/MmJsonClient_GenericType/multiple_proxy_servers/connects_to_another_proxy_when_one_is_disabled.yml +72 -0
  22. data/fixtures/vcr_cassettes/MmJsonClient_GenericType/proxy_options/allows_a_separate_proxy_to_be_defined.yml +75 -0
  23. data/fixtures/vcr_cassettes/MmJsonClient_GenericType/ssl_options/allows_ignoring_bad_certificates.yml +74 -0
  24. data/generators/enum_def_generator.rb +22 -0
  25. data/generators/method_def_generator.rb +20 -0
  26. data/generators/type_def_generator.rb +50 -0
  27. data/generators/wasabi_extension.rb +45 -0
  28. data/lib/core_ext/hash_extension.rb +43 -0
  29. data/lib/core_ext/string_extension.rb +60 -0
  30. data/lib/core_ext/symbol_extension.rb +24 -0
  31. data/lib/mm_json_client.rb +64 -0
  32. data/lib/mm_json_client/api_definitions/enums.json +1 -0
  33. data/lib/mm_json_client/api_definitions/methods.json +1 -0
  34. data/lib/mm_json_client/api_definitions/types.json +1 -0
  35. data/lib/mm_json_client/client.rb +148 -0
  36. data/lib/mm_json_client/enums/enum_factory.rb +24 -0
  37. data/lib/mm_json_client/enums/generic_enum.rb +12 -0
  38. data/lib/mm_json_client/exceptions.rb +23 -0
  39. data/lib/mm_json_client/generic_type.rb +37 -0
  40. data/lib/mm_json_client/http_client/client.rb +54 -0
  41. data/lib/mm_json_client/json_rpc_http/client.rb +40 -0
  42. data/lib/mm_json_client/json_rpc_http/error.rb +18 -0
  43. data/lib/mm_json_client/json_rpc_http/exceptions.rb +10 -0
  44. data/lib/mm_json_client/json_rpc_http/response.rb +31 -0
  45. data/lib/mm_json_client/response_code.rb +6 -0
  46. data/lib/mm_json_client/type_factory.rb +83 -0
  47. data/lib/mm_json_client/version.rb +3 -0
  48. data/mm_json_client.gemspec +38 -0
  49. metadata +231 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 946734877ba960aefa97bf9a59279468c50b3afe
4
+ data.tar.gz: 73520ee1beabd428543aaae65a13e4f918aef6e4
5
+ SHA512:
6
+ metadata.gz: 4d8cea7e5fc454914d0536b0b87d2be6628aad3b2332f301a98bdafaba15d4f0e6c7a642c9a5da1c8f94cd0ff6a8fbf0e84ab2085eae10920829b010a3020980
7
+ data.tar.gz: 773b74328b2e3f6f7051942e35a56983390ad5ed5adf9830df163548ac60b48e53db2a9f2233a1fd97e20c61041ed7c956335469b46e6f730388148acbd1a034
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.2
5
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mm_json_client.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,22 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features) \
6
+ # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
+
8
+ ## Note: if you are using the `directories` clause above and you are not
9
+ ## watching the project directory ('.'), then you will want to move
10
+ ## the Guardfile to a watched dir and symlink it back, e.g.
11
+ #
12
+ # $ mkdir config
13
+ # $ mv Guardfile config/
14
+ # $ ln -s config/Guardfile .
15
+ #
16
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
+ guard :rspec, cmd: 'bundle exec rspec' do
18
+ watch(%r{^spec/.+_spec\.rb$})
19
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
20
+ watch(%r{^generators/(.+)\.rb$}) { |m| "spec/generators/#{m[1]}_spec.rb" }
21
+ watch('spec/spec_helper.rb') { 'spec' }
22
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Eric Wannemacher
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,151 @@
1
+ [![Build Status](https://travis-ci.org/ewannema/mm_json_client.svg?branch=master)](https://travis-ci.org/ewannema/mm_json_client)
2
+
3
+ # MmJsonClient
4
+
5
+ The MmJsonClient gem is used to access the Men and Mice IPAM API using the
6
+ JSON-RPC format that was first made available in version 6.6.
7
+
8
+ In order to minimize run time dependencies this gem includes API definition
9
+ files that define the enumerations, methods and types available when using the
10
+ gem. The API definition has been built from the version 7.1.5 WSDL so methods
11
+ available in your installation may differ slightly. Instructions will be coming
12
+ on how to build your own API definition files and use them instead.
13
+
14
+ ## Usage
15
+
16
+ ### Basic Connection Setup
17
+
18
+ ```ruby
19
+ require 'mm_json_client'
20
+ client = MmJsonClient::Client.new(server: 'my-ipam.example.com',
21
+ username: 'demo',
22
+ password: 'demo')
23
+ client.login
24
+ ```
25
+
26
+ Optional parameters to ```Client.new``` are:
27
+
28
+ | Parameter | Values | Purpose |
29
+ | ----------- | ---------- | ------- |
30
+ |endpoint: | string | The URI for the web service. Defaults to /_mmwebext/mmwebext.dll?Soap. |
31
+ |open\_timeout:| integer | Seconds to wait for the initial connection to time out. Defaults to 10.|
32
+ |port: | integer | The TCP port for the connection. Defaults to 80 or 443 if SSL is enabled. |
33
+ |proxy: | string | The M&M API server. Defaults to the same as the server: parameter. |
34
+ |ssl: | true/false | Use SSL for the connection. Defaults to false.|
35
+ |verify\_ssl: | true/false | Whether to validate SSL certificates. Defaults to true.|
36
+
37
+ ### Advanced Connection Setup
38
+
39
+ The client can optionally take an array of servers for the ```proxy:```
40
+ parameter. This allows you to have redundant API servers without a load
41
+ balancer.
42
+
43
+ This also allows you to handle the case where the web server is running
44
+ on the same servers as the Central service in a cluster configuration.
45
+ This is important, because in this architecture the web component
46
+ connects to the local server instead of using the DNS round robin entry
47
+ and if you pick the inactive server then you will receive an error about
48
+ operations being disabled.
49
+
50
+ ```ruby
51
+ # When you have multiple web servers that are not behind a load balancer.
52
+ client = MmJsonClient::Client.new(proxy: ['my-ipam01.example.com',
53
+ 'my-ipam02.example.com'],
54
+ server: 'ipam-cluster-name.example.com',
55
+ username: 'demo',
56
+ password: 'demo')
57
+
58
+ # When you have the web role on the M&M central servers in a cluster and need
59
+ # to make sure you can connect to a functioning one.
60
+ client = MmJsonClient::Client.new(proxy: ['my-ipam01.example.com',
61
+ 'my-ipam02.example.com'],
62
+ server: 'localhost',
63
+ username: 'demo',
64
+ password: 'demo')
65
+ ```
66
+
67
+ ### Case Conversion
68
+
69
+ Methods, parameters, and values returned from the API use snake case instead of
70
+ camel case as listed in the M&M API docs. Class and enumeration names still use
71
+ Pascal case.
72
+
73
+ ### Making Requests
74
+
75
+ The methods in the M&M API will be available directly on the client. Any
76
+ required or optional parameters can be included as a hash. The client will
77
+ return a response object that matches what is in the API docs.
78
+
79
+ ### Retrieving Data
80
+
81
+ ```ruby
82
+ # Get all of the DNS zones
83
+ response = client.get_dns_zones
84
+ puts "Found #{response.total_results} zones on the server."
85
+ puts "The first one is #{response.dns_zones.first.name}."
86
+
87
+ # Search for a particular zone
88
+ response = client.get_dns_zones(filter: 'name:^demo.example.com.$')
89
+ puts "Found #{response.total_results} zones on the server."
90
+ puts "The first one is #{response.dns_zones.first.name}."
91
+
92
+ # Get all sub-zones from a particular zone.
93
+ response = client.get_dns_zones(filter: 'name:example.com.$')
94
+ puts "Found #{response.total_results} zones on the server."
95
+ puts "The first one is #{response.dns_zones.first.name}."
96
+
97
+ # Get a single dns zone by reference.
98
+ response = client.get_dns_zone(dns_zone_ref: '{#4-#1000}')
99
+ zone = response.dns_zone
100
+ puts "The zone name is #{zone.name}."
101
+ ```
102
+
103
+ ### Making Changes
104
+
105
+ When you need to create a type to use as a parameter to a method you will
106
+ instantiate it, modify it as necessary and then pass it in.
107
+
108
+ ```ruby
109
+ new_user = MmJsonClient::User.new
110
+ new_user.name = 'demouser'
111
+ new_user.password = 'password'
112
+ new_user.authentication_type = 'Internal'
113
+
114
+ # or
115
+
116
+ new_user = MmJsonClient::User.new(name: 'demouser',
117
+ password: 'password',
118
+ authentication_type: 'Internal')
119
+
120
+ # and then
121
+ client.add_user(user: new_user)
122
+ ```
123
+
124
+ ### Enumerated Values
125
+
126
+ Some fields in the API such as AuthenticationType are enumerated values. To
127
+ see valid values for the enumation use the ```.values``` method.
128
+
129
+ ```ruby
130
+ MmJsonClient::Enums::AuthenticationType.values
131
+ => ["Internal", "AD", "ADGroup", "RADIUS"]
132
+ ```
133
+
134
+ ### Learning and Exploration
135
+
136
+ There are some [examples](examples/) in this repository.
137
+
138
+ Check out the SOAP API docs on your server. Although this gem is using the
139
+ JSON-RPC API, the methods, types, enumerations and responses are the same.
140
+
141
+ If you have completion enabled in your IRB session you will be able to complete
142
+ on method, type, and enumeration names.
143
+
144
+ ## Contributing
145
+
146
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ewannema/mm_json_client.
147
+
148
+ ## License
149
+
150
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
151
+
data/Rakefile ADDED
@@ -0,0 +1,33 @@
1
+ require 'bundler/gem_tasks'
2
+ require_relative 'generators/enum_def_generator'
3
+ require_relative 'generators/method_def_generator'
4
+ require_relative 'generators/type_def_generator'
5
+ require 'rspec/core/rake_task'
6
+ require 'wasabi'
7
+
8
+ RSpec::Core::RakeTask.new(:spec)
9
+
10
+ task default: :spec
11
+
12
+ namespace :generate do
13
+ desc 'Generate a type definition file from a WSDL.'
14
+ task :type_def do
15
+ ARGV.each { |a| task a.to_sym {} }
16
+ generator = MmJsonClient::Generators::TypeDefGenerator.new
17
+ generator.generate(ARGV[1], ARGV[2])
18
+ end
19
+
20
+ desc 'Generate an enum definition file from a WSDL.'
21
+ task :enum_def do
22
+ ARGV.each { |a| task a.to_sym {} }
23
+ generator = MmJsonClient::Generators::EnumDefGenerator.new
24
+ generator.generate(ARGV[1], ARGV[2])
25
+ end
26
+
27
+ desc 'Generate an method definition file from a WSDL.'
28
+ task :method_def do
29
+ ARGV.each { |a| task a.to_sym {} }
30
+ generator = MmJsonClient::Generators::MethodDefGenerator.new
31
+ generator.generate(ARGV[1], ARGV[2])
32
+ end
33
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'mm_json_client'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'mm_json_client'
4
+
5
+ #######################################
6
+ # Configuration values from somewhere #
7
+ #######################################
8
+ server = 'my-ipam.example.com'
9
+ username = 'admin'
10
+ password = 'password'
11
+
12
+ # Options for finding the IP.
13
+ ping_verify = false # try to ping the IP address
14
+ claim_time = 30 # seconds to temporarily claim the record to avoid
15
+ # race conditions
16
+ exclude_dhcp = true # exclude DHCP ranges from available IPs
17
+
18
+ ip_range = '10.20.30.0/24' # The range to pull the IP from
19
+
20
+ #######################################
21
+
22
+ client = MmJsonClient::Client.new(server: server,
23
+ username: username,
24
+ password: password)
25
+ client.login
26
+
27
+ #######################
28
+ # Get the IP range
29
+ #######################
30
+ response = client.get_ranges(filter: "name:^#{ip_range}$")
31
+ raise "IP Range #{ip_range} not found" if response.total_results == 0
32
+ range = response.ranges.first
33
+
34
+ ###############################################
35
+ # Get the first available address in the range
36
+ ###############################################
37
+ response = client.get_next_free_address(range_ref: range.ref,
38
+ ping: ping_verify,
39
+ exclude_dhcp: exclude_dhcp,
40
+ temporary_claim_time: claim_time)
41
+ ip_address = response.address
42
+
43
+ ###############################################
44
+ # Get the corresponding IPAM record for the IP
45
+ ###############################################
46
+ response = client.get_ipam_records(range_ref: range.ref,
47
+ limit: 1,
48
+ filter: "address:^#{ip_address}$")
49
+ raise "No IPAM record for #{ip_address}" if response.total_results == 0
50
+ ipam_record = response.ipam_records.first
51
+
52
+ ###############################################
53
+ # Get a deep copy of the IPAM record to update
54
+ # and mark it IPAM record as claimed.
55
+ ###############################################
56
+ updated_ipam_record = ipam_record.deep_copy
57
+ updated_ipam_record.state = 'Claimed'
58
+
59
+ response = client.set_ipam_record(ipam_record_before: ipam_record,
60
+ ipam_record_after: updated_ipam_record)
61
+ unless response.errors.empty?
62
+ raise "Unable to claim the IPAM record. Errors #{response.errors.inspect}"
63
+ end
64
+
65
+ ###################################################
66
+ # Verify that the IPAM record is marked as claimed
67
+ ###################################################
68
+ response = client.get_ipam_record(addr_ref: ipam_record.addr_ref)
69
+ if response.ipam_record.state == 'Claimed'
70
+ puts "#{ip_address} was successfully claimed."
71
+ else
72
+ raise 'Failed to claim an IP address.'
73
+ end
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'mm_json_client'
4
+
5
+ #######################################
6
+ # Configuration values from somewhere #
7
+ #######################################
8
+ server = 'my-ipam.example.com'
9
+ username = 'admin'
10
+ password = 'password'
11
+
12
+ ip_address = '10.20.30.2'
13
+ dns_zone_name = 'example.com.'
14
+ dns_name = 'gemdemo'
15
+
16
+ #######################################
17
+
18
+ client = MmJsonClient::Client.new(server: server,
19
+ username: username,
20
+ password: password)
21
+ client.login
22
+
23
+ #######################
24
+ # Get the DNS Zone
25
+ #######################
26
+ response = client.get_dns_zones(filter: "name:^#{dns_zone_name}$")
27
+ raise "DNS Zone #{dns_zone_name} not found" if response.total_results == 0
28
+ dns_zone = response.dns_zones.first
29
+
30
+ dns_record = MmJsonClient::DNSRecord.new(name: dns_name, type: 'A', ttl: nil,
31
+ data: ip_address, enabled: true,
32
+ dns_zone_ref: dns_zone.ref)
33
+
34
+ response = client.add_dns_record(dns_record: dns_record)
35
+ raise 'Unable to add the DNS record.' unless response.ref
36
+
37
+ puts "Registered #{dns_name}.#{dns_zone_name} with IP #{ip_address}"
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'mm_json_client'
4
+
5
+ #######################################
6
+ # Configuration values from somewhere #
7
+ #######################################
8
+ server = 'my-ipam.example.com'
9
+ username = 'admin'
10
+ password = 'password'
11
+
12
+ ip_range = '10.20.30.0/24' # The range the IP is in
13
+ ip_address = '10.20.30.2'
14
+
15
+ #######################################
16
+
17
+ client = MmJsonClient::Client.new(server: server,
18
+ username: username,
19
+ password: password)
20
+ client.login
21
+
22
+ #######################
23
+ # Get the IP range
24
+ #######################
25
+ response = client.get_ranges(filter: "name:^#{ip_range}$")
26
+ raise "IP Range #{ip_range} not found" if response.total_results == 0
27
+ range = response.ranges.first
28
+
29
+ ###############################################
30
+ # Get the corresponding IPAM record for the IP
31
+ ###############################################
32
+ response = client.get_ipam_records(range_ref: range.ref,
33
+ limit: 1,
34
+ filter: "address:^#{ip_address}$")
35
+ raise "No IPAM record for #{ip_address}" if response.total_results == 0
36
+ ipam_record = response.ipam_records.first
37
+
38
+ ###############################################
39
+ # Get a deep copy of the IPAM record to update
40
+ # and mark it IPAM record as free
41
+ ###############################################
42
+ updated_ipam_record = ipam_record.deep_copy
43
+ updated_ipam_record.state = 'Free'
44
+
45
+ response = client.set_ipam_record(ipam_record_before: ipam_record,
46
+ ipam_record_after: updated_ipam_record)
47
+ unless response.errors.empty?
48
+ raise "Unable to release the IPAM record. Errors #{response.errors.inspect}"
49
+ end
50
+
51
+ ###################################################
52
+ # Verify that the IPAM record is marked as free
53
+ ###################################################
54
+ response = client.get_ipam_record(addr_ref: ipam_record.addr_ref)
55
+ if response.ipam_record.state == 'Free'
56
+ puts "#{ip_address} was successfully released."
57
+ else
58
+ raise 'Failed to release the IP address.'
59
+ end