netdot-restclient 1.3 → 1.4

Sign up to get free protection for your applications and to get access to all the features.
data/lib/netdot.rb ADDED
@@ -0,0 +1,22 @@
1
+ require 'logger'
2
+
3
+ module Netdot
4
+
5
+ class << self
6
+ attr_accessor :logger
7
+ end
8
+
9
+ class NullLogger < Logger
10
+ def initialize(*args)
11
+ end
12
+
13
+ def add(*args, &block)
14
+ end
15
+ end
16
+
17
+ Netdot.logger = NullLogger.new
18
+ end
19
+
20
+ require 'netdot/restclient'
21
+ require 'netdot/host'
22
+ require 'netdot/subnet'
@@ -0,0 +1,76 @@
1
+ module Netdot
2
+ class Host
3
+ attr_accessor :connection
4
+
5
+ def initialize(argv = {})
6
+ [:connection].each do |k|
7
+ raise ArgumentError, "Missing required argument '#{k}'" unless argv[k]
8
+ end
9
+
10
+ argv.each { |k,v| instance_variable_set("@#{k}", v) }
11
+ end
12
+
13
+ ##############################################################
14
+ # Find RR and Ipblock records with flexible arguments
15
+ # Handle exceptions
16
+ def find(param, value)
17
+ begin
18
+ host = @connection.get("/host?#{param.to_s}=#{value}");
19
+ rescue Exception => e
20
+ # Not Found is ok, otherwise re-raise
21
+ raise unless (e.message =~ /404/)
22
+ end
23
+ # Return what we got
24
+ host
25
+ end
26
+
27
+ ##############################################################
28
+ # Find RR and Ipblock records associated with given name
29
+ def find_by_name(name)
30
+ find(:name, name)
31
+ end
32
+
33
+ ##############################################################
34
+ # Find RR and Ipblock records associated with this IP
35
+ def find_by_ip(ip)
36
+ find(:address, ip)
37
+ end
38
+
39
+ ##############################################################
40
+ # Create A record for given name and IP
41
+ # Will also create PTR record if .arpa zone exists
42
+ def create(name, ip)
43
+ Netdot.logger.debug("Creating new DNS records with name:#{name} and ip:#{ip}")
44
+ @connection.post('host', {'name' => name, 'address' => ip})
45
+ end
46
+
47
+ ##############################################################
48
+ # Update A record for given name and IP
49
+ # Will also create PTR record if .arpa zone exists
50
+ def update(name, ip)
51
+ Netdot.logger.debug("Updating DNS records with name:#{name} and ip:#{ip}")
52
+ delete(name)
53
+ #delete_by_ip(ip)
54
+ create(name, ip)
55
+ end
56
+
57
+ ##############################################################
58
+ # Delete A record for given name
59
+ def delete(name)
60
+ host = find_by_name(name)
61
+ return unless host
62
+
63
+ # remove any associated IP addresses
64
+ Netdot.logger.debug("Removing IP records for #{name}")
65
+ host['Ipblock'].keys.each do |id|
66
+ begin
67
+ @connection.delete("host?ipid=#{id}")
68
+ rescue Exception => e
69
+ # Not Found is ok, otherwise re-raise
70
+ raise unless (e.message =~ /404/)
71
+ end
72
+ end
73
+ end
74
+
75
+ end
76
+ end
@@ -1,6 +1,6 @@
1
1
  module Netdot
2
2
  class RestClient
3
- VERSION = "1.3"
3
+ VERSION = "1.4"
4
4
 
5
5
  def version
6
6
  VERSION
@@ -0,0 +1,107 @@
1
+ module Netdot
2
+ class Subnet
3
+ attr_accessor :connection
4
+
5
+ def initialize(argv = {})
6
+ [:connection].each do |k|
7
+ raise ArgumentError, "Missing required argument '#{k}'" unless argv[k]
8
+ end
9
+
10
+ argv.each { |k,v| instance_variable_set("@#{k}", v) }
11
+ end
12
+
13
+ # Get next available subnet in given container
14
+ #
15
+ # Arguments:
16
+ # IP container block (string)
17
+ # CIDR subnet size (optional integer)
18
+ # description (optional string)
19
+ # Returns:
20
+ # New subnet ID when successful
21
+ #
22
+ def allocate(container, prefix=24, description=nil)
23
+
24
+ # Netdot currently only supports /24 prefixes
25
+ raise ArgumentError, "Prefix size #{prefix} is not currently supported (must be 24)" unless prefix==24
26
+
27
+ # Search for container and get its ID
28
+ cont_id = get_ipblock_id(container)
29
+
30
+ # Get container's children blocks
31
+ begin
32
+ resp = @connection.get("Ipblock?parent=#{cont_id}")
33
+ rescue Exception => e
34
+ # Not Found is ok, otherwise re-raise
35
+ raise unless (e.message =~ /404/)
36
+ end
37
+
38
+ # store existing subnets in hash (if any)
39
+ subnets = Hash.new
40
+ if ( resp )
41
+ resp.values.each do |b|
42
+ b.each do |k,v|
43
+ address = v['address']
44
+ subnets[address] = 1
45
+ end
46
+ end
47
+ end
48
+
49
+ # Iterate over all possible subnets
50
+ # This assumes that subnets are /24
51
+ spref = container.split('/')[0]
52
+ spref.gsub!(/(\d+\.\d+)\..*/, '\1')
53
+
54
+ (1..255).each do |n|
55
+ saddr = spref.dup
56
+ saddr = spref + ".#{n}.0"
57
+ if !subnets.empty? && subnets.key?(saddr)
58
+ next # subnet exists
59
+ end
60
+
61
+ # Create subnet
62
+ args = { 'address' => saddr, 'prefix' => prefix.to_s, 'status' => 'Subnet' }
63
+ args['description'] = description unless description.nil?
64
+ resp = @connection.post("Ipblock", args)
65
+ return resp['address'] + '/' + resp['prefix']
66
+ end
67
+
68
+ raise "Could not allocate subnet in #{container}"
69
+ end
70
+
71
+ ######################################################################
72
+ # Delete subnet and all its records
73
+ #
74
+ # Arguments:
75
+ # subnet address (CIDR)
76
+ # Returns:
77
+ # True if successful
78
+ #
79
+ def delete(subnet)
80
+ resp = @connection.get("host?subnet=#{subnet}")
81
+
82
+ if ! resp.empty?
83
+ resp['Ipblock'].keys.each do |id|
84
+ @connection.delete("Ipblock/#{id}")
85
+ end
86
+ end
87
+
88
+ sid = get_ipblock_id(subnet)
89
+ @connection.delete("Ipblock/#{sid}")
90
+ end
91
+
92
+ private
93
+ ######################################################################
94
+ # Get Ipblock ID given its address in CIDR format
95
+ #
96
+ # Arguments:
97
+ # subnet address (CIDR)
98
+ # Returns:
99
+ # ID (number)
100
+ #
101
+ def get_ipblock_id(cidr)
102
+ (address, prefix) = cidr.split('/')
103
+ resp = @connection.get("Ipblock?address=#{address}&prefix=#{prefix}")
104
+ resp['Ipblock'].keys[0]
105
+ end
106
+ end
107
+ end
data/spec/host_spec.rb ADDED
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe Netdot::Host do
4
+
5
+ before :all do
6
+ @netdot = connect
7
+ @host = Netdot::Host.new(:connection => @netdot) if @host.nil?
8
+ subnet = Netdot::Subnet.new(:connection => @netdot)
9
+ subnet_id = subnet.allocate('10.0.0.0/8', 24, 'test')
10
+ @cidr = NetAddr::CIDR.create(subnet_id)
11
+ end
12
+
13
+ context 'when creating a new Host instance' do
14
+ it 'creates a new instance' do
15
+ expect {
16
+ host = Netdot::Host.new(:connection => @netdot)
17
+ }.not_to raise_error
18
+ end
19
+
20
+ it 'raises an exception for invalid arguments' do
21
+ expect {
22
+ host = Netdot::Host.new
23
+ }.to raise_error(ArgumentError)
24
+ end
25
+ end
26
+
27
+ context 'creating a new host' do
28
+ it 'creates a new host allocation' do
29
+ h = @host.create('test0-01-yyy', @cidr.nth(1))
30
+ expect(h.key? 'name').to be_truthy
31
+ expect(h['name']).to match('test0-01-yyy')
32
+ end
33
+
34
+ it 'updates an existing allocation' do
35
+ h = @host.update('test0-01-zzz', @cidr.nth(1))
36
+ expect(h.key? 'name').to be_truthy
37
+ expect(h['name']).to match('test0-01-zzz')
38
+ end
39
+ end
40
+
41
+ context 'finding existing hosts' do
42
+ it 'finds a host by name' do
43
+ h = @host.find_by_name('test0-01-zzz')
44
+ expect(h).to be_instance_of(Hash)
45
+ end
46
+
47
+ it 'finds a host by ip' do
48
+ h = @host.find_by_ip(@cidr.nth(1))
49
+ expect(h).to be_instance_of(Hash)
50
+ end
51
+ end
52
+
53
+ context 'deleting a host' do
54
+ it 'deletes a host' do
55
+ expect {
56
+ @host.delete('test-01-zzz')
57
+ }.not_to raise_error
58
+ end
59
+ end
60
+ end
@@ -1,37 +1,64 @@
1
- require 'netdot/restclient'
1
+ require 'spec_helper'
2
2
 
3
3
  describe Netdot::RestClient do
4
+
4
5
  before :all do
5
- args = {
6
- username: 'admin',
7
- server: 'http://localhost/netdot'
8
- }
9
- expect {
10
- Netdot::RestClient.new(args)
11
- }.to raise_error(ArgumentError)
12
-
13
- args[:password] = 'this-is-not-the-password'
14
- expect {
15
- Netdot::RestClient.new(args)
16
- }.to raise_error
6
+ @netdot = connect
7
+ end
8
+
9
+ context 'when connecting' do
10
+
11
+ it 'raises an exception for incomplete arguments' do
12
+ args = {
13
+ username: 'admin',
14
+ server: 'http://localhost/netdot'
15
+ }
16
+ expect {
17
+ Netdot::RestClient.new(args)
18
+ }.to raise_error(ArgumentError)
19
+ end
17
20
 
18
- args[:password] = 'admin'
21
+ it 'raises an exception for an invalid password' do
22
+ args = {
23
+ server: ENV['SERVER'] || 'http://localhost/netdot',
24
+ username: ENV['USERNAME'] || 'admin',
25
+ password: 'this-is-not-the-password'
26
+ }
27
+ expect {
28
+ Netdot::RestClient.new(args)
29
+ }.to raise_error
30
+ end
19
31
 
20
32
  # The following two assume that local server has SSL
21
33
  # enabled with a self-signed cert
22
- args[:server] = 'https://localhost/netdot'
23
- expect {
24
- Netdot::RestClient.new(args)
25
- }.to raise_error
26
-
27
- args[:ssl_verify] = false
28
- @netdot = Netdot::RestClient.new(args)
29
- expect(@netdot).to be_an_instance_of(Netdot::RestClient)
30
-
31
- args[:server] = 'http://localhost/netdot'
32
- @netdot = Netdot::RestClient.new(args)
33
- expect(@netdot).to be_an_instance_of(Netdot::RestClient)
34
-
34
+ it 'raises an exception if SSL verification fails' do
35
+ args = {
36
+ server: ENV['SERVER'] || 'https://localhost/netdot',
37
+ username: ENV['USERNAME'] || 'admin',
38
+ password: ENV['PASSWORD'] || 'admin'
39
+ }
40
+ expect {
41
+ Netdot::RestClient.new(args)
42
+ }.to raise_error
43
+ end
44
+
45
+ it 'does not raise an exception if SSL verification is disabled' do
46
+ args = {
47
+ server: ENV['SERVER'] || 'https://localhost/netdot',
48
+ username: ENV['USERNAME'] || 'admin',
49
+ password: ENV['PASSWORD'] || 'admin',
50
+ ssl_verify: false
51
+ }
52
+
53
+ netdot = Netdot::RestClient.new(args)
54
+ expect(netdot).to be_an_instance_of(Netdot::RestClient)
55
+ end
56
+
57
+ it 'connects to the API' do
58
+ n = connect
59
+ expect(n).to be_an_instance_of(Netdot::RestClient)
60
+ end
61
+
35
62
  end
36
63
 
37
64
  context 'when getting' do
@@ -44,7 +71,7 @@ describe Netdot::RestClient do
44
71
 
45
72
  it 'valid resource as hash' do
46
73
  resp = @netdot.get('Entity/1')
47
- resp.should be_an_instance_of(Hash)
74
+ expect(resp).to be_an_instance_of(Hash)
48
75
  end
49
76
 
50
77
  it 'record by id' do
@@ -57,18 +84,17 @@ describe Netdot::RestClient do
57
84
  expect(resp['Entity']['1']['name']).to eq('Unknown')
58
85
  end
59
86
 
60
-
61
87
  end
62
88
 
63
- context 'when posting' do
89
+ context 'when posting' do
64
90
 
65
- it 'fails to update invalid record' do
91
+ it 'fails to update invalid record' do
66
92
  expect {
67
93
  resp = @netdot.post('Foobar/1', {'key' => 'value'} )
68
94
  }.to raise_error
69
- end
95
+ end
70
96
 
71
- it 'creates new record' do
97
+ it 'creates new record' do
72
98
  resp = @netdot.post('Person',
73
99
  { 'firstname' => 'Joe',
74
100
  'lastname' => 'Plumber',
@@ -77,9 +103,9 @@ describe Netdot::RestClient do
77
103
  expect(resp['firstname']).to eq ('Joe')
78
104
  expect(resp['lastname']).to eq ('Plumber')
79
105
 
80
- end
106
+ end
81
107
 
82
- it 'fails to create duplicate record' do
108
+ it 'fails to create duplicate record' do
83
109
  expect {
84
110
  resp = @netdot.post('Person',
85
111
  { 'firstname' => 'Joe',
@@ -87,7 +113,7 @@ describe Netdot::RestClient do
87
113
  'username' => 'joetubes'
88
114
  })
89
115
  }.to raise_error
90
- end
116
+ end
91
117
 
92
118
  end
93
119
 
@@ -103,7 +129,7 @@ describe Netdot::RestClient do
103
129
  resp = @netdot.get('Person?lastname=Plumber')
104
130
  person_id = resp['Person'].keys[0]
105
131
  resp = @netdot.delete("Person/#{person_id}")
106
- expect(resp).to be_true
132
+ expect(resp).to be_truthy
107
133
  end
108
134
 
109
135
  end
@@ -0,0 +1,15 @@
1
+ require 'netdot'
2
+ require 'netaddr'
3
+
4
+ @netdot = nil
5
+
6
+ def connect
7
+ args = {
8
+ server: ENV['SERVER'] || 'http://localhost/netdot',
9
+ username: ENV['USERNAME'] || 'admin',
10
+ password: ENV['PASSWORD'] || 'admin',
11
+ ssl_verify: false
12
+ }
13
+ @netdot = Netdot::RestClient.new(args) if @netdot.nil?
14
+ return @netdot
15
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe Netdot::Subnet do
4
+
5
+ before :all do
6
+ @netdot = connect
7
+ @subnet = Netdot::Subnet.new(:connection => @netdot) if @subnet.nil?
8
+ end
9
+
10
+ context 'when creating a new Subnet instance' do
11
+ it 'creates a new instance' do
12
+ expect {
13
+ subnet = Netdot::Subnet.new(:connection => @netdot)
14
+ }.not_to raise_error
15
+ end
16
+
17
+ it 'raises an exception for invalid arguments' do
18
+ expect {
19
+ subnet = Netdot::Subnet.new
20
+ }.to raise_error(ArgumentError)
21
+ end
22
+ end
23
+
24
+ context 'creating a new subnet' do
25
+ it 'allocates a new subnet' do
26
+ subnet_id = @subnet.allocate('10.0.0.0/8', 24, 'test')
27
+ expect(subnet_id).to match('10.0.*.0/24')
28
+ end
29
+
30
+ it 'throws an exception if the prefix is not /24' do
31
+ expect {
32
+ subnet_id = @subnet.allocate('10.0.0.0/8', 26)
33
+ }.to raise_error(ArgumentError)
34
+ end
35
+ end
36
+
37
+ context 'deleting a subnet' do
38
+ let(:subnet_id) do
39
+ @subnet.allocate('10.0.0.0/8', 24)
40
+ end
41
+
42
+ it 'deletes a subnet' do
43
+ expect(@subnet.delete(subnet_id)).to be_truthy
44
+ end
45
+ end
46
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: netdot-restclient
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.3'
4
+ version: '1.4'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-09-02 00:00:00.000000000 Z
12
+ date: 2014-09-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -103,11 +103,17 @@ files:
103
103
  - LICENSE.txt
104
104
  - README.md
105
105
  - Rakefile
106
+ - lib/netdot.rb
107
+ - lib/netdot/host.rb
106
108
  - lib/netdot/restclient.rb
107
109
  - lib/netdot/restclient/version.rb
110
+ - lib/netdot/subnet.rb
108
111
  - netdot-restclient.gemspec
109
112
  - sample/host.rb
113
+ - spec/host_spec.rb
110
114
  - spec/restclient_spec.rb
115
+ - spec/spec_helper.rb
116
+ - spec/subnet_spec.rb
111
117
  homepage: ''
112
118
  licenses:
113
119
  - Apache-2.0
@@ -134,4 +140,7 @@ signing_key:
134
140
  specification_version: 3
135
141
  summary: RESTful API client for Netdot
136
142
  test_files:
143
+ - spec/host_spec.rb
137
144
  - spec/restclient_spec.rb
145
+ - spec/spec_helper.rb
146
+ - spec/subnet_spec.rb