zenoss_client 0.1.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/README.textile +67 -0
  2. data/VERSION +1 -1
  3. data/lib/{events/zevent.rb → ext/ipaddr.rb} +11 -8
  4. data/lib/zenoss.rb +13 -89
  5. data/lib/zenoss/connection.rb +62 -0
  6. data/lib/{model/events/event_class.rb → zenoss/events.rb} +2 -0
  7. data/lib/{model/rrd_view.rb → zenoss/events/event.rb} +25 -19
  8. data/lib/zenoss/events/zevent.rb +25 -0
  9. data/lib/zenoss/exceptions.rb +25 -0
  10. data/lib/zenoss/jsonapi.rb +84 -0
  11. data/lib/zenoss/jsonapi/device_router.rb +93 -0
  12. data/lib/{events/event.rb → zenoss/jsonapi/events_router.rb} +24 -12
  13. data/lib/{model/events/mysql_event_manager.rb → zenoss/jsonapi/report_router.rb} +10 -12
  14. data/lib/{model → zenoss}/model.rb +6 -10
  15. data/lib/{model → zenoss/model}/devices.rb +4 -4
  16. data/lib/{model → zenoss/model}/devices/device.rb +20 -21
  17. data/lib/{model → zenoss/model}/devices/device_class.rb +0 -0
  18. data/lib/{model → zenoss/model}/devices/device_hw.rb +0 -0
  19. data/lib/{model → zenoss/model}/devices/operating_system.rb +0 -0
  20. data/lib/{model → zenoss/model}/event_view.rb +0 -0
  21. data/lib/{model → zenoss/model}/manufacturers/manufacturers.rb +0 -0
  22. data/lib/{model → zenoss/model}/processes/os_process_organizer.rb +0 -0
  23. data/lib/{model → zenoss/model}/rrd/rrd_data_point.rb +10 -7
  24. data/lib/zenoss/model/rrd_view.rb +93 -0
  25. data/lib/{model → zenoss/model}/services.rb +5 -5
  26. data/lib/{model → zenoss/model}/services/ip_service.rb +0 -0
  27. data/lib/{model → zenoss/model}/services/service.rb +0 -0
  28. data/lib/{model → zenoss/model}/services/service_class.rb +0 -0
  29. data/lib/{model → zenoss/model}/services/service_organizer.rb +0 -0
  30. data/lib/{model → zenoss/model}/services/win_service.rb +0 -0
  31. data/lib/{model → zenoss/model}/systems.rb +1 -1
  32. data/lib/{model → zenoss/model}/systems/system.rb +0 -0
  33. data/lib/{model → zenoss/model}/z_device_loader.rb +0 -0
  34. data/lib/{model → zenoss/model}/zenpack/zenpack_manager.rb +0 -0
  35. data/lib/zenoss/restapi.rb +80 -0
  36. data/lib/zenoss_client.rb +3 -0
  37. data/zenoss_client.gemspec +34 -0
  38. metadata +69 -51
  39. data/README.rdoc +0 -58
  40. data/lib/model/events/event_manager_base.rb +0 -66
data/README.textile ADDED
@@ -0,0 +1,67 @@
1
+ h1. zenoss_client: A Ruby library for JSON/REST access to Zenoss
2
+
3
+ This is a work-in-progress to create an easy to use client JSON/REST API for
4
+ Zenoss (http://www.zenoss.com) written in Ruby. I love Zenoss as a product,
5
+ but I am much more efficient in Ruby than Python so I decided to start
6
+ hacking this library together. It is very incomplete and I am just adding
7
+ functionality as I need it or it is requested.
8
+
9
+ Cheers,
10
+
11
+ Dan Wanek
12
+
13
+ h2. CHANGES SINCE THE 0.1.x BRANCH
14
+
15
+ Starting with 0.5.0 I am trying to incorporate as much of the new JSON API
16
+ into this library as possible because the type conversion hackery that was
17
+ present in the REST code is largely unnecessary. Please note that the
18
+ initialization is much different because we are using the sign-in form auth
19
+ instead of the Basic auth of the previous version. This is because the JSON
20
+ calls do not support Basic authentication.
21
+
22
+ There is still much work that needs to be done in order to fully implement
23
+ all of the JSON methods. For now many of the older features fall back to
24
+ REST and some of those will always rely on REST because they are not
25
+ currently supported in the JSON API.
26
+
27
+
28
+ h2. REQUIREMENTS:
29
+ Gems:
30
+
31
+ * tzinfo: For Python DateTime to Ruby DateTime conversion
32
+
33
+
34
+ h2. UNSUPPORTED REST METHODS:
35
+ Some methods within Zope are unsupported due to type conversion issues.
36
+ I have created a work-around but you must add a custom Python script
37
+ to Zope in order to do this. Please see this blog post for information
38
+ on how add the custom script:
39
+
40
+ http://distributed-frostbite.blogspot.com/2010/04/using-ruby-with-zenoss-part-1.html
41
+
42
+ UPDATE: The script itself is now part of the source tree and can be found here: tools/callZenossMethod.py
43
+ It should still be installed in the same fashion as the blog post steps through.
44
+
45
+ == TO USE:
46
+ A gem is now available. 'gem install zenoss_client'
47
+
48
+ require 'zenoss'
49
+ require 'date'
50
+
51
+ server = 'https://zenhost:port/zport/dmd'
52
+ user, pass = 'zuser', 'zpass'
53
+ @zen = Zenoss.connect server, user, pass
54
+ dev = (@zen.find_devices_by_name 'myservername').first
55
+
56
+ # Get RRD data for this device
57
+ rrdpts = dev.get_rrd_data_points
58
+ datapoints = dev.fetch_rrd_value rrdpts.first.name, (DateTime.now - 1)
59
+
60
+ # Get the uptime of the device
61
+ dev.sys_uptime
62
+
63
+ # Get a list of events for this system
64
+ dev.get_events
65
+
66
+
67
+ Have fun and let me know what needs to be fixed / added.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.5.0
@@ -17,13 +17,16 @@
17
17
  # You should have received a copy of the GNU General Public License along
18
18
  # with zenoss_client. If not, see <http://www.gnu.org/licenses/>.
19
19
  #############################################################################
20
+ require 'ipaddr'
21
+
22
+ class IPAddr
23
+
24
+ # This is an extension to the base IPAddr that adds the ability to convert
25
+ # an IP Address from it's network form to a string in the standard dotted
26
+ # decimal format
27
+ def self.inet_ntoa(addr)
28
+ [addr].pack("N").unpack("C*").join "."
29
+ end
30
+ end
20
31
 
21
- module Zenoss
22
- module Event
23
- class ZEvent < Event
24
- include Zenoss
25
- include Zenoss::Event
26
32
 
27
- end # ZEvent
28
- end # Event
29
- end # Zenoss
data/lib/zenoss.rb CHANGED
@@ -21,94 +21,18 @@ require 'rubygems'
21
21
  require 'date'
22
22
  require 'tzinfo'
23
23
  require 'uri'
24
+ require 'httpclient'
25
+ require 'json'
24
26
 
25
- module Zenoss
26
-
27
- # Set the Base URI of the Zenoss server
28
- #
29
- # @param [URI, String] uri is the URL we use to connect to the Zenoss server
30
- # @return [URI] the URI that was parsed and used as our base connection
31
- def Zenoss.uri(uri)
32
- if(uri.kind_of?(URI))
33
- uri.path << '/' unless(uri.path.index /\/$/)
34
- const_set(:BASE_URI, uri)
35
- else
36
- uri << '/' unless(uri.index /\/$/)
37
- const_set(:BASE_URI, URI.parse(uri))
38
- end
39
- end
40
-
41
- # @param [String] user
42
- # @param [String] pass
43
- # @return [Boolean]
44
- def Zenoss.set_auth(user, pass)
45
- const_set(:USER, user)
46
- const_set(:PASS, pass)
47
- true
48
- end
49
-
50
- # @return [Model::DeviceClass] the base DeviceClass /zport/dmd/Devices
51
- def Zenoss.devices
52
- Model::DeviceClass.new('/zport/dmd/Devices')
53
- end
54
-
55
- # @return [Model::ServiceOrganizer] the base ServiceOrganizer /zport/dmd/Services
56
- def Zenoss.services
57
- Model::ServiceOrganizer.new('/zport/dmd/Services')
58
- end
27
+ # An extension to IPAddr for address conversion
28
+ require 'ext/ipaddr'
59
29
 
60
- # @return [Model::System] the base System /zport/dmd/Systems
61
- def Zenoss.systems
62
- Model::System.new('/zport/dmd/Systems')
63
- end
64
-
65
-
66
-
67
- private
68
-
69
- # Prepend the appropriate path and call the REST method on the URL set with Zenoss#uri
70
- #
71
- # @param [String] req_path the request path of the REST method
72
- # @return [String] the response body of the REST call
73
- def rest(req_path)
74
- Net::HTTP.start(Zenoss::BASE_URI.host,Zenoss::BASE_URI.port) {|http|
75
- req = Net::HTTP::Get.new("#{BASE_URI.path}#{req_path}")
76
- puts "Request: #{BASE_URI.path}#{req_path}"
77
- req.basic_auth USER, PASS if USER
78
- response = http.request(req)
79
- response.body.chomp! unless response.body.nil?
80
- return(response.body)
81
- }
82
- end
30
+ module Zenoss
83
31
 
84
- # Call a custom Zope method to work around some issues of unsupported or bad behaving
85
- # REST methods.
86
- # @see http://gist.github.com/343627 for more info.
87
- #
88
- # @param [String] req_path the request path of the REST method ( as if it wasn't misbehaving )
89
- # @example req_path
90
- # getRRDValues?dsnames=['ProcessorTotalUserTime_ProcessorTotalUserTime','MemoryPagesOutputSec_MemoryPagesOutputSec']
91
- # @param [String] callback_func the name of the function to be called on the returned object before giving it back to Ruby
92
- # @param [String] callback_attr the name of the attribute to fetch on the returned object before giving it back to Ruby
93
- # @return [String] the response body of the REST call
94
- def custom_rest(req_path,callback_func = nil, callback_attr=nil)
95
- meth,args = req_path.split('?')
96
- meth = "callZenossMethod?methodName=#{meth}"
97
- unless args.nil?
98
- meth << '&args=['
99
- # Remove the named parameters because we can't dynamically call named parameters in Python.
100
- # This method uses positional parameters via the passed Array (Python List).
101
- args.split('&').inject(nil) do |delim,arg|
102
- arg.gsub!(/'/, "'''") # This may cause problems if the passed argument is already triple quoted.
103
- meth << "#{delim}#{arg.split('=').last}"
104
- delim = '===' if delim.nil?
105
- end
106
- meth << ']'
107
- end
108
- meth << "&filterFunc=#{callback_func}" unless callback_func.nil?
109
- meth << "&filterAttr=#{callback_attr}" unless callback_attr.nil?
110
- puts "METHOD: #{meth}"
111
- rest(meth)
32
+ # initialize a connection to a Zenoss server. This is the same as doing
33
+ # Zenoss::Connection.new(server,user,pass)
34
+ def Zenoss.connect(server, user, pass)
35
+ Connection.new(server,user,pass)
112
36
  end
113
37
 
114
38
  # Some of the REST methods return Strings that are formated like a Python list.
@@ -172,9 +96,7 @@ module Zenoss
172
96
  # @return [Hash,nil] a Ruby Hash
173
97
  def pdict_to_hash(dict)
174
98
  return nil if dict.nil?
175
- puts "Dict: #{dict}"
176
99
  dict = sanitize_str(dict)
177
- puts "New Dict: #{dict}"
178
100
  dict = dict.sub(/^\{(.*)\}$/,'\1').split(/[,:]/).map do |str|
179
101
  str.strip
180
102
  end
@@ -214,5 +136,7 @@ module Zenoss
214
136
 
215
137
  end # Zenoss
216
138
 
217
- require 'model/model'
218
- require 'events/event'
139
+ require 'zenoss/connection'
140
+ require 'zenoss/exceptions'
141
+ require 'zenoss/model'
142
+ require 'zenoss/events'
@@ -0,0 +1,62 @@
1
+ #############################################################################
2
+ # Copyright © 2010 Dan Wanek <dwanek@nd.gov>
3
+ #
4
+ #
5
+ # This file is part of zenoss_client.
6
+ #
7
+ # zenoss_client is free software: you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or (at
10
+ # your option) any later version.
11
+ #
12
+ # zenoss_client is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
+ # Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License along
18
+ # with zenoss_client. If not, see <http://www.gnu.org/licenses/>.
19
+ #############################################################################
20
+ require 'zenoss/jsonapi'
21
+ require 'zenoss/restapi'
22
+
23
+ module Zenoss
24
+
25
+ # This class represents a connection into a Zenoss server.
26
+ class Connection
27
+ include Zenoss
28
+ include Zenoss::JSONAPI
29
+ include Zenoss::JSONAPI::DeviceRouter
30
+ include Zenoss::JSONAPI::EventsRouter
31
+ include Zenoss::JSONAPI::ReportRouter
32
+ include Zenoss::RESTAPI
33
+
34
+ def initialize(url, user, pass)
35
+ @zenoss_uri = (url.is_a?(URI) ? url : URI.parse(url))
36
+ @request_number = 1
37
+ @httpcli = HTTPClient.new
38
+ sign_in(user,pass)
39
+ end
40
+
41
+ private
42
+
43
+ # Sign-in to this Zenoss instance.
44
+ def sign_in(user,pass)
45
+ login_parms = {
46
+ :__ac_name => user,
47
+ :__ac_password => pass,
48
+ :submitted => true,
49
+ :came_from => "#{@zenoss_uri}/zport/dmd",
50
+ }
51
+ login_path = "#{@zenoss_uri}/zport/acl_users/cookieAuthHelper/login"
52
+ resp = @httpcli.post login_path, login_parms
53
+ if(resp.status == 302)
54
+ login_path = resp.header['Location'].first
55
+ resp = @httpcli.post login_path, login_parms
56
+ raise ZenossError, "(HTTP Response #{resp.status}) Could not authenticate to #{@zenoss_uri}" unless resp.status == 200
57
+ end
58
+ true
59
+ end
60
+
61
+ end # Connection
62
+ end # Zenoss
@@ -17,3 +17,5 @@
17
17
  # You should have received a copy of the GNU General Public License along
18
18
  # with zenoss_client. If not, see <http://www.gnu.org/licenses/>.
19
19
  #############################################################################
20
+ require 'zenoss/events/event'
21
+ require 'zenoss/events/zevent'
@@ -17,30 +17,36 @@
17
17
  # You should have received a copy of the GNU General Public License along
18
18
  # with zenoss_client. If not, see <http://www.gnu.org/licenses/>.
19
19
  #############################################################################
20
+
20
21
  module Zenoss
21
- module Model
22
- module RRDView
23
- include Zenoss::Model
22
+ module Events
23
+ class Event < OpenStruct
24
+
25
+ # Initialize this object from a Hash returned via the JSON api
26
+ # @param[Zenoss] zenoss the current instance we are connecting with
27
+ # @param[Hash] zhash a hash of values used to create this Event instance
28
+ def initialize(zenoss,zhash)
29
+ @zenoss = zenoss
30
+ super zhash
31
+ self.firstTime = DateTime.parse(self.firstTime) if self.firstTime
32
+ self.lastTime = DateTime.parse(self.lastTime) if self.lastTime
33
+ end
24
34
 
25
- # @return [Array] of datapoints
26
- def get_rrd_data_points
27
- (plist_to_array( custom_rest('getRRDDataPoints') )).map do |dstr|
28
- dp = dstr.sub(/^<([\w]+)\s+at\s+(.*)>$/,'\2')
29
- RRDDataPoint.new(dp)
35
+ def detail(history = false)
36
+ data = {
37
+ :evid => self.evid,
38
+ :history => history,
39
+ }
40
+ resp = @zenoss.json_request('EventsRouter', 'detail', [data])
41
+ resp['event'].first.each_pair do |k,v|
42
+ self.new_ostruct_member(k)
43
+ @table[k.to_sym] = v
30
44
  end
31
45
  end
32
46
 
33
- # Get key/value pairs of RRD Values for the passed data source names.
34
- #
35
- # @param [Array <String>] dsnames data source names from RRDDataPoint#name
36
- # @return [Hash] key/value pairs of data source name and data source values
37
- def get_rrd_values(dsnames)
38
- pdict_to_hash(custom_rest("getRRDValues?dsnames=[#{dsnames.join(',')}]"))
47
+ def acknowledge
39
48
  end
40
49
 
41
- end # RRDView
42
- end # Model
50
+ end # Event
51
+ end # Events
43
52
  end # Zenoss
44
-
45
- # Load the RRD related files
46
- require 'model/rrd/rrd_data_point'
@@ -0,0 +1,25 @@
1
+ #############################################################################
2
+ # Copyright © 2010 Dan Wanek <dwanek@nd.gov>
3
+ #
4
+ #
5
+ # This file is part of zenoss_client.
6
+ #
7
+ # zenoss_client is free software: you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or (at
10
+ # your option) any later version.
11
+ #
12
+ # zenoss_client is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
+ # Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License along
18
+ # with zenoss_client. If not, see <http://www.gnu.org/licenses/>.
19
+ #############################################################################
20
+
21
+ module Zenoss
22
+ module Events
23
+ class ZEvent < Event; end
24
+ end # Events
25
+ end # Zenoss
@@ -0,0 +1,25 @@
1
+ #############################################################################
2
+ # Copyright © 2010 Dan Wanek <dwanek@nd.gov>
3
+ #
4
+ #
5
+ # This file is part of zenoss_client.
6
+ #
7
+ # zenoss_client is free software: you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or (at
10
+ # your option) any later version.
11
+ #
12
+ # zenoss_client is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
+ # Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License along
18
+ # with zenoss_client. If not, see <http://www.gnu.org/licenses/>.
19
+ #############################################################################
20
+
21
+ module Zenoss
22
+
23
+ class ZenossError < StandardError; end
24
+
25
+ end
@@ -0,0 +1,84 @@
1
+ #############################################################################
2
+ # Copyright © 2010 Dan Wanek <dwanek@nd.gov>
3
+ #
4
+ #
5
+ # This file is part of zenoss_client.
6
+ #
7
+ # zenoss_client is free software: you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or (at
10
+ # your option) any later version.
11
+ #
12
+ # zenoss_client is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
+ # Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License along
18
+ # with zenoss_client. If not, see <http://www.gnu.org/licenses/>.
19
+ #############################################################################
20
+
21
+ module Zenoss
22
+ module JSONAPI
23
+ ROUTERS = {
24
+ 'MessagingRouter' => 'messaging',
25
+ 'EventsRouter' => 'evconsole',
26
+ 'ProcessRouter' => 'process',
27
+ 'ServiceRouter' => 'service',
28
+ 'DeviceRouter' => 'device',
29
+ 'NetworkRouter' => 'network',
30
+ 'TemplateRouter' => 'template',
31
+ 'DetailNavRouter' => 'detailnav',
32
+ 'ReportRouter' => 'report',
33
+ 'MibRouter' => 'mib',
34
+ 'ZenPackRouter' => 'zenpack',
35
+ }
36
+
37
+ def json_request(router, method, data={})
38
+ raise ZenossError, "Router (#{router}) not found" unless ROUTERS.has_key?(router)
39
+
40
+ req_url = "#{@zenoss_uri}/zport/dmd/#{ROUTERS[router]}_router"
41
+ req_headers = {'Content-type' => 'application/json; charset=utf-8'}
42
+ req_body = [{
43
+ :action => router,
44
+ :method => method,
45
+ :data => data,
46
+ :type => 'rpc',
47
+ :tid => @request_number,
48
+ }].to_json
49
+
50
+ @request_number += 1
51
+
52
+ resp = @httpcli.post req_url, req_body, req_headers
53
+ parse_json(resp)
54
+ end
55
+
56
+ private
57
+
58
+ # Check the HTTP and JSON response for errors and return JSON response
59
+ def parse_json(resp)
60
+ begin
61
+ if(resp.status != 200)
62
+ raise ZenossError, "Bad HTTP Response #{resp.status}: Cound not make JSON call"
63
+ end
64
+
65
+ json = JSON.load(resp.body.content)
66
+ # Check for JSON success. There are some exceptions where this doesn't make sense:
67
+ # 1. Sometimes the 'success' key does not exist like in EventsRouter#query
68
+ # 2. When json['result'] is not a Hash like a return from ReportRouter#get_tree
69
+ if(json['result'].is_a?(Hash) && json['result'].has_key?('success') && !json['result']['success'])
70
+ raise ZenossError, "JSON request '#{json['method']}' on '#{json['action']}' was unsuccessful"
71
+ end
72
+
73
+ json['result']
74
+ rescue JSON::ParserError => e
75
+ raise ZenossError, "Invalid JSON response: #{e.message}"
76
+ end
77
+ end
78
+
79
+ end
80
+ end
81
+
82
+ require 'zenoss/jsonapi/device_router'
83
+ require 'zenoss/jsonapi/events_router'
84
+ require 'zenoss/jsonapi/report_router'