zenoss_client 0.1.0 → 0.5.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 (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'