vagrant-skytap 0.1.1a
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.hgignore +22 -0
- data/.project +11 -0
- data/.rspec +1 -0
- data/CHANGELOG.md +85 -0
- data/Gemfile +29 -0
- data/LICENSE +8 -0
- data/README.md +292 -0
- data/Rakefile +31 -0
- data/Vagrantfile.orig +34 -0
- data/bar/checksums.yaml +7 -0
- data/bar/data.tar +0 -0
- data/bar/metadata +242 -0
- data/dummy.box +0 -0
- data/example_box/README.md +13 -0
- data/example_box/metadata.json +3 -0
- data/lib/vagrant-skytap/action/add_vm_to_environment.rb +35 -0
- data/lib/vagrant-skytap/action/create_environment.rb +44 -0
- data/lib/vagrant-skytap/action/delete_environment.rb +26 -0
- data/lib/vagrant-skytap/action/delete_vm.rb +27 -0
- data/lib/vagrant-skytap/action/existence_check.rb +35 -0
- data/lib/vagrant-skytap/action/fetch_environment.rb +32 -0
- data/lib/vagrant-skytap/action/initialize_api_client.rb +28 -0
- data/lib/vagrant-skytap/action/is_running.rb +19 -0
- data/lib/vagrant-skytap/action/is_stopped.rb +19 -0
- data/lib/vagrant-skytap/action/is_suspended.rb +19 -0
- data/lib/vagrant-skytap/action/message_already_created.rb +16 -0
- data/lib/vagrant-skytap/action/message_already_running.rb +16 -0
- data/lib/vagrant-skytap/action/message_environment_url.rb +16 -0
- data/lib/vagrant-skytap/action/message_not_created.rb +16 -0
- data/lib/vagrant-skytap/action/message_will_not_destroy.rb +16 -0
- data/lib/vagrant-skytap/action/mixin_machine_index.rb +22 -0
- data/lib/vagrant-skytap/action/prepare_nfs_settings.rb +46 -0
- data/lib/vagrant-skytap/action/prepare_nfs_valid_ids.rb +28 -0
- data/lib/vagrant-skytap/action/read_ssh_info.rb +23 -0
- data/lib/vagrant-skytap/action/read_state.rb +42 -0
- data/lib/vagrant-skytap/action/run_environment.rb +53 -0
- data/lib/vagrant-skytap/action/run_vm.rb +51 -0
- data/lib/vagrant-skytap/action/set_hostname.rb +31 -0
- data/lib/vagrant-skytap/action/set_up_vm.rb +21 -0
- data/lib/vagrant-skytap/action/stop_environment.rb +43 -0
- data/lib/vagrant-skytap/action/stop_vm.rb +43 -0
- data/lib/vagrant-skytap/action/store_extra_data.rb +35 -0
- data/lib/vagrant-skytap/action/suspend_environment.rb +32 -0
- data/lib/vagrant-skytap/action/suspend_vm.rb +32 -0
- data/lib/vagrant-skytap/action/timed_provision.rb +21 -0
- data/lib/vagrant-skytap/action/update_hardware.rb +37 -0
- data/lib/vagrant-skytap/action.rb +272 -0
- data/lib/vagrant-skytap/api/busyable.rb +37 -0
- data/lib/vagrant-skytap/api/client.rb +127 -0
- data/lib/vagrant-skytap/api/credentials.rb +41 -0
- data/lib/vagrant-skytap/api/environment.rb +99 -0
- data/lib/vagrant-skytap/api/interface.rb +123 -0
- data/lib/vagrant-skytap/api/network.rb +40 -0
- data/lib/vagrant-skytap/api/public_ip.rb +103 -0
- data/lib/vagrant-skytap/api/published_service.rb +90 -0
- data/lib/vagrant-skytap/api/resource.rb +44 -0
- data/lib/vagrant-skytap/api/runstate_operations.rb +63 -0
- data/lib/vagrant-skytap/api/specified_attributes.rb +27 -0
- data/lib/vagrant-skytap/api/vm.rb +88 -0
- data/lib/vagrant-skytap/api/vpn.rb +146 -0
- data/lib/vagrant-skytap/api/vpn_attachment.rb +57 -0
- data/lib/vagrant-skytap/config.rb +106 -0
- data/lib/vagrant-skytap/core_ext/object/blank.rb +82 -0
- data/lib/vagrant-skytap/core_ext/object/tap.rb +8 -0
- data/lib/vagrant-skytap/core_ext/try.rb +42 -0
- data/lib/vagrant-skytap/environment_properties.rb +11 -0
- data/lib/vagrant-skytap/errors.rb +59 -0
- data/lib/vagrant-skytap/plugin.rb +73 -0
- data/lib/vagrant-skytap/properties.rb +42 -0
- data/lib/vagrant-skytap/provider.rb +50 -0
- data/lib/vagrant-skytap/setup_helper.rb +193 -0
- data/lib/vagrant-skytap/util/ip_address.rb +69 -0
- data/lib/vagrant-skytap/util/subnet.rb +97 -0
- data/lib/vagrant-skytap/util/timer.rb +17 -0
- data/lib/vagrant-skytap/version.rb +5 -0
- data/lib/vagrant-skytap/version.rb.orig +5 -0
- data/lib/vagrant-skytap/vm_properties.rb +22 -0
- data/lib/vagrant-skytap.rb +23 -0
- data/locales/en.yml +127 -0
- data/skytap-dummy.box +0 -0
- data/spec/acceptance/base.rb +2 -0
- data/spec/acceptance/provider/halt_spec.rb +3 -0
- data/spec/acceptance/shared/context_skytap.rb +3 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/isolated_environment.rb +45 -0
- data/spec/unit/base.rb +57 -0
- data/spec/unit/config_spec.rb +73 -0
- data/spec/unit/environment_spec.rb +144 -0
- data/spec/unit/skeletons/empty_environment.json +19 -0
- data/spec/unit/skeletons/network1.json +36 -0
- data/spec/unit/skeletons/vm1.json +85 -0
- data/spec/unit/support/dummy_communicator.rb +83 -0
- data/spec/unit/support/dummy_provider.rb +41 -0
- data/spec/unit/support/isolated_environment.rb +217 -0
- data/spec/unit/support/shared/action_synced_folders_context.rb +15 -0
- data/spec/unit/support/shared/base_context.rb +116 -0
- data/spec/unit/support/shared/capability_helpers_context.rb +29 -0
- data/spec/unit/support/shared/plugin_command_context.rb +12 -0
- data/spec/unit/support/shared/skytap_context.rb +3 -0
- data/spec/unit/vm_spec.rb +118 -0
- data/tasks/acceptance.rake +22 -0
- data/tasks/bundler.rake +3 -0
- data/tasks/test.rake +14 -0
- data/vagrant-skytap.gemspec +62 -0
- data/vagrant-spec.config.rb +10 -0
- metadata +247 -0
@@ -0,0 +1,272 @@
|
|
1
|
+
require "pathname"
|
2
|
+
require "vagrant/action/builder"
|
3
|
+
|
4
|
+
module VagrantPlugins
|
5
|
+
module Skytap
|
6
|
+
module Action
|
7
|
+
include Vagrant::Action::Builtin
|
8
|
+
|
9
|
+
# This action is called to halt the remote machine.
|
10
|
+
def self.action_halt
|
11
|
+
Vagrant::Action::Builder.new.tap do |b|
|
12
|
+
b.use ConfigValidate
|
13
|
+
b.use InitializeAPIClient
|
14
|
+
b.use FetchEnvironment
|
15
|
+
b.use Call, ExistenceCheck do |env1, b1|
|
16
|
+
case result = env1[:result]
|
17
|
+
when :missing_environment, :missing_vm, :no_vms
|
18
|
+
b1.use MessageNotCreated
|
19
|
+
else
|
20
|
+
b1.use StopVm
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# This action is called to suspend the remote machine.
|
27
|
+
def self.action_suspend
|
28
|
+
Vagrant::Action::Builder.new.tap do |b|
|
29
|
+
b.use ConfigValidate
|
30
|
+
b.use InitializeAPIClient
|
31
|
+
b.use FetchEnvironment
|
32
|
+
b.use Call, ExistenceCheck do |env1, b1|
|
33
|
+
case result = env1[:result]
|
34
|
+
when :missing_environment, :missing_vm, :no_vms
|
35
|
+
b1.use MessageNotCreated
|
36
|
+
else
|
37
|
+
b1.use SuspendVm
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# This action is called to terminate the remote machine.
|
44
|
+
def self.action_destroy
|
45
|
+
Vagrant::Action::Builder.new.tap do |b|
|
46
|
+
b.use ConfigValidate
|
47
|
+
b.use InitializeAPIClient
|
48
|
+
b.use FetchEnvironment
|
49
|
+
b.use Call, ExistenceCheck do |env, b1|
|
50
|
+
case existence_state = env[:result]
|
51
|
+
when :missing_environment, :no_vms
|
52
|
+
b1.use MessageNotCreated
|
53
|
+
b1.use DeleteEnvironment
|
54
|
+
next
|
55
|
+
when :missing_vm
|
56
|
+
b1.use MessageNotCreated
|
57
|
+
b1.use DeleteVm
|
58
|
+
next
|
59
|
+
end
|
60
|
+
b1.use Call, DestroyConfirm do |env2, b2|
|
61
|
+
if env2[:result]
|
62
|
+
case existence_state
|
63
|
+
when :one_of_many_vms
|
64
|
+
b2.use DeleteVm
|
65
|
+
else
|
66
|
+
b2.use DeleteEnvironment
|
67
|
+
end
|
68
|
+
b2.use ProvisionerCleanup if defined?(ProvisionerCleanup)
|
69
|
+
else
|
70
|
+
b2.use MessageWillNotDestroy
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
b.use PrepareNFSValidIds
|
75
|
+
b.use SyncedFolderCleanup
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# This action is called when `vagrant provision` is called.
|
80
|
+
def self.action_provision
|
81
|
+
Vagrant::Action::Builder.new.tap do |b|
|
82
|
+
b.use ConfigValidate
|
83
|
+
b.use InitializeAPIClient
|
84
|
+
b.use FetchEnvironment
|
85
|
+
b.use Call, ExistenceCheck do |env, b1|
|
86
|
+
case result = env[:result]
|
87
|
+
when :missing_environment, :missing_vm, :no_vms
|
88
|
+
b1.use MessageNotCreated
|
89
|
+
next
|
90
|
+
end
|
91
|
+
b1.use Call, IsStopped do |env2, b2|
|
92
|
+
b2.use Call, IsRunning do |env3, b3|
|
93
|
+
b3.use RunVm unless env3[:result]
|
94
|
+
end
|
95
|
+
|
96
|
+
was_stopped = env2[:result]
|
97
|
+
if was_stopped
|
98
|
+
b2.use PrepareNFSSettings
|
99
|
+
b2.use PrepareNFSValidIds
|
100
|
+
end
|
101
|
+
b2.use Provision
|
102
|
+
if was_stopped
|
103
|
+
b2.use SyncedFolders
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# This action is called to read the SSH info of the machine. The
|
111
|
+
# resulting state is expected to be put into the `:machine_ssh_info`
|
112
|
+
# key.
|
113
|
+
def self.action_read_ssh_info
|
114
|
+
Vagrant::Action::Builder.new.tap do |b|
|
115
|
+
b.use ConfigValidate
|
116
|
+
b.use InitializeAPIClient
|
117
|
+
b.use FetchEnvironment
|
118
|
+
b.use ReadSSHInfo
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# This action is called to read the state of the machine. The
|
123
|
+
# resulting state is expected to be put into the `:machine_state_id`
|
124
|
+
# key.
|
125
|
+
def self.action_read_state
|
126
|
+
Vagrant::Action::Builder.new.tap do |b|
|
127
|
+
b.use ConfigValidate
|
128
|
+
b.use InitializeAPIClient
|
129
|
+
b.use FetchEnvironment
|
130
|
+
#TODO:NLA Can this whole action be removed?
|
131
|
+
b.use ReadState
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# This action is called to SSH into the machine.
|
136
|
+
def self.action_ssh
|
137
|
+
Vagrant::Action::Builder.new.tap do |b|
|
138
|
+
b.use ConfigValidate
|
139
|
+
b.use InitializeAPIClient
|
140
|
+
b.use FetchEnvironment
|
141
|
+
b.use Call, ExistenceCheck do |env1, b1|
|
142
|
+
case result = env1[:result]
|
143
|
+
when :missing_environment, :missing_vm, :no_vms
|
144
|
+
b1.use MessageNotCreated
|
145
|
+
else
|
146
|
+
b1.use SSHExec
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.action_ssh_run
|
153
|
+
Vagrant::Action::Builder.new.tap do |b|
|
154
|
+
b.use ConfigValidate
|
155
|
+
b.use InitializeAPIClient
|
156
|
+
b.use FetchEnvironment
|
157
|
+
b.use Call, ExistenceCheck do |env1, b1|
|
158
|
+
case result = env1[:result]
|
159
|
+
when :missing_environment, :missing_vm, :no_vms
|
160
|
+
b1.use MessageNotCreated
|
161
|
+
else
|
162
|
+
b1.use SSHRun
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.action_prepare_boot
|
169
|
+
Vagrant::Action::Builder.new.tap do |b|
|
170
|
+
b.use PrepareNFSSettings
|
171
|
+
b.use PrepareNFSValidIds
|
172
|
+
b.use Provision
|
173
|
+
b.use SyncedFolderCleanup
|
174
|
+
b.use SyncedFolders
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.action_resume
|
179
|
+
Vagrant::Action::Builder.new.tap do |b|
|
180
|
+
b.use action_up
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# This action is called to bring the box up from nothing.
|
185
|
+
def self.action_up
|
186
|
+
Vagrant::Action::Builder.new.tap do |b|
|
187
|
+
b.use HandleBox
|
188
|
+
b.use ConfigValidate
|
189
|
+
b.use InitializeAPIClient
|
190
|
+
b.use FetchEnvironment
|
191
|
+
b.use Call, ExistenceCheck do |env, b1|
|
192
|
+
case result = env[:result]
|
193
|
+
when :missing_environment
|
194
|
+
b1.use CreateEnvironment
|
195
|
+
b1.use MessageEnvironmentUrl
|
196
|
+
when :no_vms, :missing_vm
|
197
|
+
b1.use AddVmToEnvironment
|
198
|
+
else
|
199
|
+
next
|
200
|
+
end
|
201
|
+
b1.use StoreExtraData
|
202
|
+
b1.use SetUpVm
|
203
|
+
end
|
204
|
+
b.use Call, IsRunning do |env, b1|
|
205
|
+
if env[:result]
|
206
|
+
b1.use MessageAlreadyRunning
|
207
|
+
next
|
208
|
+
end
|
209
|
+
|
210
|
+
b1.use Call, IsStopped do |env2, b2|
|
211
|
+
if env2[:result]
|
212
|
+
b2.use UpdateHardware
|
213
|
+
b2.use SetHostname
|
214
|
+
b2.use action_prepare_boot
|
215
|
+
end
|
216
|
+
|
217
|
+
b2.use RunVm
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def self.action_reload
|
224
|
+
Vagrant::Action::Builder.new.tap do |b|
|
225
|
+
b.use ConfigValidate
|
226
|
+
b.use InitializeAPIClient
|
227
|
+
b.use FetchEnvironment
|
228
|
+
b.use Call, ExistenceCheck do |env, b2|
|
229
|
+
case env[:result]
|
230
|
+
when :missing_environment, :missing_vm, :no_vms
|
231
|
+
b2.use MessageNotCreated
|
232
|
+
else
|
233
|
+
b2.use action_halt
|
234
|
+
b2.use action_up
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# The autoload farm
|
241
|
+
action_root = Pathname.new(File.expand_path("../action", __FILE__))
|
242
|
+
autoload :StoreExtraData, action_root.join("store_extra_data")
|
243
|
+
autoload :AddVmToEnvironment, action_root.join("add_vm_to_environment")
|
244
|
+
autoload :CreateEnvironment, action_root.join("create_environment")
|
245
|
+
autoload :DeleteEnvironment, action_root.join("delete_environment")
|
246
|
+
autoload :DeleteVm, action_root.join("delete_vm")
|
247
|
+
autoload :ExistenceCheck, action_root.join("existence_check")
|
248
|
+
autoload :FetchEnvironment, action_root.join("fetch_environment")
|
249
|
+
autoload :InitializeAPIClient, action_root.join("initialize_api_client")
|
250
|
+
autoload :IsRunning, action_root.join("is_running")
|
251
|
+
autoload :IsStopped, action_root.join("is_stopped")
|
252
|
+
autoload :IsSuspended, action_root.join("is_suspended")
|
253
|
+
autoload :MessageAlreadyCreated, action_root.join("message_already_created")
|
254
|
+
autoload :MessageAlreadyRunning, action_root.join("message_already_running")
|
255
|
+
autoload :MessageNotCreated, action_root.join("message_not_created")
|
256
|
+
autoload :MessageEnvironmentUrl, action_root.join("message_environment_url")
|
257
|
+
autoload :MessageWillNotDestroy, action_root.join("message_will_not_destroy")
|
258
|
+
autoload :PrepareNFSSettings, action_root.join("prepare_nfs_settings")
|
259
|
+
autoload :PrepareNFSValidIds, action_root.join("prepare_nfs_valid_ids")
|
260
|
+
autoload :ReadSSHInfo, action_root.join("read_ssh_info")
|
261
|
+
autoload :ReadState, action_root.join("read_state")
|
262
|
+
autoload :RunEnvironment, action_root.join("run_environment")
|
263
|
+
autoload :RunVm, action_root.join("run_vm")
|
264
|
+
autoload :SetHostname, action_root.join("set_hostname")
|
265
|
+
autoload :SetUpVm, action_root.join("set_up_vm")
|
266
|
+
autoload :StopVm, action_root.join("stop_vm")
|
267
|
+
autoload :SuspendVm, action_root.join("suspend_vm")
|
268
|
+
autoload :TimedProvision, action_root.join("timed_provision") # some plugins now expect this action to exist
|
269
|
+
autoload :UpdateHardware, action_root.join("update_hardware")
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module Skytap
|
5
|
+
module API
|
6
|
+
module Busyable
|
7
|
+
WAIT_TIMEOUT = 600
|
8
|
+
WAIT_ITERATION_PERIOD = 1
|
9
|
+
|
10
|
+
def update_with_retry(attrs, path=nil)
|
11
|
+
retry_while_resource_busy do
|
12
|
+
resp = api_client.put(path || url, JSON.dump(attrs))
|
13
|
+
refresh(JSON.load(resp.body))
|
14
|
+
break
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def retry_while_resource_busy(timeout=WAIT_TIMEOUT, &block)
|
19
|
+
begin
|
20
|
+
Timeout.timeout(timeout) do
|
21
|
+
loop do
|
22
|
+
break if env[:interrupted] # is this right?
|
23
|
+
begin
|
24
|
+
yield
|
25
|
+
rescue Errors::ResourceBusy
|
26
|
+
end
|
27
|
+
sleep WAIT_ITERATION_PERIOD
|
28
|
+
end
|
29
|
+
end
|
30
|
+
rescue Timeout::Error => ex
|
31
|
+
raise Errors::InstanceReadyTimeout, timeout: timeout
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module Skytap
|
5
|
+
module API
|
6
|
+
class Client
|
7
|
+
attr_reader :config, :http
|
8
|
+
|
9
|
+
MAX_RATE_LIMIT_RETRIES = 3
|
10
|
+
DEFAULT_RETRY_AFTER_SECONDS = 10
|
11
|
+
|
12
|
+
def initialize(config)
|
13
|
+
@logger = Log4r::Logger.new("vagrant_skytap::api_client")
|
14
|
+
|
15
|
+
@config = config
|
16
|
+
uri = URI.parse(config.base_url)
|
17
|
+
@http = Net::HTTP.new(uri.host, uri.port)
|
18
|
+
|
19
|
+
#TODO:NLA Turn this on conditionally based on configuration value.
|
20
|
+
@http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
21
|
+
|
22
|
+
@http.use_ssl = uri.port == 443 || uri.scheme == 'https'
|
23
|
+
end
|
24
|
+
|
25
|
+
def get(path, options={})
|
26
|
+
req('GET', path, nil, options)
|
27
|
+
end
|
28
|
+
|
29
|
+
def post(path, body=nil, options={})
|
30
|
+
req('POST', path, body, options)
|
31
|
+
end
|
32
|
+
|
33
|
+
def put(path, body=nil, options={})
|
34
|
+
req('PUT', path, body, options)
|
35
|
+
end
|
36
|
+
|
37
|
+
def delete(path, options={})
|
38
|
+
req('DELETE', path, nil, options)
|
39
|
+
end
|
40
|
+
|
41
|
+
# +path+ may optionally include query and fragment parts
|
42
|
+
#
|
43
|
+
# +options+ are:
|
44
|
+
# query: A string or hash of the query string
|
45
|
+
# extra_headers: A hash of extra headers
|
46
|
+
def req(method, path, body, options={})
|
47
|
+
@logger.info("REST API call: #{method} #{path} #{'body: ' + body if body.present?}")
|
48
|
+
|
49
|
+
uri = URI.parse(path)
|
50
|
+
|
51
|
+
if qq = options[:query]
|
52
|
+
if qq.is_a?(Hash)
|
53
|
+
extra_query = qq.collect{|k,v| [k,v].join('=')}.join('&')
|
54
|
+
else
|
55
|
+
extra_query = qq.to_s
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
if (query = [uri.query, extra_query].compact.join('&')).present?
|
60
|
+
path = [uri.path, query].join('?')
|
61
|
+
end
|
62
|
+
|
63
|
+
headers = default_headers.merge(options[:extra_headers] || {})
|
64
|
+
tries = 0
|
65
|
+
retry_after = DEFAULT_RETRY_AFTER_SECONDS
|
66
|
+
begin
|
67
|
+
tries += 1
|
68
|
+
http.send_request(method, URI.encode(path), body, headers).tap do |ret|
|
69
|
+
@logger.debug("REST API response: #{ret.body}")
|
70
|
+
unless ret.code =~ /^2\d\d/
|
71
|
+
raise Errors::DoesNotExist, object_name: "Object '#{path}'" if ret.code == '404'
|
72
|
+
error_class = case ret.code
|
73
|
+
when '403'
|
74
|
+
Errors::Unauthorized
|
75
|
+
when '422'
|
76
|
+
Errors::UnprocessableEntity
|
77
|
+
when '423'
|
78
|
+
Errors::ResourceBusy
|
79
|
+
when '429'
|
80
|
+
retry_after = ret['Retry-After'] || DEFAULT_RETRY_AFTER_SECONDS
|
81
|
+
Errors::RateLimited
|
82
|
+
else
|
83
|
+
Errors::OperationFailed
|
84
|
+
end
|
85
|
+
raise error_class, err: error_string_from_body(ret)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
rescue Errors::RateLimited => ex
|
89
|
+
raise if tries > MAX_RATE_LIMIT_RETRIES
|
90
|
+
@logger.info("Rate limited, wil retry in #{retry_after} seconds")
|
91
|
+
sleep retry_after.to_f + 0.1
|
92
|
+
retry
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def error_string_from_body(resp)
|
97
|
+
resp = resp.body if resp.respond_to?(:body)
|
98
|
+
begin
|
99
|
+
resp = JSON.load(resp)
|
100
|
+
errors = resp['error'] || resp['errors']
|
101
|
+
errors = errors.join('; ') if errors.respond_to? :join
|
102
|
+
rescue
|
103
|
+
# treat non-JSON string as error text
|
104
|
+
errors = resp
|
105
|
+
end
|
106
|
+
errors if errors.present?
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
|
112
|
+
def default_headers
|
113
|
+
{
|
114
|
+
'Authorization' => auth_header,
|
115
|
+
'Content-Type' => 'application/json',
|
116
|
+
'Accept' => 'application/json'
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
120
|
+
def auth_header
|
121
|
+
"Basic #{Base64.encode64(config.username + ":" +
|
122
|
+
config.api_token)}".gsub("\n", '')
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'vagrant-skytap/api/resource'
|
2
|
+
|
3
|
+
module VagrantPlugins
|
4
|
+
module Skytap
|
5
|
+
module API
|
6
|
+
class Credentials < Resource
|
7
|
+
|
8
|
+
attr_reader :vm
|
9
|
+
attr_reader :username, :password
|
10
|
+
|
11
|
+
reads :text
|
12
|
+
|
13
|
+
def initialize(attrs, vm, env)
|
14
|
+
super
|
15
|
+
@vm = vm
|
16
|
+
|
17
|
+
# Set username and password if we recognize them in the form
|
18
|
+
# "uname / pw."
|
19
|
+
uname, pw = text.split('/', 2).collect(&:strip)
|
20
|
+
if uname && pw
|
21
|
+
@username = uname
|
22
|
+
@password = pw
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Are the credentials in a form that we recognize?
|
27
|
+
def recognized?
|
28
|
+
!!(@username && @password)
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
if recognized?
|
33
|
+
"#{username} / #{password}"
|
34
|
+
else
|
35
|
+
text
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'yaml'
|
3
|
+
require 'vagrant-skytap/environment_properties'
|
4
|
+
require 'vagrant-skytap/api/resource'
|
5
|
+
require 'vagrant-skytap/api/vm'
|
6
|
+
require 'vagrant-skytap/api/network'
|
7
|
+
require_relative 'runstate_operations'
|
8
|
+
|
9
|
+
module VagrantPlugins
|
10
|
+
module Skytap
|
11
|
+
module API
|
12
|
+
class Environment < Resource
|
13
|
+
include RunstateOperations
|
14
|
+
|
15
|
+
RESOURCE_ROOT = '/configurations'
|
16
|
+
class << self
|
17
|
+
def create!(env, vm_url)
|
18
|
+
vm = Vm.fetch(env, vm_url)
|
19
|
+
check_vm_before_adding(env, vm)
|
20
|
+
|
21
|
+
resp = env[:api_client].post(RESOURCE_ROOT, JSON.dump(template_id: vm.template_id, vm_ids: [vm.id]))
|
22
|
+
fetch(env, resp['Location'])
|
23
|
+
end
|
24
|
+
|
25
|
+
def fetch(env, url)
|
26
|
+
resp = env[:api_client].get(url)
|
27
|
+
new(JSON.load(resp.body), env)
|
28
|
+
end
|
29
|
+
|
30
|
+
def properties(env)
|
31
|
+
EnvironmentProperties.read(env[:machine].env.local_data_path)
|
32
|
+
end
|
33
|
+
|
34
|
+
def check_vm_before_adding(env, vm)
|
35
|
+
raise Errors::SourceVmNotStopped, name: env[:machine].name, url: vm.url unless vm.stopped?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
attr_reader :provider_config
|
41
|
+
attr_reader :vms, :networks
|
42
|
+
|
43
|
+
reads :id, :name, :vms, :networks, :region, :runstate, :url
|
44
|
+
|
45
|
+
def initialize(attrs, env)
|
46
|
+
super
|
47
|
+
@provider_config = env[:machine].provider_config
|
48
|
+
end
|
49
|
+
|
50
|
+
def vms
|
51
|
+
@vms ||= (get_api_attribute('vms') || []).collect do |vm_attrs|
|
52
|
+
Vm.new(vm_attrs, self, env)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def get_vm_by_id(id)
|
57
|
+
vms.find{|vm|vm.id == id}
|
58
|
+
end
|
59
|
+
|
60
|
+
def current_vm
|
61
|
+
vms.find(&:current_machine?)
|
62
|
+
end
|
63
|
+
|
64
|
+
def networks
|
65
|
+
@networks ||= (get_api_attribute('networks') || []).collect do |network_attrs|
|
66
|
+
Network.new(network_attrs, self, env)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def refresh(attrs)
|
71
|
+
@vms = nil
|
72
|
+
@networks = nil
|
73
|
+
super
|
74
|
+
end
|
75
|
+
|
76
|
+
def delete
|
77
|
+
retry_while_resource_busy do
|
78
|
+
api_client.delete(url)
|
79
|
+
break
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def add_vm(vm_url)
|
84
|
+
vm = Vm.fetch(env, vm_url)
|
85
|
+
self.class.check_vm_before_adding(env, vm)
|
86
|
+
vm_ids = vms.collect(&:id)
|
87
|
+
|
88
|
+
update_with_retry(template_id: vm.template_id, vm_ids: [vm.id])
|
89
|
+
new_vm_ids = vms.collect(&:id) - vm_ids
|
90
|
+
get_vm_by_id(new_vm_ids.last)
|
91
|
+
end
|
92
|
+
|
93
|
+
def properties
|
94
|
+
@properties ||= EnvironmentProperties.new(env[:machine].env.local_data_path)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'vagrant-skytap/api/resource'
|
2
|
+
require 'vagrant-skytap/api/public_ip'
|
3
|
+
require 'vagrant-skytap/api/published_service'
|
4
|
+
require_relative 'busyable'
|
5
|
+
|
6
|
+
module VagrantPlugins
|
7
|
+
module Skytap
|
8
|
+
module API
|
9
|
+
class Interface < Resource
|
10
|
+
include Busyable
|
11
|
+
|
12
|
+
attr_reader :vm
|
13
|
+
|
14
|
+
reads :id, :ip, :nat_addresses, :network_id, :public_ips, :services
|
15
|
+
|
16
|
+
def initialize(attrs, vm, env)
|
17
|
+
super
|
18
|
+
@vm = vm
|
19
|
+
end
|
20
|
+
|
21
|
+
def network
|
22
|
+
if network_id
|
23
|
+
vm.environment.networks.detect do |network|
|
24
|
+
network.id == network_id
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def public_ips
|
30
|
+
@public_ips ||= (get_api_attribute('public_ips') || []).collect do |ip_attrs|
|
31
|
+
PublicIp.new(ip_attrs, self, env)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def published_services
|
36
|
+
@published_services ||= (get_api_attribute('services') || []).collect do |service_attrs|
|
37
|
+
PublishedService.new(service_attrs, self, env)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def url
|
42
|
+
"/configurations/#{vm.environment.id}/vms/#{vm.id}/interfaces/#{id}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def available_ips
|
46
|
+
resp = api_client.get("#{url}/ips/available")
|
47
|
+
ip_attrs = JSON.load(resp.body)
|
48
|
+
ip_attrs.collect do |attrs|
|
49
|
+
PublicIp.new(attrs, nil, env)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def attachment_for(vpn)
|
54
|
+
network.try(:attachment_for, vpn)
|
55
|
+
end
|
56
|
+
|
57
|
+
def address_for(vpn)
|
58
|
+
if vpn.nat_enabled?
|
59
|
+
if info = vpn_nat_addresses.detect {|aa| aa['vpn_id'] == vpn.id}
|
60
|
+
info['ip_address']
|
61
|
+
end
|
62
|
+
elsif vpn_attachments.any? {|aa| aa.vpn['id'] == vpn.id}
|
63
|
+
ip
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def vpn_attachments
|
68
|
+
if network
|
69
|
+
network.vpn_attachments
|
70
|
+
else
|
71
|
+
[]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def vpn_nat_addresses
|
76
|
+
nat_addresses['vpn_nat_addresses'] || []
|
77
|
+
end
|
78
|
+
|
79
|
+
def nat_addresses
|
80
|
+
get_api_attribute('nat_addresses') || {}
|
81
|
+
end
|
82
|
+
|
83
|
+
def attach_public_ip(ip)
|
84
|
+
address = ip.is_a?(PublicIp) ? ip.address : ip.to_s
|
85
|
+
begin
|
86
|
+
resp = api_client.post("#{url}/ips", JSON.dump(ip: address))
|
87
|
+
rescue Errors::OperationFailed => ex
|
88
|
+
raise Errors::OperationFailed, err: 'Failed to attach public IP'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def published_service_choices
|
93
|
+
[uncreated_published_service_choice] +
|
94
|
+
published_services.collect(&:choice_for_setup)
|
95
|
+
end
|
96
|
+
|
97
|
+
def uncreated_published_service_choice
|
98
|
+
PublishedServiceChoice.uncreated_choice(env, self)
|
99
|
+
end
|
100
|
+
|
101
|
+
def create_published_service(internal_port)
|
102
|
+
#TODO:NLA Move this into PublishedService::create! Same with other places.
|
103
|
+
begin
|
104
|
+
resp = api_client.post("#{url}/services", JSON.dump(internal_port: internal_port))
|
105
|
+
rescue Errors::OperationFailed => ex
|
106
|
+
raise Errors::OperationFailed, err: 'Failed to create published service'
|
107
|
+
end
|
108
|
+
|
109
|
+
service_attrs = JSON.load(resp.body)
|
110
|
+
PublishedService.new(service_attrs, self, env).tap do |service|
|
111
|
+
published_services << service
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def refresh(attrs)
|
116
|
+
@public_ips = nil
|
117
|
+
@published_services = nil
|
118
|
+
super
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|