stacker_bee 1.0.0 → 1.0.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3dec8ed7bf3c0530da68cb9ba61803e1279f3ff3
4
- data.tar.gz: 5532fc2e8188567eb1e7d9590d6249ee0ab60493
3
+ metadata.gz: 113d9dc3b7034e99f4fa25d03b558de6f6a71c67
4
+ data.tar.gz: 3ec61352a7c75f6cdf92fbe7bd628fd2d3890d15
5
5
  SHA512:
6
- metadata.gz: 709fe57d52d12d7a153e5415fb3b947131f4bf0169c19dda3a8440aa58d1601b5ca081449c138be6430e339c19f6e836ddc7fd5efd7c3f5fafa1b49d032287b1
7
- data.tar.gz: 7f827708d6c38a505a577cc4c5ae921fc67fe1ddd726e0741cd378bf3da33f25472257a2505921e65ac0c7eb7140295c15b966490a42553fa939fe5c7ccda579
6
+ metadata.gz: 3583805c59178743eba73e67477b1d7a47e47dbd57e76248e2ab90ba398ebf2c452601c7c7b431039b22d612414d4c7fa874fec74ecea333ca0feceae53e21f0
7
+ data.tar.gz: e5bd192f9e42c7d9a6facca1fed0ab07339a089e329e41aaf6257516ede551e50f51e79963ede771ba183f8a8951a4b0bfc0919391c9ba5af9a5530905869739
data/.gitignore CHANGED
@@ -2,6 +2,7 @@
2
2
  *.rbc
3
3
  config.yml
4
4
  .rvmrc
5
+ .ruby-version
5
6
  .bundle
6
7
  .config
7
8
  .yardoc
data/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  [![Code Climate](https://codeclimate.com/github/promptworks/stacker_bee.png)](https://codeclimate.com/github/promptworks/stacker_bee)
4
4
  [![Dependency Status](https://gemnasium.com/promptworks/stacker_bee.png)](https://gemnasium.com/promptworks/stacker_bee)
5
5
  [![Build Status](https://travis-ci.org/promptworks/stacker_bee.png?branch=master)](https://travis-ci.org/promptworks/stacker_bee)
6
+ [![Gem Version](https://badge.fury.io/rb/stacker_bee.png)](http://badge.fury.io/rb/stacker_bee)
6
7
 
7
8
  The unofficial CloudStack client for Ruby.
8
9
 
@@ -20,71 +21,66 @@ And execute:
20
21
 
21
22
  $ bundle install
22
23
 
24
+
23
25
  ## Basic Usage
24
26
 
25
- my_client = StackerBee::Client.new({
27
+ cloud_stack = StackerBee::Client.new(
26
28
  url: 'http://localhost:8080/client/api',
27
29
  api_key: 'MY_API_KEY',
28
30
  secret_key: 'MY_SECRET_KEY'
29
- })
30
-
31
- my_client.list_virtual_machines
32
-
33
- my_client.create_volume name: "MyVolume"
34
-
35
- ## Basic Configuration
31
+ )
36
32
 
37
- All configuration parameters set on the Client class are used as defaults for Client instances.
33
+ cloud_stack.list_virtual_machines state: 'Running'
34
+ # => [ { id: '48b91ab4...', displayName: '...', ... },
35
+ # { id: '59c02bc5...', displayName: '...', ... },
36
+ # ... ]
38
37
 
39
- ### URL
38
+ cloud_stack.create_volume name: 'MyVolume'
40
39
 
41
- Your CloudStack URL. E.g. http://localhost:8080/client/api
40
+ ## Features
42
41
 
43
- StackerBee::Client.url = "http://localhost:8080/client/api"
42
+ ### Idomatic Ruby formatting for names
44
43
 
45
- Or:
44
+ You can use `list_virtual_machines` instead of `listVirtualMachines` and
45
+ `affinity_group_id` instead of `affinitygroupid` (if you want to).
46
46
 
47
- my_client = StackerBee::Client.new({
48
- url: 'http://localhost:8080/client/api'
49
- })
47
+ For example:
50
48
 
51
- ### Keys
49
+ vm = cloud_stack.list_virtual_machines(affinity_group_id: id).first
50
+ puts vm[:iso_display_text]
52
51
 
53
- Your CloudStack credentials, i.e. API key and secret key.
52
+ ### Handling 'map' parameters
54
53
 
55
- StackerBee::Client.api_key = "MY_API_KEY"
56
- StackerBee::Client.secret_key = "MY_SECRET_KEY"
54
+ For any endpoint requiring a map parameter, simply pass in a hash.
57
55
 
58
- Or:
56
+ create_tags(tags: { type: 'community'}, resource_type: "Template", resource_ids: id )
59
57
 
60
- my_client = StackerBee::Client.new({
61
- api_key: 'MY_API_KEY',
62
- secret_key: 'MY_SECRET_KEY'
63
- })
58
+ This will yield a request with the following query string:
64
59
 
65
- ### Logger
60
+ ...&tags[0].key=type&tags[0].name=type&tags[0].value=community
66
61
 
67
- Your logger of choice.
62
+ ### Configurable Logger
68
63
 
69
64
  StackerBee::Client.logger = Rails.logger
70
65
 
71
- Or:
66
+ Or
72
67
 
73
- my_client = StackerBee::Client.new({
74
- logger: Rails.logger
75
- })
68
+ StackerBee::Client.logger = Logger.new
76
69
 
77
- ## Configurable API
70
+ ### Configurable API Version
78
71
 
79
- StackerBee::Client.api_path = "/path/to/your/listApis/response.json"
72
+ By default, StackerBee uses the CloudStack 4.2 API, but it doesn't have to.
73
+ Use a different API version by setting the `api_path` configuration option to the path of a JSON file containing the response from your CloudStack instance's `listApis` command.
80
74
 
81
- ## CLI
75
+ StackerBee::Client.api_path = '/path/to/your/listApis/response.json'
76
+
77
+ ### CloudStack REPL
82
78
 
83
79
  Usage:
84
80
 
85
81
  $ stacker_bee [OPTIONS]
86
82
 
87
- Examples:
83
+ Example:
88
84
 
89
85
  $ stacker_bee -u http://localhost:8080/client/api -a MY_API_KEY -s MY_SECRET_KEY
90
86
  StackerBee CloudStack REPL
@@ -95,17 +91,110 @@ Examples:
95
91
  ...
96
92
  >>
97
93
 
94
+ ## Configuration
95
+
96
+ Configuring a client:
97
+
98
+ cloud_stack = StackerBee::Client.new(
99
+ url: 'http://localhost:8080/client/api'
100
+ api_key: 'API_KEY',
101
+ secret_key: 'SECRET_KEY',
102
+ logger: Rails.logger
103
+ )
104
+
105
+ All configuration parameters set on the `StackerBee::Client` class are used as defaults for `StackerBee::Client` instances.
106
+
107
+ StackerBee::Client.url = 'http://localhost:8080/client/api'
108
+ StackerBee::Client.logger = Rails.logger
109
+
110
+ user_client = StackerBee::Client.new(
111
+ api_key: 'USER_API_KEY',
112
+ secret_key: 'USER_SECRET_KEY'
113
+ )
114
+
115
+ root_client = StackerBee::Client.new(
116
+ api_key: 'ROOT_API_KEY',
117
+ secret_key: 'ROOT_SECRET_KEY'
118
+ )
119
+
120
+ ### URL
121
+
122
+ The URL of your CloudStack instance's URL.
123
+
124
+ StackerBee::Client.url = 'http://localhost:8080/client/api'
125
+
126
+ Or:
127
+
128
+ my_client = StackerBee::Client.new(
129
+ url: 'http://localhost:8080/client/api'
130
+ )
131
+
132
+ ### Keys
133
+
134
+ Your CloudStack credentials, i.e. API key and secret key.
135
+
136
+ StackerBee::Client.api_key = 'MY_API_KEY'
137
+ StackerBee::Client.secret_key = 'MY_SECRET_KEY'
138
+
139
+ Or:
140
+
141
+ my_client = StackerBee::Client.new(
142
+ api_key: 'MY_API_KEY',
143
+ secret_key: 'MY_SECRET_KEY'
144
+ )
145
+
146
+ ### Logger
147
+
148
+ StackerBee can be configured with a custom `Logger`.
149
+
150
+ StackerBee::Client.logger = Logger.new
151
+
152
+ Or
153
+
154
+ my_client = StackerBee::Client.new(
155
+ logger: Logger.new
156
+ )
157
+
158
+ For Rails apps, it's sometimes convenient to use Rail's logger:
159
+
160
+ StackerBee::Client.logger = Rails.logger
161
+
162
+ ### Bulk Configuration
163
+
164
+ The `StackerBee::Client` class can be configured with multiple options at once.
165
+
166
+ StackerBee::Client.default_config = {
167
+ url: 'http://localhost:8080/client/api',
168
+ logger: Rails.logger,
169
+ api_key: 'API_KEY',
170
+ secret_key: 'MY_SECRET_KEY'
171
+ }
172
+
98
173
  ## Contributing
99
174
 
175
+ ### Testing
176
+
100
177
  Running the tests:
101
178
 
102
- $ rake
179
+ $ bundle exec rake
180
+
181
+ ### Testing against CloudStack
103
182
 
104
- To interact with a real CloudStack server:
183
+ To interact with a real CloudStack server, instead of the recorded responses:
105
184
 
106
185
  $ cp config.default.yml config.yml
107
186
 
108
- And edit `config.yml`, specifying the URL and credentials for your CloudStack server. This file is ignored by git.
187
+ And edit `config.yml`, specifying the URL and credentials for your CloudStack server. This file is used by the test suite if it exists, but is ignored by git.
188
+
189
+ ### Coding Style
190
+
191
+ This project uses [Rubocop](https://github.com/bbatsov/rubocop) to enforce code style.
192
+
193
+ $ bundle exec rubocop
194
+
195
+ ## Thanks to
196
+
197
+ - [Chip Childers](http://github.com/chipchilders) for a [reference implementation of a CloudStack client in Ruby](http://chipchilders.github.io/cloudstack_ruby_client/)
109
198
 
110
199
  ## License
111
200
 
data/lib/stacker_bee.rb CHANGED
@@ -1,16 +1,2 @@
1
- require_stacker_bee = if defined?(require_relative)
2
- lambda do |path|
3
- require_relative path
4
- end
5
- else # for 1.8.7
6
- lambda do |path|
7
- require "stacker_bee/#{path}"
8
- end
9
- end
10
-
11
- %w(
12
- version
13
- client
14
- ).each do |file_name|
15
- require_stacker_bee["stacker_bee/#{file_name}"]
16
- end
1
+ require_relative "stacker_bee/version"
2
+ require_relative "stacker_bee/client"
@@ -3,6 +3,7 @@ require "stacker_bee/configuration"
3
3
  require "stacker_bee/api"
4
4
  require "stacker_bee/connection"
5
5
  require "stacker_bee/request"
6
+ require "stacker_bee/dictionary_flattener"
6
7
  require "stacker_bee/response"
7
8
 
8
9
  module StackerBee
@@ -23,7 +24,6 @@ module StackerBee
23
24
  :secret_key=
24
25
 
25
26
  class << self
26
-
27
27
  def reset!
28
28
  @api, @api_path, @default_config = nil
29
29
  end
@@ -66,7 +66,7 @@ module StackerBee
66
66
  end
67
67
 
68
68
  def request(endpoint_name, params = {})
69
- request = Request.new(endpoint_for(endpoint_name), api_key, params)
69
+ request = Request.new(endpoint_for(endpoint_name), api_key, params)
70
70
  request.allow_empty_string_params =
71
71
  configuration.allow_empty_string_params
72
72
  raw_response = connection.get(request)
@@ -2,6 +2,7 @@ require "faraday"
2
2
  require "uri"
3
3
  require "stacker_bee/middleware/signed_query"
4
4
  require "stacker_bee/middleware/logger"
5
+ require "stacker_bee/middleware/detokenizer"
5
6
 
6
7
  module StackerBee
7
8
  class ConnectionError < Exception
@@ -9,15 +10,21 @@ module StackerBee
9
10
 
10
11
  class Connection
11
12
  attr_accessor :configuration
13
+
12
14
  def initialize(configuration)
13
15
  @configuration = configuration
14
16
  uri = URI.parse(self.configuration.url)
15
17
  @path = uri.path
16
18
  uri.path = ''
17
19
  fail ConnectionError, "no protocol specified" unless uri.scheme
20
+ initialize_faraday(uri)
21
+ end
22
+
23
+ def initialize_faraday(uri)
18
24
  @faraday = Faraday.new(url: uri.to_s) do |faraday|
19
- faraday.use Middleware::SignedQuery, self.configuration.secret_key
20
- faraday.use Middleware::Logger, self.configuration.logger
25
+ faraday.use Middleware::Detokenizer
26
+ faraday.use Middleware::SignedQuery, configuration.secret_key
27
+ faraday.use Middleware::Logger, configuration.logger
21
28
  faraday.adapter Faraday.default_adapter # Net::HTTP
22
29
  end
23
30
  end
@@ -0,0 +1,41 @@
1
+ module StackerBee
2
+ class DictionaryFlattener
3
+ LB = "SBLEFTBRACKET"
4
+ RB = "SBRIGHTBRACKET"
5
+
6
+ attr_accessor :params
7
+
8
+ def self.tokenize(key)
9
+ key.gsub(/\[/, LB).gsub(/\]/, RB)
10
+ end
11
+
12
+ def self.detokenize(key)
13
+ key.gsub(/#{LB}/, "[").gsub(/#{RB}/, "]")
14
+ end
15
+
16
+ def initialize(params)
17
+ @params = params.dup
18
+ flatten_params
19
+ end
20
+
21
+ def flatten_params
22
+ hashes = params.select { |key, val| val.respond_to? :keys }
23
+ flatten_map_values(params, hashes)
24
+ end
25
+
26
+ def flatten_map_values(params, hashes)
27
+ hashes.each do |outer|
28
+ remove_empties(outer[1]).each_with_index do |array, index|
29
+ key = self.class.tokenize("#{outer[0]}[#{index}]")
30
+ params["#{key}.key"] = params["#{key}.name"] = array[0]
31
+ params["#{key}.value"] = array[1]
32
+ end
33
+ params.delete outer[0]
34
+ end
35
+ end
36
+
37
+ def remove_empties(hash)
38
+ hash.reject{ |k, v| v.nil? || v == "" }
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,17 @@
1
+ require "faraday"
2
+ require "base64"
3
+
4
+ module StackerBee
5
+ module Middleware
6
+ class Detokenizer < Faraday::Middleware
7
+ def call(env)
8
+ detokenize(env[:url])
9
+ @app.call(env)
10
+ end
11
+
12
+ def detokenize(uri)
13
+ uri.query = StackerBee::DictionaryFlattener.detokenize uri.query
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,6 +1,6 @@
1
- require 'forwardable'
2
- require 'logger'
3
- require 'pp'
1
+ require "forwardable"
2
+ require "logger"
3
+ require "pp"
4
4
 
5
5
  module StackerBee
6
6
  module Middleware
@@ -42,7 +42,6 @@ module StackerBee
42
42
  debug env[:response_headers].pretty_inspect
43
43
  debug env[:body]
44
44
  end
45
-
46
45
  end
47
46
  end
48
47
  end
@@ -1,4 +1,4 @@
1
- require 'forwardable'
1
+ require "forwardable"
2
2
  require "stacker_bee/utilities"
3
3
 
4
4
  module StackerBee
@@ -6,21 +6,26 @@ module StackerBee
6
6
 
7
7
  RESPONSE_TYPE = "json"
8
8
 
9
- attr_accessor :params
10
9
  attr_writer :allow_empty_string_params
10
+ attr_accessor :endpoint, :api_key
11
11
 
12
12
  def initialize(endpoint, api_key, params = {})
13
- params[:api_key] = api_key
14
- params[:command] = endpoint
15
- params[:response] = RESPONSE_TYPE
16
- self.params = params
13
+ @params = params
14
+ self.endpoint = endpoint
15
+ self.api_key = api_key
17
16
  end
18
17
 
19
18
  def query_params
20
- params
19
+ flat_params = DictionaryFlattener.new(@params).params
20
+
21
+ flat_params[:api_key] = api_key
22
+ flat_params[:command] = endpoint
23
+ flat_params[:response] = RESPONSE_TYPE
24
+
25
+ flat_params
21
26
  .reject { |key, val| val.nil? }
22
27
  .reject { |key, val| !allow_empty_string_params && val == '' }
23
- .sort
28
+ .sort_by { |key, val| key.to_s }
24
29
  .map { |(key, val)| [cloud_stack_key(key), cloud_stack_value(val)] }
25
30
  end
26
31
 
@@ -1,6 +1,6 @@
1
1
  require "stacker_bee/body_parser"
2
2
  require "stacker_bee/request_error"
3
- require 'delegate'
3
+ require "delegate"
4
4
 
5
5
  module StackerBee
6
6
  class Response < SimpleDelegator
@@ -1,7 +1,9 @@
1
1
  module StackerBee
2
2
  module Utilities
3
+ REGEX = /\s|-|_/
4
+
3
5
  def uncase(string)
4
- string.to_s.downcase.gsub(/\W|_/, '')
6
+ string.to_s.downcase.gsub(REGEX, '')
5
7
  end
6
8
 
7
9
  def snake_case(string)
@@ -9,7 +11,7 @@ module StackerBee
9
11
  end
10
12
 
11
13
  def camel_case(string, lower = false)
12
- string.to_s.split(/\W|_/).each_with_object('') do |word, memo|
14
+ string.to_s.split(REGEX).each_with_object('') do |word, memo|
13
15
  memo << (memo.empty? && lower ? word[0].downcase : word[0].upcase)
14
16
  memo << word[1..-1]
15
17
  end
@@ -1,3 +1,3 @@
1
1
  module StackerBee
2
- VERSION = "1.0.0"
2
+ VERSION = "1.0.1"
3
3
  end
@@ -0,0 +1,183 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: <CLOUD_STACK_URL>?apiKey=<CLOUD_STACK_API_KEY>&command=listZones&response=json&signature=/MnHgTBKGc%2BXx0Z8KwMllvw92vg=
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ User-Agent:
11
+ - Faraday v0.8.8
12
+ Accept-Encoding:
13
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
14
+ Accept:
15
+ - '*/*'
16
+ response:
17
+ status:
18
+ code: 200
19
+ message: OK
20
+ headers:
21
+ Content-Type:
22
+ - text/javascript; charset=UTF-8
23
+ Content-Length:
24
+ - '403'
25
+ Server:
26
+ - Jetty(6.1.26)
27
+ body:
28
+ encoding: UTF-8
29
+ string: '{ "listzonesresponse" : { "count":1 ,"zone" : [ {"id":"39e9e31e-925c-4b55-8d33-f99912b73dfc","name":"Sandbox-simulator","dns1":"10.147.28.6","internaldns1":"10.147.28.6","guestcidraddress":"10.1.1.0/24","networktype":"Advanced","securitygroupsenabled":false,"allocationstate":"Enabled","zonetoken":"ea3acbc4-cae8-3b82-b42f-7492ddfede00","dhcpprovider":"VirtualRouter","localstorageenabled":false}
30
+ ] } }'
31
+ http_version:
32
+ recorded_at: Thu, 19 Dec 2013 17:34:44 GMT
33
+ - request:
34
+ method: get
35
+ uri: <CLOUD_STACK_URL>?apiKey=<CLOUD_STACK_API_KEY>&command=listNetworkOfferings&response=json&signature=BAdYatJ5Rjcffi6sU8P%2BZtLhSF8=&supportedServices=sourcenat&type=isolated
36
+ body:
37
+ encoding: US-ASCII
38
+ string: ''
39
+ headers:
40
+ User-Agent:
41
+ - Faraday v0.8.8
42
+ Accept-Encoding:
43
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
44
+ Accept:
45
+ - '*/*'
46
+ response:
47
+ status:
48
+ code: 200
49
+ message: OK
50
+ headers:
51
+ Content-Type:
52
+ - text/javascript; charset=UTF-8
53
+ Content-Length:
54
+ - '6006'
55
+ Server:
56
+ - Jetty(6.1.26)
57
+ body:
58
+ encoding: UTF-8
59
+ string: '{ "listnetworkofferingsresponse" : { "count":4 ,"networkoffering" :
60
+ [ {"id":"fa126319-f822-4786-9cd3-30bd0c55ca94","name":"DefaultIsolatedNetworkOfferingWithSourceNatService","displaytext":"Offering
61
+ for Isolated networks with Source Nat service enabled","traffictype":"Guest","isdefault":true,"specifyvlan":false,"conservemode":true,"specifyipranges":false,"availability":"Required","networkrate":200,"state":"Enabled","guestiptype":"Isolated","serviceofferingid":"983410fa-b51b-439c-b8ba-be973804c38f","service":[{"name":"PortForwarding","provider":[{"name":"VirtualRouter"}]},{"name":"StaticNat","provider":[{"name":"VirtualRouter"}],"capability":[{"name":"ElasticIp","value":"false","canchooseservicecapability":false},{"name":"AssociatePublicIP","value":"true","canchooseservicecapability":false}]},{"name":"Dhcp","provider":[{"name":"VirtualRouter"}]},{"name":"Firewall","provider":[{"name":"VirtualRouter"}]},{"name":"Lb","provider":[{"name":"VirtualRouter"}],"capability":[{"name":"SupportedLBIsolation","value":"dedicated","canchooseservicecapability":false},{"name":"ElasticLb","value":"false","canchooseservicecapability":false},{"name":"InlineMode","value":"false","canchooseservicecapability":false}]},{"name":"UserData","provider":[{"name":"VirtualRouter"}]},{"name":"Dns","provider":[{"name":"VirtualRouter"}]},{"name":"SourceNat","provider":[{"name":"VirtualRouter"}],"capability":[{"name":"SupportedSourceNatTypes","value":"peraccount","canchooseservicecapability":false},{"name":"RedundantRouter","value":"false","canchooseservicecapability":false}]},{"name":"Vpn","provider":[{"name":"VirtualRouter"}]}],"forvpc":false,"ispersistent":false,"egressdefaultpolicy":false},
62
+ {"id":"7e70b3b5-5e2c-42cb-9b49-93a87c13a675","name":"DefaultIsolatedNetworkOfferingForVpcNetworks","displaytext":"Offering
63
+ for Isolated Vpc networks with Source Nat service enabled","traffictype":"Guest","isdefault":true,"specifyvlan":false,"conservemode":false,"specifyipranges":false,"availability":"Optional","networkrate":200,"state":"Enabled","guestiptype":"Isolated","serviceofferingid":"983410fa-b51b-439c-b8ba-be973804c38f","service":[{"name":"PortForwarding","provider":[{"name":"VpcVirtualRouter"}]},{"name":"StaticNat","provider":[{"name":"VpcVirtualRouter"}],"capability":[{"name":"ElasticIp","value":"false","canchooseservicecapability":false},{"name":"AssociatePublicIP","value":"true","canchooseservicecapability":false}]},{"name":"Dhcp","provider":[{"name":"VpcVirtualRouter"}]},{"name":"NetworkACL","provider":[{"name":"VpcVirtualRouter"}]},{"name":"Lb","provider":[{"name":"VpcVirtualRouter"}],"capability":[{"name":"SupportedLBIsolation","value":"dedicated","canchooseservicecapability":false},{"name":"ElasticLb","value":"false","canchooseservicecapability":false},{"name":"InlineMode","value":"false","canchooseservicecapability":false}]},{"name":"UserData","provider":[{"name":"VpcVirtualRouter"}]},{"name":"Dns","provider":[{"name":"VpcVirtualRouter"}]},{"name":"SourceNat","provider":[{"name":"VpcVirtualRouter"}],"capability":[{"name":"SupportedSourceNatTypes","value":"peraccount","canchooseservicecapability":false},{"name":"RedundantRouter","value":"false","canchooseservicecapability":false}]},{"name":"Vpn","provider":[{"name":"VpcVirtualRouter"}]}],"forvpc":true,"ispersistent":false,"egressdefaultpolicy":false},
64
+ {"id":"a9d78d52-f306-44c5-bb6d-c121a507db05","name":"DefaultIsolatedNetworkOfferingForVpcNetworksNoLB","displaytext":"Offering
65
+ for Isolated Vpc networks with Source Nat service enabled and LB service Disabled","traffictype":"Guest","isdefault":true,"specifyvlan":false,"conservemode":false,"specifyipranges":false,"availability":"Optional","networkrate":200,"state":"Enabled","guestiptype":"Isolated","serviceofferingid":"983410fa-b51b-439c-b8ba-be973804c38f","service":[{"name":"PortForwarding","provider":[{"name":"VpcVirtualRouter"}]},{"name":"StaticNat","provider":[{"name":"VpcVirtualRouter"}],"capability":[{"name":"ElasticIp","value":"false","canchooseservicecapability":false},{"name":"AssociatePublicIP","value":"true","canchooseservicecapability":false}]},{"name":"Dhcp","provider":[{"name":"VpcVirtualRouter"}]},{"name":"NetworkACL","provider":[{"name":"VpcVirtualRouter"}]},{"name":"UserData","provider":[{"name":"VpcVirtualRouter"}]},{"name":"Dns","provider":[{"name":"VpcVirtualRouter"}]},{"name":"SourceNat","provider":[{"name":"VpcVirtualRouter"}],"capability":[{"name":"SupportedSourceNatTypes","value":"peraccount","canchooseservicecapability":false},{"name":"RedundantRouter","value":"false","canchooseservicecapability":false}]},{"name":"Vpn","provider":[{"name":"VpcVirtualRouter"}]}],"forvpc":true,"ispersistent":false,"egressdefaultpolicy":false},
66
+ {"id":"f9a7b0d0-ce61-4f8d-a360-be1040d67287","name":"DefaultIsolatedNetworkOfferingForVpcNetworksWithInternalLB","displaytext":"Offering
67
+ for Isolated Vpc networks with Internal LB support","traffictype":"Guest","isdefault":true,"specifyvlan":false,"conservemode":false,"specifyipranges":false,"availability":"Optional","networkrate":200,"state":"Enabled","guestiptype":"Isolated","serviceofferingid":"983410fa-b51b-439c-b8ba-be973804c38f","service":[{"name":"Dhcp","provider":[{"name":"VpcVirtualRouter"}]},{"name":"NetworkACL","provider":[{"name":"VpcVirtualRouter"}]},{"name":"Lb","provider":[{"name":"InternalLbVm"}],"capability":[{"name":"SupportedLBIsolation","value":"dedicated","canchooseservicecapability":false},{"name":"ElasticLb","value":"false","canchooseservicecapability":false},{"name":"InlineMode","value":"false","canchooseservicecapability":false}]},{"name":"UserData","provider":[{"name":"VpcVirtualRouter"}]},{"name":"Dns","provider":[{"name":"VpcVirtualRouter"}]},{"name":"SourceNat","provider":[{"name":"VpcVirtualRouter"}],"capability":[{"name":"SupportedSourceNatTypes","value":"peraccount","canchooseservicecapability":false},{"name":"RedundantRouter","value":"false","canchooseservicecapability":false}]}],"forvpc":true,"ispersistent":false,"egressdefaultpolicy":false}
68
+ ] } }'
69
+ http_version:
70
+ recorded_at: Thu, 19 Dec 2013 17:34:44 GMT
71
+ - request:
72
+ method: get
73
+ uri: <CLOUD_STACK_URL>?apiKey=<CLOUD_STACK_API_KEY>&command=createNetwork&displaytext=John&name=John&networkOfferingId=fa126319-f822-4786-9cd3-30bd0c55ca94&response=json&signature=UDcfFqlzwpHJib/9kLKzR0%2BCbyc=&zoneId=39e9e31e-925c-4b55-8d33-f99912b73dfc
74
+ body:
75
+ encoding: US-ASCII
76
+ string: ''
77
+ headers:
78
+ User-Agent:
79
+ - Faraday v0.8.8
80
+ Accept-Encoding:
81
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
82
+ Accept:
83
+ - '*/*'
84
+ response:
85
+ status:
86
+ code: 200
87
+ message: OK
88
+ headers:
89
+ Content-Type:
90
+ - text/javascript; charset=UTF-8
91
+ Content-Length:
92
+ - '4441'
93
+ Server:
94
+ - Jetty(6.1.26)
95
+ body:
96
+ encoding: UTF-8
97
+ string: '{ "createnetworkresponse" : { "network" : {"id":"18772181-9674-4a30-81e7-99ebb6a5baed","name":"John","displaytext":"John","broadcastdomaintype":"Vlan","traffictype":"Guest","gateway":"10.1.1.1","netmask":"255.255.255.0","cidr":"10.1.1.0/24","zoneid":"39e9e31e-925c-4b55-8d33-f99912b73dfc","zonename":"Sandbox-simulator","networkofferingid":"fa126319-f822-4786-9cd3-30bd0c55ca94","networkofferingname":"DefaultIsolatedNetworkOfferingWithSourceNatService","networkofferingdisplaytext":"Offering
98
+ for Isolated networks with Source Nat service enabled","networkofferingconservemode":true,"networkofferingavailability":"Required","issystem":false,"state":"Allocated","related":"18772181-9674-4a30-81e7-99ebb6a5baed","dns1":"10.147.28.6","type":"Isolated","acltype":"Account","account":"admin","domainid":"ce4585b8-41f5-11e3-aaa7-d6558ad1fb9f","domain":"ROOT","service":[{"name":"PortForwarding"},{"name":"StaticNat"},{"name":"Dhcp","capability":[{"name":"DhcpAccrossMultipleSubnets","value":"true","canchooseservicecapability":false}]},{"name":"Firewall","capability":[{"name":"TrafficStatistics","value":"per
99
+ public ip","canchooseservicecapability":false},{"name":"SupportedTrafficDirection","value":"ingress,
100
+ egress","canchooseservicecapability":false},{"name":"SupportedEgressProtocols","value":"tcp,udp,icmp,
101
+ all","canchooseservicecapability":false},{"name":"SupportedProtocols","value":"tcp,udp,icmp","canchooseservicecapability":false},{"name":"MultipleIps","value":"true","canchooseservicecapability":false}]},{"name":"Lb","capability":[{"name":"SupportedLbAlgorithms","value":"roundrobin,leastconn,source","canchooseservicecapability":false},{"name":"SupportedLBIsolation","value":"dedicated","canchooseservicecapability":false},{"name":"SupportedStickinessMethods","value":"[{\"methodname\":\"LbCookie\",\"paramlist\":[{\"paramname\":\"cookie-name\",\"required\":false,\"isflag\":false,\"description\":\"
102
+ \"},{\"paramname\":\"mode\",\"required\":false,\"isflag\":false,\"description\":\"
103
+ \"},{\"paramname\":\"nocache\",\"required\":false,\"isflag\":true,\"description\":\"
104
+ \"},{\"paramname\":\"indirect\",\"required\":false,\"isflag\":true,\"description\":\"
105
+ \"},{\"paramname\":\"postonly\",\"required\":false,\"isflag\":true,\"description\":\"
106
+ \"},{\"paramname\":\"domain\",\"required\":false,\"isflag\":false,\"description\":\"
107
+ \"}],\"description\":\"This is loadbalancer cookie based stickiness method.\"},{\"methodname\":\"AppCookie\",\"paramlist\":[{\"paramname\":\"cookie-name\",\"required\":false,\"isflag\":false,\"description\":\"
108
+ \"},{\"paramname\":\"length\",\"required\":false,\"isflag\":false,\"description\":\"
109
+ \"},{\"paramname\":\"holdtime\",\"required\":false,\"isflag\":false,\"description\":\"
110
+ \"},{\"paramname\":\"request-learn\",\"required\":false,\"isflag\":true,\"description\":\"
111
+ \"},{\"paramname\":\"prefix\",\"required\":false,\"isflag\":true,\"description\":\"
112
+ \"},{\"paramname\":\"mode\",\"required\":false,\"isflag\":false,\"description\":\"
113
+ \"}],\"description\":\"This is App session based sticky method. Define session
114
+ stickiness on an existing application cookie. It can be used only for a specific
115
+ http traffic\"},{\"methodname\":\"SourceBased\",\"paramlist\":[{\"paramname\":\"tablesize\",\"required\":false,\"isflag\":false,\"description\":\"
116
+ \"},{\"paramname\":\"expire\",\"required\":false,\"isflag\":false,\"description\":\"
117
+ \"}],\"description\":\"This is source based Stickiness method, it can be used
118
+ for any type of protocol.\"}]","canchooseservicecapability":false},{"name":"LbSchemes","value":"Public","canchooseservicecapability":false},{"name":"SupportedProtocols","value":"tcp,
119
+ udp","canchooseservicecapability":false}]},{"name":"UserData"},{"name":"Dns","capability":[{"name":"AllowDnsSuffixModification","value":"true","canchooseservicecapability":false}]},{"name":"SourceNat","capability":[{"name":"RedundantRouter","value":"true","canchooseservicecapability":false},{"name":"SupportedSourceNatTypes","value":"peraccount","canchooseservicecapability":false}]},{"name":"Vpn","capability":[{"name":"SupportedVpnTypes","value":"pptp,l2tp,ipsec","canchooseservicecapability":false},{"name":"VpnTypes","value":"removeaccessvpn","canchooseservicecapability":false}]}],"networkdomain":"cs2sandbox.simulator","physicalnetworkid":"46d08659-81e9-4f31-a589-a15aca972af0","restartrequired":false,"specifyipranges":false,"canusefordeploy":true,"ispersistent":false,"tags":[],"displaynetwork":true}
120
+ } }'
121
+ http_version:
122
+ recorded_at: Thu, 19 Dec 2013 17:34:44 GMT
123
+ - request:
124
+ method: get
125
+ uri: <CLOUD_STACK_URL>?apiKey=<CLOUD_STACK_API_KEY>&command=createTags&resourceIds=18772181-9674-4a30-81e7-99ebb6a5baed&resourceType=Network&response=json&signature=xFXocwxqx4V1VH1EGVtjwdGZ0Yw=&tags%5B0%5D%5B%7D.key%5D=speed%20%5Blab%5D&tags%5B0%5D%5B%7D.name%5D=speed%20%5Blab%5D&tags%5B0%5D%5B%7D.value%5D=real%20fast!
126
+ body:
127
+ encoding: US-ASCII
128
+ string: ''
129
+ headers:
130
+ User-Agent:
131
+ - Faraday v0.8.8
132
+ Accept-Encoding:
133
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
134
+ Accept:
135
+ - '*/*'
136
+ response:
137
+ status:
138
+ code: 401
139
+ message: Unauthorized
140
+ headers:
141
+ Content-Type:
142
+ - text/javascript; charset=UTF-8
143
+ Content-Length:
144
+ - '131'
145
+ Server:
146
+ - Jetty(6.1.26)
147
+ body:
148
+ encoding: UTF-8
149
+ string: '{ "createtagsresponse" : {"uuidList":[],"errorcode":401,"errortext":"unable
150
+ to verify user credentials and/or request signature"} }'
151
+ http_version:
152
+ recorded_at: Thu, 19 Dec 2013 17:34:44 GMT
153
+ - request:
154
+ method: get
155
+ uri: <CLOUD_STACK_URL>?apiKey=<CLOUD_STACK_API_KEY>&command=createTags&resourceIds=18772181-9674-4a30-81e7-99ebb6a5baed&resourceType=Network&response=json&signature=IqPennOpZ2NWFlJIBomJG9Ys9Zg=&tags%5B0%5D%5B.key%5D=speed%20%5Blab%5D&tags%5B0%5D%5B.name%5D=speed%20%5Blab%5D&tags%5B0%5D%5B.value%5D=real%20fast!
156
+ body:
157
+ encoding: US-ASCII
158
+ string: ''
159
+ headers:
160
+ User-Agent:
161
+ - Faraday v0.8.8
162
+ Accept-Encoding:
163
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
164
+ Accept:
165
+ - '*/*'
166
+ response:
167
+ status:
168
+ code: 200
169
+ message: OK
170
+ headers:
171
+ Content-Type:
172
+ - text/javascript; charset=UTF-8
173
+ Content-Length:
174
+ - '75'
175
+ Server:
176
+ - Jetty(6.1.26)
177
+ body:
178
+ encoding: UTF-8
179
+ string: '{ "createtagsresponse" : {"jobid":"7f37bba8-dd8e-4ba2-b5a4-bb97f3cf0cab"}
180
+ }'
181
+ http_version:
182
+ recorded_at: Thu, 19 Dec 2013 17:42:02 GMT
183
+ recorded_with: VCR 2.8.0
@@ -0,0 +1,153 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: <CLOUD_STACK_URL>?apiKey=<CLOUD_STACK_API_KEY>&command=listZones&response=json&signature=/MnHgTBKGc%2BXx0Z8KwMllvw92vg=
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ User-Agent:
11
+ - Faraday v0.8.8
12
+ Accept-Encoding:
13
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
14
+ Accept:
15
+ - ! '*/*'
16
+ response:
17
+ status:
18
+ code: 200
19
+ message: OK
20
+ headers:
21
+ Content-Type:
22
+ - text/javascript; charset=UTF-8
23
+ Content-Length:
24
+ - '403'
25
+ Server:
26
+ - Jetty(6.1.26)
27
+ body:
28
+ encoding: US-ASCII
29
+ string: ! '{ "listzonesresponse" : { "count":1 ,"zone" : [ {"id":"39e9e31e-925c-4b55-8d33-f99912b73dfc","name":"Sandbox-simulator","dns1":"10.147.28.6","internaldns1":"10.147.28.6","guestcidraddress":"10.1.1.0/24","networktype":"Advanced","securitygroupsenabled":false,"allocationstate":"Enabled","zonetoken":"ea3acbc4-cae8-3b82-b42f-7492ddfede00","dhcpprovider":"VirtualRouter","localstorageenabled":false}
30
+ ] } }'
31
+ http_version:
32
+ recorded_at: Wed, 18 Dec 2013 21:25:06 GMT
33
+ - request:
34
+ method: get
35
+ uri: <CLOUD_STACK_URL>?apiKey=<CLOUD_STACK_API_KEY>&command=listNetworkOfferings&response=json&signature=BAdYatJ5Rjcffi6sU8P%2BZtLhSF8=&supportedServices=sourcenat&type=isolated
36
+ body:
37
+ encoding: US-ASCII
38
+ string: ''
39
+ headers:
40
+ User-Agent:
41
+ - Faraday v0.8.8
42
+ Accept-Encoding:
43
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
44
+ Accept:
45
+ - ! '*/*'
46
+ response:
47
+ status:
48
+ code: 200
49
+ message: OK
50
+ headers:
51
+ Content-Type:
52
+ - text/javascript; charset=UTF-8
53
+ Content-Length:
54
+ - '6006'
55
+ Server:
56
+ - Jetty(6.1.26)
57
+ body:
58
+ encoding: US-ASCII
59
+ string: ! '{ "listnetworkofferingsresponse" : { "count":4 ,"networkoffering"
60
+ : [ {"id":"fa126319-f822-4786-9cd3-30bd0c55ca94","name":"DefaultIsolatedNetworkOfferingWithSourceNatService","displaytext":"Offering
61
+ for Isolated networks with Source Nat service enabled","traffictype":"Guest","isdefault":true,"specifyvlan":false,"conservemode":true,"specifyipranges":false,"availability":"Required","networkrate":200,"state":"Enabled","guestiptype":"Isolated","serviceofferingid":"983410fa-b51b-439c-b8ba-be973804c38f","service":[{"name":"SourceNat","provider":[{"name":"VirtualRouter"}],"capability":[{"name":"SupportedSourceNatTypes","value":"peraccount","canchooseservicecapability":false},{"name":"RedundantRouter","value":"false","canchooseservicecapability":false}]},{"name":"Dns","provider":[{"name":"VirtualRouter"}]},{"name":"UserData","provider":[{"name":"VirtualRouter"}]},{"name":"PortForwarding","provider":[{"name":"VirtualRouter"}]},{"name":"Firewall","provider":[{"name":"VirtualRouter"}]},{"name":"Lb","provider":[{"name":"VirtualRouter"}],"capability":[{"name":"SupportedLBIsolation","value":"dedicated","canchooseservicecapability":false},{"name":"ElasticLb","value":"false","canchooseservicecapability":false},{"name":"InlineMode","value":"false","canchooseservicecapability":false}]},{"name":"Dhcp","provider":[{"name":"VirtualRouter"}]},{"name":"Vpn","provider":[{"name":"VirtualRouter"}]},{"name":"StaticNat","provider":[{"name":"VirtualRouter"}],"capability":[{"name":"ElasticIp","value":"false","canchooseservicecapability":false},{"name":"AssociatePublicIP","value":"true","canchooseservicecapability":false}]}],"forvpc":false,"ispersistent":false,"egressdefaultpolicy":false},
62
+ {"id":"7e70b3b5-5e2c-42cb-9b49-93a87c13a675","name":"DefaultIsolatedNetworkOfferingForVpcNetworks","displaytext":"Offering
63
+ for Isolated Vpc networks with Source Nat service enabled","traffictype":"Guest","isdefault":true,"specifyvlan":false,"conservemode":false,"specifyipranges":false,"availability":"Optional","networkrate":200,"state":"Enabled","guestiptype":"Isolated","serviceofferingid":"983410fa-b51b-439c-b8ba-be973804c38f","service":[{"name":"SourceNat","provider":[{"name":"VpcVirtualRouter"}],"capability":[{"name":"SupportedSourceNatTypes","value":"peraccount","canchooseservicecapability":false},{"name":"RedundantRouter","value":"false","canchooseservicecapability":false}]},{"name":"Dns","provider":[{"name":"VpcVirtualRouter"}]},{"name":"UserData","provider":[{"name":"VpcVirtualRouter"}]},{"name":"PortForwarding","provider":[{"name":"VpcVirtualRouter"}]},{"name":"Lb","provider":[{"name":"VpcVirtualRouter"}],"capability":[{"name":"SupportedLBIsolation","value":"dedicated","canchooseservicecapability":false},{"name":"ElasticLb","value":"false","canchooseservicecapability":false},{"name":"InlineMode","value":"false","canchooseservicecapability":false}]},{"name":"Dhcp","provider":[{"name":"VpcVirtualRouter"}]},{"name":"Vpn","provider":[{"name":"VpcVirtualRouter"}]},{"name":"StaticNat","provider":[{"name":"VpcVirtualRouter"}],"capability":[{"name":"ElasticIp","value":"false","canchooseservicecapability":false},{"name":"AssociatePublicIP","value":"true","canchooseservicecapability":false}]},{"name":"NetworkACL","provider":[{"name":"VpcVirtualRouter"}]}],"forvpc":true,"ispersistent":false,"egressdefaultpolicy":false},
64
+ {"id":"a9d78d52-f306-44c5-bb6d-c121a507db05","name":"DefaultIsolatedNetworkOfferingForVpcNetworksNoLB","displaytext":"Offering
65
+ for Isolated Vpc networks with Source Nat service enabled and LB service Disabled","traffictype":"Guest","isdefault":true,"specifyvlan":false,"conservemode":false,"specifyipranges":false,"availability":"Optional","networkrate":200,"state":"Enabled","guestiptype":"Isolated","serviceofferingid":"983410fa-b51b-439c-b8ba-be973804c38f","service":[{"name":"SourceNat","provider":[{"name":"VpcVirtualRouter"}],"capability":[{"name":"SupportedSourceNatTypes","value":"peraccount","canchooseservicecapability":false},{"name":"RedundantRouter","value":"false","canchooseservicecapability":false}]},{"name":"Dns","provider":[{"name":"VpcVirtualRouter"}]},{"name":"UserData","provider":[{"name":"VpcVirtualRouter"}]},{"name":"PortForwarding","provider":[{"name":"VpcVirtualRouter"}]},{"name":"Dhcp","provider":[{"name":"VpcVirtualRouter"}]},{"name":"Vpn","provider":[{"name":"VpcVirtualRouter"}]},{"name":"StaticNat","provider":[{"name":"VpcVirtualRouter"}],"capability":[{"name":"ElasticIp","value":"false","canchooseservicecapability":false},{"name":"AssociatePublicIP","value":"true","canchooseservicecapability":false}]},{"name":"NetworkACL","provider":[{"name":"VpcVirtualRouter"}]}],"forvpc":true,"ispersistent":false,"egressdefaultpolicy":false},
66
+ {"id":"f9a7b0d0-ce61-4f8d-a360-be1040d67287","name":"DefaultIsolatedNetworkOfferingForVpcNetworksWithInternalLB","displaytext":"Offering
67
+ for Isolated Vpc networks with Internal LB support","traffictype":"Guest","isdefault":true,"specifyvlan":false,"conservemode":false,"specifyipranges":false,"availability":"Optional","networkrate":200,"state":"Enabled","guestiptype":"Isolated","serviceofferingid":"983410fa-b51b-439c-b8ba-be973804c38f","service":[{"name":"SourceNat","provider":[{"name":"VpcVirtualRouter"}],"capability":[{"name":"SupportedSourceNatTypes","value":"peraccount","canchooseservicecapability":false},{"name":"RedundantRouter","value":"false","canchooseservicecapability":false}]},{"name":"Dns","provider":[{"name":"VpcVirtualRouter"}]},{"name":"UserData","provider":[{"name":"VpcVirtualRouter"}]},{"name":"Lb","provider":[{"name":"InternalLbVm"}],"capability":[{"name":"SupportedLBIsolation","value":"dedicated","canchooseservicecapability":false},{"name":"ElasticLb","value":"false","canchooseservicecapability":false},{"name":"InlineMode","value":"false","canchooseservicecapability":false}]},{"name":"Dhcp","provider":[{"name":"VpcVirtualRouter"}]},{"name":"NetworkACL","provider":[{"name":"VpcVirtualRouter"}]}],"forvpc":true,"ispersistent":false,"egressdefaultpolicy":false}
68
+ ] } }'
69
+ http_version:
70
+ recorded_at: Wed, 18 Dec 2013 21:25:06 GMT
71
+ - request:
72
+ method: get
73
+ uri: <CLOUD_STACK_URL>?apiKey=<CLOUD_STACK_API_KEY>&command=createNetwork&displaytext=John&name=John&networkOfferingId=fa126319-f822-4786-9cd3-30bd0c55ca94&response=json&signature=UDcfFqlzwpHJib/9kLKzR0%2BCbyc=&zoneId=39e9e31e-925c-4b55-8d33-f99912b73dfc
74
+ body:
75
+ encoding: US-ASCII
76
+ string: ''
77
+ headers:
78
+ User-Agent:
79
+ - Faraday v0.8.8
80
+ Accept-Encoding:
81
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
82
+ Accept:
83
+ - ! '*/*'
84
+ response:
85
+ status:
86
+ code: 200
87
+ message: OK
88
+ headers:
89
+ Content-Type:
90
+ - text/javascript; charset=UTF-8
91
+ Content-Length:
92
+ - '4441'
93
+ Server:
94
+ - Jetty(6.1.26)
95
+ body:
96
+ encoding: US-ASCII
97
+ string: ! '{ "createnetworkresponse" : { "network" : {"id":"02e5fd84-e9ea-49f7-ba6a-6d63ea23a27f","name":"John","displaytext":"John","broadcastdomaintype":"Vlan","traffictype":"Guest","gateway":"10.1.1.1","netmask":"255.255.255.0","cidr":"10.1.1.0/24","zoneid":"39e9e31e-925c-4b55-8d33-f99912b73dfc","zonename":"Sandbox-simulator","networkofferingid":"fa126319-f822-4786-9cd3-30bd0c55ca94","networkofferingname":"DefaultIsolatedNetworkOfferingWithSourceNatService","networkofferingdisplaytext":"Offering
98
+ for Isolated networks with Source Nat service enabled","networkofferingconservemode":true,"networkofferingavailability":"Required","issystem":false,"state":"Allocated","related":"02e5fd84-e9ea-49f7-ba6a-6d63ea23a27f","dns1":"10.147.28.6","type":"Isolated","acltype":"Account","account":"admin","domainid":"ce4585b8-41f5-11e3-aaa7-d6558ad1fb9f","domain":"ROOT","service":[{"name":"SourceNat","capability":[{"name":"SupportedSourceNatTypes","value":"peraccount","canchooseservicecapability":false},{"name":"RedundantRouter","value":"true","canchooseservicecapability":false}]},{"name":"Dns","capability":[{"name":"AllowDnsSuffixModification","value":"true","canchooseservicecapability":false}]},{"name":"UserData"},{"name":"PortForwarding"},{"name":"Firewall","capability":[{"name":"TrafficStatistics","value":"per
99
+ public ip","canchooseservicecapability":false},{"name":"SupportedTrafficDirection","value":"ingress,
100
+ egress","canchooseservicecapability":false},{"name":"SupportedProtocols","value":"tcp,udp,icmp","canchooseservicecapability":false},{"name":"MultipleIps","value":"true","canchooseservicecapability":false},{"name":"SupportedEgressProtocols","value":"tcp,udp,icmp,
101
+ all","canchooseservicecapability":false}]},{"name":"Lb","capability":[{"name":"SupportedLbAlgorithms","value":"roundrobin,leastconn,source","canchooseservicecapability":false},{"name":"LbSchemes","value":"Public","canchooseservicecapability":false},{"name":"SupportedStickinessMethods","value":"[{\"methodname\":\"LbCookie\",\"paramlist\":[{\"paramname\":\"cookie-name\",\"required\":false,\"isflag\":false,\"description\":\"
102
+ \"},{\"paramname\":\"mode\",\"required\":false,\"isflag\":false,\"description\":\"
103
+ \"},{\"paramname\":\"nocache\",\"required\":false,\"isflag\":true,\"description\":\"
104
+ \"},{\"paramname\":\"indirect\",\"required\":false,\"isflag\":true,\"description\":\"
105
+ \"},{\"paramname\":\"postonly\",\"required\":false,\"isflag\":true,\"description\":\"
106
+ \"},{\"paramname\":\"domain\",\"required\":false,\"isflag\":false,\"description\":\"
107
+ \"}],\"description\":\"This is loadbalancer cookie based stickiness method.\"},{\"methodname\":\"AppCookie\",\"paramlist\":[{\"paramname\":\"cookie-name\",\"required\":false,\"isflag\":false,\"description\":\"
108
+ \"},{\"paramname\":\"length\",\"required\":false,\"isflag\":false,\"description\":\"
109
+ \"},{\"paramname\":\"holdtime\",\"required\":false,\"isflag\":false,\"description\":\"
110
+ \"},{\"paramname\":\"request-learn\",\"required\":false,\"isflag\":true,\"description\":\"
111
+ \"},{\"paramname\":\"prefix\",\"required\":false,\"isflag\":true,\"description\":\"
112
+ \"},{\"paramname\":\"mode\",\"required\":false,\"isflag\":false,\"description\":\"
113
+ \"}],\"description\":\"This is App session based sticky method. Define session
114
+ stickiness on an existing application cookie. It can be used only for a specific
115
+ http traffic\"},{\"methodname\":\"SourceBased\",\"paramlist\":[{\"paramname\":\"tablesize\",\"required\":false,\"isflag\":false,\"description\":\"
116
+ \"},{\"paramname\":\"expire\",\"required\":false,\"isflag\":false,\"description\":\"
117
+ \"}],\"description\":\"This is source based Stickiness method, it can be used
118
+ for any type of protocol.\"}]","canchooseservicecapability":false},{"name":"SupportedProtocols","value":"tcp,
119
+ udp","canchooseservicecapability":false},{"name":"SupportedLBIsolation","value":"dedicated","canchooseservicecapability":false}]},{"name":"Dhcp","capability":[{"name":"DhcpAccrossMultipleSubnets","value":"true","canchooseservicecapability":false}]},{"name":"Vpn","capability":[{"name":"SupportedVpnTypes","value":"pptp,l2tp,ipsec","canchooseservicecapability":false},{"name":"VpnTypes","value":"removeaccessvpn","canchooseservicecapability":false}]},{"name":"StaticNat"}],"networkdomain":"cs2sandbox.simulator","physicalnetworkid":"46d08659-81e9-4f31-a589-a15aca972af0","restartrequired":false,"specifyipranges":false,"canusefordeploy":true,"ispersistent":false,"tags":[],"displaynetwork":true}
120
+ } }'
121
+ http_version:
122
+ recorded_at: Wed, 18 Dec 2013 21:25:06 GMT
123
+ - request:
124
+ method: get
125
+ uri: <CLOUD_STACK_URL>?apiKey=<CLOUD_STACK_API_KEY>&command=createTags&resourceIds=02e5fd84-e9ea-49f7-ba6a-6d63ea23a27f&resourceType=Network&response=json&signature=KnWoymoA3Q34Iz%2BZ9iFEc8R0lEo=&tags%5B0%5D%5B.key%5D=speed&tags%5B0%5D%5B.name%5D=speed&tags%5B0%5D%5B.value%5D=fast
126
+ body:
127
+ encoding: US-ASCII
128
+ string: ''
129
+ headers:
130
+ User-Agent:
131
+ - Faraday v0.8.8
132
+ Accept-Encoding:
133
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
134
+ Accept:
135
+ - ! '*/*'
136
+ response:
137
+ status:
138
+ code: 200
139
+ message: OK
140
+ headers:
141
+ Content-Type:
142
+ - text/javascript; charset=UTF-8
143
+ Content-Length:
144
+ - '75'
145
+ Server:
146
+ - Jetty(6.1.26)
147
+ body:
148
+ encoding: US-ASCII
149
+ string: ! '{ "createtagsresponse" : {"jobid":"90bdb3e9-dcd5-4aac-9aa9-1879863082cc"}
150
+ }'
151
+ http_version:
152
+ recorded_at: Wed, 18 Dec 2013 21:25:07 GMT
153
+ recorded_with: VCR 2.8.0
@@ -1,5 +1,5 @@
1
- require 'spec_helper'
2
- require 'logger'
1
+ require "spec_helper"
2
+ require "logger"
3
3
 
4
4
  describe "A response to a request sent to the CloudStack API", :vcr do
5
5
  let(:io) { StringIO.new }
@@ -15,9 +15,11 @@ describe "A response to a request sent to the CloudStack API", :vcr do
15
15
  apis_path: File.join(File.dirname(__FILE__), '../fixtures/4.2.json')
16
16
  }
17
17
  end
18
+
18
19
  let(:client) do
19
20
  StackerBee::Client.new(config_hash)
20
21
  end
22
+
21
23
  subject { client.list_accounts }
22
24
 
23
25
  it { should_not be_empty }
@@ -55,10 +57,10 @@ describe "A response to a request sent to the CloudStack API", :vcr do
55
57
  end
56
58
 
57
59
  context "failing to connect" do
58
- let(:url) { "http://127.0.0.1:1234/client/api" }
60
+ let(:url) { "http://127.0.0.1:666/client/api" }
59
61
  it "should raise helpful exception" do
60
62
  klass = StackerBee::ConnectionError
61
- expect(-> { subject }).to raise_error klass, /#{url}/
63
+ expect{ subject }.to raise_error klass, /#{url}/
62
64
  end
63
65
  end
64
66
 
@@ -113,4 +115,30 @@ describe "A response to a request sent to the CloudStack API", :vcr do
113
115
  it { should include "events" }
114
116
  it { should_not include "cpuallocated" }
115
117
  end
118
+
119
+ context "a request parameter with a map" do
120
+ let(:zone_id) { client.list_zones.first["id"] }
121
+
122
+ let(:service_offering_id) do
123
+ client.list_network_offerings(
124
+ supported_services: 'sourcenat',
125
+ type: 'isolated').first["id"]
126
+ end
127
+
128
+ let(:network) do
129
+ client.create_network(zone_id: zone_id,
130
+ network_offering_id: service_offering_id,
131
+ name: "John", displaytext: "John")
132
+ end
133
+
134
+ let(:tag) do
135
+ client.create_tags(resource_type: "Network",
136
+ resource_ids: network["id"],
137
+ tags: { "speed [lab]" => 'real fast!' })
138
+ end
139
+
140
+ it "can create an object" do
141
+ tag.should_not be_nil
142
+ end
143
+ end
116
144
  end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe StackerBee::DictionaryFlattener do
4
+ let(:lb) { StackerBee::DictionaryFlattener::LB }
5
+ let(:rb) { StackerBee::DictionaryFlattener::RB }
6
+ let(:string) { "cool[0].name%21" }
7
+ let(:tokenized_string) { "cool#{lb}0#{rb}.name%21" }
8
+
9
+ describe ".detokenize" do
10
+ subject { StackerBee::DictionaryFlattener.detokenize(tokenized_string) }
11
+ it { should eq string }
12
+ end
13
+
14
+ describe ".tokenize" do
15
+ subject { StackerBee::DictionaryFlattener.tokenize(string) }
16
+ it { should eq tokenized_string }
17
+ end
18
+
19
+ describe ".new" do
20
+ def prefix(number)
21
+ StackerBee::DictionaryFlattener.tokenize "rebels[#{number}]"
22
+ end
23
+
24
+ let(:params) do
25
+ { "time" => "long ago",
26
+ "rebels" => { "r2" => "d2", "r1" => "", "father" => false } }
27
+ end
28
+
29
+ subject do
30
+ StackerBee::DictionaryFlattener.new(params).params
31
+ end
32
+
33
+ it "flattens objects in the manner that cloudstack expects" do
34
+ subject["#{prefix(0)}.name"].should eq "r2"
35
+ subject["#{prefix(0)}.key"].should eq "r2"
36
+ subject["#{prefix(0)}.value"].should eq "d2"
37
+ end
38
+
39
+ it "doesnt flatten empty hashes" do
40
+ subject.should_not have_key "#{prefix(2)}.name"
41
+ end
42
+
43
+ it "handles booleans" do
44
+ subject["#{prefix(1)}.name"].should eq "father"
45
+ end
46
+
47
+ it "removes original map params" do
48
+ subject.should_not have_key "rebels"
49
+ end
50
+ end
51
+ end
@@ -8,33 +8,35 @@ describe StackerBee::Request do
8
8
  list: :all,
9
9
  nothing: nil,
10
10
  deets: [:things, :stuff],
11
+ settings: { cookiename: "Fred", something: "cool" },
11
12
  blank: ''
12
13
  }
13
14
  end
15
+
14
16
  let(:request) { StackerBee::Request.new endpoint, api_key, params }
15
17
  subject { request }
16
- its(:params) do
17
- should == {
18
- list: :all,
19
- api_key: api_key,
20
- command: "listStuff",
21
- nothing: nil,
22
- blank: '',
23
- deets: [:things, :stuff],
24
- response: "json"
25
- }
26
- end
27
18
 
28
19
  describe "#query_params" do
20
+ let(:new_params) { params.merge("settings[1].name" => "cool") }
21
+ let(:flattener) { double(new: double(params: new_params)) }
22
+
23
+ before do
24
+ stub_const "StackerBee::DictionaryFlattener", flattener
25
+ end
26
+
29
27
  subject { request.query_params }
28
+
29
+ it { should include ["settings[1].name", "cool"] }
30
30
  it { should include ["list", :all] }
31
31
  it { should include %W(apiKey #{api_key}) }
32
32
  it { should include %w(command listStuff) }
33
33
  it { should include %w(response json) }
34
34
  it { should include %w(deets things,stuff) }
35
+
35
36
  it "removes params with nil values" do
36
37
  subject.map(&:first).should_not include "nothing"
37
38
  end
39
+
38
40
  it "removes params with blank values" do
39
41
  subject.map(&:first).should_not include "blank"
40
42
  end
@@ -6,6 +6,7 @@ describe StackerBee::Utilities, "#uncase" do
6
6
  it { uncase("foo_bar").should eq "foobar" }
7
7
  it { uncase("foo-bar").should eq "foobar" }
8
8
  it { uncase("fooBar").should eq "foobar" }
9
+ it { uncase("foo[0].Bar").should eq "foo[0].bar" }
9
10
 
10
11
  it { snake_case("Foo Bar").should eq "foo_bar" }
11
12
  it { snake_case("foo_bar").should eq "foo_bar" }
@@ -22,4 +23,5 @@ describe StackerBee::Utilities, "#uncase" do
22
23
  it { camel_case("foo-bar", true).should eq "fooBar" }
23
24
  it { camel_case("fooBar", true).should eq "fooBar" }
24
25
  it { camel_case("fooBar", false).should eq "FooBar" }
26
+ it { camel_case("foo[0].Bar", false).should eq "Foo[0].Bar" }
25
27
  end
data/stacker_bee.gemspec CHANGED
@@ -7,8 +7,8 @@ Gem::Specification.new do |spec|
7
7
  spec.name = "stacker_bee"
8
8
  spec.version = StackerBee::VERSION
9
9
  spec.platform = Gem::Platform::RUBY
10
- spec.authors = ["Greg Sterndale"]
11
- spec.email = ["greg@promptworks.com"]
10
+ spec.authors = ["Greg Sterndale", "Mike Nicholaides"]
11
+ spec.email = ["team@promptworks.com"]
12
12
  spec.summary = %q{Ruby CloudStack client}
13
13
  spec.description = %q{Ruby CloudStack client and CLI}
14
14
  spec.homepage = "http://github.com/promptworks/stacker_bee"
@@ -27,4 +27,5 @@ Gem::Specification.new do |spec|
27
27
  spec.add_development_dependency "rspec", "~> 2.1"
28
28
  spec.add_development_dependency "webmock", "~> 1.15"
29
29
  spec.add_development_dependency "vcr", "~> 2.6"
30
+ spec.add_development_dependency "rubocop"
30
31
  end
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stacker_bee
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg Sterndale
8
+ - Mike Nicholaides
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2013-11-01 00:00:00.000000000 Z
12
+ date: 2013-12-31 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: faraday
@@ -108,9 +109,23 @@ dependencies:
108
109
  - - ~>
109
110
  - !ruby/object:Gem::Version
110
111
  version: '2.6'
112
+ - !ruby/object:Gem::Dependency
113
+ name: rubocop
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
111
126
  description: Ruby CloudStack client and CLI
112
127
  email:
113
- - greg@promptworks.com
128
+ - team@promptworks.com
114
129
  executables:
115
130
  - stacker_bee
116
131
  extensions: []
@@ -133,6 +148,8 @@ files:
133
148
  - lib/stacker_bee/client.rb
134
149
  - lib/stacker_bee/configuration.rb
135
150
  - lib/stacker_bee/connection.rb
151
+ - lib/stacker_bee/dictionary_flattener.rb
152
+ - lib/stacker_bee/middleware/detokenizer.rb
136
153
  - lib/stacker_bee/middleware/logger.rb
137
154
  - lib/stacker_bee/middleware/signed_query.rb
138
155
  - lib/stacker_bee/rash.rb
@@ -143,6 +160,8 @@ files:
143
160
  - lib/stacker_bee/version.rb
144
161
  - spec/cassettes/A_response_to_a_request_sent_to_the_CloudStack_API/.yml
145
162
  - spec/cassettes/A_response_to_a_request_sent_to_the_CloudStack_API/a_nil_request_parameter/properly_executes_the_request.yml
163
+ - spec/cassettes/A_response_to_a_request_sent_to_the_CloudStack_API/a_request_parameter_with_a_Map/can_create_an_object.yml
164
+ - spec/cassettes/A_response_to_a_request_sent_to_the_CloudStack_API/a_request_parameter_with_a_Map/object.yml
146
165
  - spec/cassettes/A_response_to_a_request_sent_to_the_CloudStack_API/a_request_parameter_with_an_Array/.yml
147
166
  - spec/cassettes/A_response_to_a_request_sent_to_the_CloudStack_API/a_request_parameter_with_and_empty_string/properly_executes_the_request.yml
148
167
  - spec/cassettes/A_response_to_a_request_sent_to_the_CloudStack_API/containing_an_error/.yml
@@ -162,6 +181,7 @@ files:
162
181
  - spec/units/stacker_bee/client_spec.rb
163
182
  - spec/units/stacker_bee/configuration_spec.rb
164
183
  - spec/units/stacker_bee/connection_spec.rb
184
+ - spec/units/stacker_bee/dictionary_flattener_spec.rb
165
185
  - spec/units/stacker_bee/middleware/logger_spec.rb
166
186
  - spec/units/stacker_bee/rash_spec.rb
167
187
  - spec/units/stacker_bee/request_error_spec.rb
@@ -197,6 +217,8 @@ summary: Ruby CloudStack client
197
217
  test_files:
198
218
  - spec/cassettes/A_response_to_a_request_sent_to_the_CloudStack_API/.yml
199
219
  - spec/cassettes/A_response_to_a_request_sent_to_the_CloudStack_API/a_nil_request_parameter/properly_executes_the_request.yml
220
+ - spec/cassettes/A_response_to_a_request_sent_to_the_CloudStack_API/a_request_parameter_with_a_Map/can_create_an_object.yml
221
+ - spec/cassettes/A_response_to_a_request_sent_to_the_CloudStack_API/a_request_parameter_with_a_Map/object.yml
200
222
  - spec/cassettes/A_response_to_a_request_sent_to_the_CloudStack_API/a_request_parameter_with_an_Array/.yml
201
223
  - spec/cassettes/A_response_to_a_request_sent_to_the_CloudStack_API/a_request_parameter_with_and_empty_string/properly_executes_the_request.yml
202
224
  - spec/cassettes/A_response_to_a_request_sent_to_the_CloudStack_API/containing_an_error/.yml
@@ -216,6 +238,7 @@ test_files:
216
238
  - spec/units/stacker_bee/client_spec.rb
217
239
  - spec/units/stacker_bee/configuration_spec.rb
218
240
  - spec/units/stacker_bee/connection_spec.rb
241
+ - spec/units/stacker_bee/dictionary_flattener_spec.rb
219
242
  - spec/units/stacker_bee/middleware/logger_spec.rb
220
243
  - spec/units/stacker_bee/rash_spec.rb
221
244
  - spec/units/stacker_bee/request_error_spec.rb