netdot-restclient 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.
- data/.gitignore +25 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +13 -0
- data/README.md +59 -0
- data/Rakefile +2 -0
- data/lib/netdot/restclient.rb +149 -0
- data/lib/netdot/restclient/version.rb +10 -0
- data/netdot-restclient.gemspec +28 -0
- data/sample/host.rb +36 -0
- data/spec/restclient_spec.rb +94 -0
- metadata +112 -0
data/.gitignore
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
*~
|
24
|
+
cookie.dat
|
25
|
+
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright (c) 2014 Carlos Vicente
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Netdot::RestClient
|
2
|
+
|
3
|
+
RESTful API Ruby client for the Network Documentation Tool
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'netdot-restclient'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install netdot-restclient
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
require 'netdot-restclient'
|
22
|
+
require 'pp'
|
23
|
+
|
24
|
+
netdot = Netdot::RestClient.new(
|
25
|
+
:server => 'http://localhost.localdomain/netdot',
|
26
|
+
:username => 'admin',
|
27
|
+
:password => 'xxxxx',
|
28
|
+
)
|
29
|
+
|
30
|
+
# Get all devices
|
31
|
+
devs = netdot.get('/Device');
|
32
|
+
|
33
|
+
pp devs
|
34
|
+
|
35
|
+
# Get Device id 1
|
36
|
+
dev = netdot.get('/Device/1');
|
37
|
+
|
38
|
+
# Get Device id 1 and foreign objects one level away
|
39
|
+
dev = netdot.get('/Device/1?depth=1');
|
40
|
+
|
41
|
+
# Update Device 1
|
42
|
+
dev = netdot.post('/Device/1', {community=>'public'});
|
43
|
+
|
44
|
+
# Delete Device 1
|
45
|
+
netdot.delete('/Device/1');
|
46
|
+
|
47
|
+
## See Also
|
48
|
+
|
49
|
+
The Netdot user manual at:
|
50
|
+
|
51
|
+
http://netdot.uoregon.edu
|
52
|
+
|
53
|
+
## Contributing
|
54
|
+
|
55
|
+
1. Fork it ( https://github.com/cvicente/netdot-restclient/fork )
|
56
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
57
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
58
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
59
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'netdot/restclient/version'
|
2
|
+
|
3
|
+
require 'httpclient'
|
4
|
+
|
5
|
+
module Netdot
|
6
|
+
class RestClient
|
7
|
+
attr_accessor :format, :base_url, :ua, :xs
|
8
|
+
|
9
|
+
# Constructor and login method
|
10
|
+
#
|
11
|
+
# Arguments (hash):
|
12
|
+
#
|
13
|
+
# server - Netdot server URL
|
14
|
+
# username - Netdot Username
|
15
|
+
# password - Netdot password
|
16
|
+
# retries - Number of attempts
|
17
|
+
# timeout - Timeout in seconds
|
18
|
+
# format - Content format <xml>
|
19
|
+
#
|
20
|
+
# Returns:
|
21
|
+
# Netdot::RestClient object
|
22
|
+
# Example:
|
23
|
+
# Netdot::Restclient.new(args)
|
24
|
+
#
|
25
|
+
def initialize(argv = {})
|
26
|
+
|
27
|
+
[:server, :username, :password].each do |k|
|
28
|
+
raise ArgumentError, "Missing required argument '#{k}'" unless argv[k]
|
29
|
+
end
|
30
|
+
|
31
|
+
argv.each { |k,v| instance_variable_set("@#{k}", v) }
|
32
|
+
|
33
|
+
@timeout ||= 10
|
34
|
+
@retries ||= 3
|
35
|
+
@format ||= 'xml'
|
36
|
+
|
37
|
+
if ( @format == 'xml' )
|
38
|
+
begin
|
39
|
+
require 'xmlsimple'
|
40
|
+
rescue LoadError => e
|
41
|
+
raise LoadError, "Cannot load XML library. Try running 'gem install xml-simple'"
|
42
|
+
end
|
43
|
+
xs = XmlSimple.new({ 'ForceArray' => true, 'KeyAttr' => 'id'})
|
44
|
+
@xs = xs
|
45
|
+
else
|
46
|
+
raise ArgumentError, "Only XML formatting supported at this time"
|
47
|
+
end
|
48
|
+
|
49
|
+
ua = HTTPClient.new(:agent_name => "Netdot::RestClient/#{self.version}")
|
50
|
+
ua.set_cookie_store("cookie.dat")
|
51
|
+
|
52
|
+
login_url = @server + '/NetdotLogin'
|
53
|
+
|
54
|
+
resp = nil
|
55
|
+
|
56
|
+
@retries.times do
|
57
|
+
resp = ua.post login_url, {
|
58
|
+
'destination' => 'index.html',
|
59
|
+
'credential_0' => @username,
|
60
|
+
'credential_1' => @password,
|
61
|
+
'permanent_session' => 1,
|
62
|
+
}
|
63
|
+
if resp
|
64
|
+
ua.save_cookie_store
|
65
|
+
@ua = ua
|
66
|
+
@base_url = @server + '/rest'
|
67
|
+
break
|
68
|
+
else
|
69
|
+
$stderr.puts "Connection attempt to #{@server} failed"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
raise "Could not log into #{@server}" unless resp
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
# Build the Extra headers
|
79
|
+
#
|
80
|
+
def extheader
|
81
|
+
{ 'Accept' => 'text/' + self.format + '; version=1.0' }
|
82
|
+
end
|
83
|
+
|
84
|
+
# Build URL given a resource
|
85
|
+
#
|
86
|
+
def build_url(resource)
|
87
|
+
self.base_url + '/' + resource
|
88
|
+
end
|
89
|
+
|
90
|
+
# Get a resource
|
91
|
+
#
|
92
|
+
# Arguments:
|
93
|
+
# resource - A URI
|
94
|
+
# Returns:
|
95
|
+
# hash when successful
|
96
|
+
# exception when not
|
97
|
+
def get(resource)
|
98
|
+
url = self.build_url(resource)
|
99
|
+
resp = self.ua.get(url, nil, self.extheader)
|
100
|
+
if ( resp.status == 200 )
|
101
|
+
self.xs.xml_in(resp.content)
|
102
|
+
else
|
103
|
+
raise "Could not get #{url}: #{resp.status}"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
# Update or create a resource
|
109
|
+
#
|
110
|
+
# Arguments:
|
111
|
+
# resource - A URI
|
112
|
+
# data - Hash with key/values
|
113
|
+
# Returns:
|
114
|
+
# new or modified record hash when successful
|
115
|
+
# exception when not
|
116
|
+
def post(resource, data)
|
117
|
+
url = self.build_url(resource)
|
118
|
+
raise ArgumentError, "Data must be hash" unless data.is_a?(Hash)
|
119
|
+
resp = self.ua.post(url, data, self.extheader)
|
120
|
+
if ( resp.status == 200 )
|
121
|
+
self.xs.xml_in(resp.content)
|
122
|
+
else
|
123
|
+
raise "Could not post to #{url}: #{resp.status}"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
# Delete a resource
|
129
|
+
#
|
130
|
+
# Arguments:
|
131
|
+
# resource - A URI
|
132
|
+
#
|
133
|
+
# Returns:
|
134
|
+
# true when successful
|
135
|
+
# exception when not
|
136
|
+
#
|
137
|
+
def delete(resource)
|
138
|
+
url = self.build_url(resource)
|
139
|
+
resp = self.ua.delete(url, nil, self.extheader)
|
140
|
+
if ( resp.status == 200 )
|
141
|
+
return true
|
142
|
+
else
|
143
|
+
raise "Could not delete #{url}: #{resp.status}"
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'netdot/restclient/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "netdot-restclient"
|
8
|
+
spec.version = Netdot::RestClient::VERSION
|
9
|
+
spec.authors = ["Carlos Vicente"]
|
10
|
+
spec.email = ["cvicente@gmail.com"]
|
11
|
+
spec.summary = %q{RESTful API client for Netdot}
|
12
|
+
spec.description = %q{Talk to Netdot via REST with Ruby}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "Apache-2.0"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec", "~> 2.6"
|
24
|
+
|
25
|
+
spec.add_dependency "httpclient"
|
26
|
+
spec.add_dependency "xml-simple"
|
27
|
+
|
28
|
+
end
|
data/sample/host.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#
|
2
|
+
# Example: Use the special-purpose 'host' resource
|
3
|
+
# to allocate a new IP and assign DNS records
|
4
|
+
#
|
5
|
+
#
|
6
|
+
require 'netdot/restclient'
|
7
|
+
|
8
|
+
require 'pp'
|
9
|
+
|
10
|
+
netdot = Netdot::RestClient.new(
|
11
|
+
:server => 'http://localhost/netdot',
|
12
|
+
:username => 'admin',
|
13
|
+
:password => 'admin',
|
14
|
+
)
|
15
|
+
# Add
|
16
|
+
# This allocates the first available IP in the given subnet
|
17
|
+
# and creates A and PTR records, assuming that the subnet
|
18
|
+
# is associated with the appropriate forward and reverse zones
|
19
|
+
netdot.post('host', { 'name' => 'testhost1', 'subnet' => '192.168.1.0/24'})
|
20
|
+
|
21
|
+
# Find
|
22
|
+
resp = netdot.get('host?name=testhost1.defaultdomain')
|
23
|
+
|
24
|
+
pp resp
|
25
|
+
|
26
|
+
ipid = resp['Ipblock'].keys[0]
|
27
|
+
|
28
|
+
# Delete DNS records associated with new IP
|
29
|
+
netdot.delete("host?ipid=#{ipid}")
|
30
|
+
|
31
|
+
# Notice that the above does not delete the Ipblock object, but
|
32
|
+
# marks it as "available"
|
33
|
+
# If user has admin privileges and really wants to delete the
|
34
|
+
# Ipblock, then they can do:
|
35
|
+
|
36
|
+
netdot.delete("Ipblock/#{ipid}")
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'netdot/restclient'
|
2
|
+
|
3
|
+
describe Netdot::RestClient do
|
4
|
+
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] = 'admin'
|
14
|
+
@netdot = Netdot::RestClient.new(args)
|
15
|
+
expect(@netdot).to be_an_instance_of(Netdot::RestClient)
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'when getting' do
|
20
|
+
|
21
|
+
it 'invalid resource raises exception' do
|
22
|
+
expect {
|
23
|
+
resp = @netdot.get('foobar')
|
24
|
+
}.to raise_error
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'valid resource as hash' do
|
28
|
+
resp = @netdot.get('Entity/1')
|
29
|
+
resp.should be_an_instance_of(Hash)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'record by id' do
|
33
|
+
resp = @netdot.get('Entity/1')
|
34
|
+
expect(resp['name']).to eq('Unknown')
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'records filtered by name' do
|
38
|
+
resp = @netdot.get('Entity?name=Unknown')
|
39
|
+
expect(resp['Entity']['1']['name']).to eq('Unknown')
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when posting' do
|
46
|
+
|
47
|
+
it 'fails to update invalid record' do
|
48
|
+
expect {
|
49
|
+
resp = @netdot.post('Foobar/1', {'key' => 'value'} )
|
50
|
+
}.to raise_error
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'creates new record' do
|
54
|
+
resp = @netdot.post('Person',
|
55
|
+
{ 'firstname' => 'Joe',
|
56
|
+
'lastname' => 'Plumber',
|
57
|
+
'username' => 'joetubes'
|
58
|
+
})
|
59
|
+
expect(resp['firstname']).to eq ('Joe')
|
60
|
+
expect(resp['lastname']).to eq ('Plumber')
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'fails to create duplicate record' do
|
65
|
+
expect {
|
66
|
+
resp = @netdot.post('Person',
|
67
|
+
{ 'firstname' => 'Joe',
|
68
|
+
'lastname' => 'Plumber',
|
69
|
+
'username' => 'joetubes'
|
70
|
+
})
|
71
|
+
}.to raise_error
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'when deleting' do
|
77
|
+
|
78
|
+
it 'fails to delete invalid record' do
|
79
|
+
expect {
|
80
|
+
resp = @netdot.delete('FooBar/1234')
|
81
|
+
}.to raise_error
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'deletes exiting record' do
|
85
|
+
resp = @netdot.get('Person?lastname=Plumber')
|
86
|
+
person_id = resp['Person'].keys[0]
|
87
|
+
resp = @netdot.delete("Person/#{person_id}")
|
88
|
+
expect(resp).to be_true
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: netdot-restclient
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '1.0'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Carlos Vicente
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-04-09 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: &83415360 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.6'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *83415360
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rake
|
27
|
+
requirement: &83414820 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *83414820
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
requirement: &83414210 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '2.6'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *83414210
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: httpclient
|
49
|
+
requirement: &83413860 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *83413860
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: xml-simple
|
60
|
+
requirement: &83413340 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :runtime
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *83413340
|
69
|
+
description: Talk to Netdot via REST with Ruby
|
70
|
+
email:
|
71
|
+
- cvicente@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- Gemfile
|
78
|
+
- LICENSE.txt
|
79
|
+
- README.md
|
80
|
+
- Rakefile
|
81
|
+
- lib/netdot/restclient.rb
|
82
|
+
- lib/netdot/restclient/version.rb
|
83
|
+
- netdot-restclient.gemspec
|
84
|
+
- sample/host.rb
|
85
|
+
- spec/restclient_spec.rb
|
86
|
+
homepage: ''
|
87
|
+
licenses:
|
88
|
+
- Apache-2.0
|
89
|
+
post_install_message:
|
90
|
+
rdoc_options: []
|
91
|
+
require_paths:
|
92
|
+
- lib
|
93
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ! '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
|
+
none: false
|
101
|
+
requirements:
|
102
|
+
- - ! '>='
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
requirements: []
|
106
|
+
rubyforge_project:
|
107
|
+
rubygems_version: 1.8.11
|
108
|
+
signing_key:
|
109
|
+
specification_version: 3
|
110
|
+
summary: RESTful API client for Netdot
|
111
|
+
test_files:
|
112
|
+
- spec/restclient_spec.rb
|