cloudmonkey 1.0.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 (2) hide show
  1. data/lib/cm.rb +180 -0
  2. metadata +48 -0
data/lib/cm.rb ADDED
@@ -0,0 +1,180 @@
1
+ require 'json'
2
+ require 'ostruct'
3
+ require 'open3'
4
+
5
+ module CloudMonkey
6
+ class CMClient
7
+ def initialize(params)
8
+ @host = params[:host] || 'localhost'
9
+ @port = params[:port] || 8080
10
+ @apikey = params[:apikey]
11
+ @secretkey = params[:secretkey]
12
+ @config = params[:config] || File.join("#{Dir.home}", ".cloudmonkey-cmrb")
13
+ @debug = params[:debug] || false
14
+ configure
15
+ end
16
+
17
+ # Set a cloudmonkey configuration option.
18
+ def set(key, value)
19
+ run_cmd("set #{key} #{value}", nil)
20
+ end
21
+
22
+ # Configure the cloudmonkey client for use with the library.
23
+ def configure
24
+ set('host', "#{@host}")
25
+ set('port', "#{@port}")
26
+ set('apikey', "#{@apikey}")
27
+ set('secretkey', "#{@secretkey}")
28
+ set('display', 'json')
29
+ set('asyncblock', 'false')
30
+ set('color', 'false')
31
+ end
32
+
33
+ # Execute a command with cloudmonkey. The command can be
34
+ # multi-word, e.g. "list users". Params, if provided, must be a
35
+ # hash (e.g. :account => "admin"), or a string value (.e.g "help"
36
+ # as the command and "list users" as the parameter).
37
+ def execute(cmd, params = nil)
38
+ cmd.downcase!
39
+ run_cmd(cmd, params)
40
+ end
41
+
42
+ # Catch missing methods to transform them into cloudmonkey
43
+ # commands. This allows magic translation of ruby into cloudmonkey
44
+ # commands. Because cloudmonkey api commands have a specific
45
+ # format of 1 or more words followed by key=value parameters, we
46
+ # turn the method_name into "method name" and pass in the first
47
+ # argument only, which is assumed to be a hash or SINGLE string
48
+ # value.
49
+ def method_missing(name, *args)
50
+ cmd = name.to_s.split('_').join(' ').strip
51
+ execute cmd, args[0]
52
+ end
53
+
54
+ private
55
+ # Run a cloudmonkey command.
56
+ def run_cmd(input, params = nil)
57
+ input += ' '
58
+
59
+ # We recognize hash parameters and straight-up string
60
+ # parameters, but nothing else
61
+ unless params.nil?
62
+ if params.is_a?(Hash)
63
+ params.each { |key, value| input += "#{key.downcase}=#{value} " }
64
+ input.chop!
65
+ elsif params.is_a?(String)
66
+ input += params
67
+ else
68
+ raise 'We do not know how to handle these parameters'
69
+ end
70
+ end
71
+
72
+ cmd = "cloudmonkey -c #{@config} #{input}"
73
+ output = nil
74
+
75
+ puts "running #{cmd}" if @debug
76
+ Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
77
+ output = stdout.read
78
+ end
79
+
80
+ # usually the output is json, but not if it is an error or just
81
+ # general messages.
82
+ begin
83
+ json = JSON.parse(output, :symbolize_names => true)
84
+ # is it an async job? these responses usually have the id of
85
+ # whatever you are making and the jobid (2 keys)
86
+ if json.has_key?(:jobid) and json.length <= 2
87
+ handle_async_job(json[:jobid])
88
+ else
89
+ # it is a query result.
90
+ return json
91
+ end
92
+ rescue JSON::ParserError
93
+ handle_non_json_output(output)
94
+ end
95
+ end
96
+
97
+ # Handle an async job that was submitted. This is for anything
98
+ # that is not a query method. The function has already been
99
+ # submitted, but we do need a result.
100
+ def handle_async_job(jobid)
101
+ result = run_cmd('query asyncjobresult', :jobid => jobid)
102
+ # see cs documentation for job status codes - 0 means pending
103
+ while result[:jobstatus] == 0
104
+ sleep(3)
105
+ result = run_cmd('query asyncjobresult', :jobid => jobid)
106
+ end
107
+
108
+ # if there is a job result, we are interested only in
109
+ # that. otherwise just return the whole response because we
110
+ # don't really know what's in it.
111
+ result.has_key?(:jobresult) ? result[:jobresult] : result
112
+ end
113
+
114
+ # Handle non-json output. This happens when Cloudmonkey returns an
115
+ # error or if it's just a generic message. Errors are raised as a
116
+ # runtime exception, while generic message sare put into a hash
117
+ # with a :message property.
118
+ def handle_non_json_output(message)
119
+ # handle various error messages, otherwise simply return a hash
120
+ # with the message
121
+ if message.start_with?('[Errno') or message.start_with?('Unauthorized:')
122
+ raise message
123
+ else
124
+ # if the message is non-0 length after stripping, then it is a
125
+ # valid message. otherwise it is nothing (nil).
126
+ { :message => message } if message.strip.length > 0
127
+ end
128
+ end
129
+ end # end cmclient
130
+
131
+ # For functions that require execution of many commands, or for
132
+ # unwrapping lists that Cloudmonkey returns.
133
+ class CMHelper
134
+ # cm is a CMClient
135
+ def initialize(cm)
136
+ @cm = cm
137
+ end
138
+
139
+ # Return a single network by name.
140
+ def list_physical_network(name)
141
+ networks = @cm.list_physicalnetworks(:name => name)
142
+ unless networks.nil?
143
+ return networks[:physicalnetwork][0] if networks[:count] > 0
144
+ end
145
+ end
146
+
147
+ # Return a single zone by name.
148
+ def list_zone(name)
149
+ zones = @cm.list_zones(:name => name)
150
+ unless zones.nil?
151
+ return zones[:zone][0] if zones[:count] > 0
152
+ end
153
+ end
154
+
155
+ # Enable a custom virtual router-derived network service
156
+ # provider. The provider will provide everything a VirtualRouter
157
+ # provides, but using itself instead of the regular VirtualRouter.
158
+ def enable_custom_vrouter_provider(provider, iface_id, zone_id)
159
+ nsp = @cm.add_networkserviceprovider(:name => provider, :physicalnetworkid => iface_id)
160
+ nsp = nsp[:networkserviceprovider]
161
+
162
+ element = @cm.create_virtualrouterelement(:nspid => nsp[:id])[:virtualrouterelement]
163
+ @cm.configure_virtualrouterelement(:enabled => true, :id => element[:id])
164
+ new_nsp = @cm.update_networkserviceprovider(:state => 'Enabled', :id => nsp[:id])
165
+ return new_nsp[:networkserviceprovider]
166
+ end
167
+
168
+ # Delete a network service provider by name and physical network ID.
169
+ def delete_network_service_provider(name, iface_id)
170
+ nsps = @cm.list_networkserviceproviders(:name => name, :phsyicalnetworkid => iface_id)
171
+ if nsps[:count] > 0
172
+ nsp = nsps[:networkserviceprovider][0]
173
+ @cm.delete_networkserviceprovider(:id => nsp[:id])
174
+ else
175
+ return false
176
+ end
177
+ end
178
+
179
+ end
180
+ end
metadata ADDED
@@ -0,0 +1,48 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cloudmonkey
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - GreenQloud
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-03-27 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: A simple interface to the CloudMonkey console command, allowing it to
15
+ be used in Ruby scripts.
16
+ email:
17
+ - developers@greenqloud.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - lib/cm.rb
23
+ homepage: http://www.greenqloud.com
24
+ licenses:
25
+ - Apache
26
+ post_install_message:
27
+ rdoc_options: []
28
+ require_paths:
29
+ - lib
30
+ required_ruby_version: !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ! '>='
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ required_rubygems_version: !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ! '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ requirements: []
43
+ rubyforge_project:
44
+ rubygems_version: 1.8.23
45
+ signing_key:
46
+ specification_version: 3
47
+ summary: CloudMonkey-Ruby
48
+ test_files: []