stacker_bee 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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