vagrant-skytap 0.1.1a

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.hgignore +22 -0
  4. data/.project +11 -0
  5. data/.rspec +1 -0
  6. data/CHANGELOG.md +85 -0
  7. data/Gemfile +29 -0
  8. data/LICENSE +8 -0
  9. data/README.md +292 -0
  10. data/Rakefile +31 -0
  11. data/Vagrantfile.orig +34 -0
  12. data/bar/checksums.yaml +7 -0
  13. data/bar/data.tar +0 -0
  14. data/bar/metadata +242 -0
  15. data/dummy.box +0 -0
  16. data/example_box/README.md +13 -0
  17. data/example_box/metadata.json +3 -0
  18. data/lib/vagrant-skytap/action/add_vm_to_environment.rb +35 -0
  19. data/lib/vagrant-skytap/action/create_environment.rb +44 -0
  20. data/lib/vagrant-skytap/action/delete_environment.rb +26 -0
  21. data/lib/vagrant-skytap/action/delete_vm.rb +27 -0
  22. data/lib/vagrant-skytap/action/existence_check.rb +35 -0
  23. data/lib/vagrant-skytap/action/fetch_environment.rb +32 -0
  24. data/lib/vagrant-skytap/action/initialize_api_client.rb +28 -0
  25. data/lib/vagrant-skytap/action/is_running.rb +19 -0
  26. data/lib/vagrant-skytap/action/is_stopped.rb +19 -0
  27. data/lib/vagrant-skytap/action/is_suspended.rb +19 -0
  28. data/lib/vagrant-skytap/action/message_already_created.rb +16 -0
  29. data/lib/vagrant-skytap/action/message_already_running.rb +16 -0
  30. data/lib/vagrant-skytap/action/message_environment_url.rb +16 -0
  31. data/lib/vagrant-skytap/action/message_not_created.rb +16 -0
  32. data/lib/vagrant-skytap/action/message_will_not_destroy.rb +16 -0
  33. data/lib/vagrant-skytap/action/mixin_machine_index.rb +22 -0
  34. data/lib/vagrant-skytap/action/prepare_nfs_settings.rb +46 -0
  35. data/lib/vagrant-skytap/action/prepare_nfs_valid_ids.rb +28 -0
  36. data/lib/vagrant-skytap/action/read_ssh_info.rb +23 -0
  37. data/lib/vagrant-skytap/action/read_state.rb +42 -0
  38. data/lib/vagrant-skytap/action/run_environment.rb +53 -0
  39. data/lib/vagrant-skytap/action/run_vm.rb +51 -0
  40. data/lib/vagrant-skytap/action/set_hostname.rb +31 -0
  41. data/lib/vagrant-skytap/action/set_up_vm.rb +21 -0
  42. data/lib/vagrant-skytap/action/stop_environment.rb +43 -0
  43. data/lib/vagrant-skytap/action/stop_vm.rb +43 -0
  44. data/lib/vagrant-skytap/action/store_extra_data.rb +35 -0
  45. data/lib/vagrant-skytap/action/suspend_environment.rb +32 -0
  46. data/lib/vagrant-skytap/action/suspend_vm.rb +32 -0
  47. data/lib/vagrant-skytap/action/timed_provision.rb +21 -0
  48. data/lib/vagrant-skytap/action/update_hardware.rb +37 -0
  49. data/lib/vagrant-skytap/action.rb +272 -0
  50. data/lib/vagrant-skytap/api/busyable.rb +37 -0
  51. data/lib/vagrant-skytap/api/client.rb +127 -0
  52. data/lib/vagrant-skytap/api/credentials.rb +41 -0
  53. data/lib/vagrant-skytap/api/environment.rb +99 -0
  54. data/lib/vagrant-skytap/api/interface.rb +123 -0
  55. data/lib/vagrant-skytap/api/network.rb +40 -0
  56. data/lib/vagrant-skytap/api/public_ip.rb +103 -0
  57. data/lib/vagrant-skytap/api/published_service.rb +90 -0
  58. data/lib/vagrant-skytap/api/resource.rb +44 -0
  59. data/lib/vagrant-skytap/api/runstate_operations.rb +63 -0
  60. data/lib/vagrant-skytap/api/specified_attributes.rb +27 -0
  61. data/lib/vagrant-skytap/api/vm.rb +88 -0
  62. data/lib/vagrant-skytap/api/vpn.rb +146 -0
  63. data/lib/vagrant-skytap/api/vpn_attachment.rb +57 -0
  64. data/lib/vagrant-skytap/config.rb +106 -0
  65. data/lib/vagrant-skytap/core_ext/object/blank.rb +82 -0
  66. data/lib/vagrant-skytap/core_ext/object/tap.rb +8 -0
  67. data/lib/vagrant-skytap/core_ext/try.rb +42 -0
  68. data/lib/vagrant-skytap/environment_properties.rb +11 -0
  69. data/lib/vagrant-skytap/errors.rb +59 -0
  70. data/lib/vagrant-skytap/plugin.rb +73 -0
  71. data/lib/vagrant-skytap/properties.rb +42 -0
  72. data/lib/vagrant-skytap/provider.rb +50 -0
  73. data/lib/vagrant-skytap/setup_helper.rb +193 -0
  74. data/lib/vagrant-skytap/util/ip_address.rb +69 -0
  75. data/lib/vagrant-skytap/util/subnet.rb +97 -0
  76. data/lib/vagrant-skytap/util/timer.rb +17 -0
  77. data/lib/vagrant-skytap/version.rb +5 -0
  78. data/lib/vagrant-skytap/version.rb.orig +5 -0
  79. data/lib/vagrant-skytap/vm_properties.rb +22 -0
  80. data/lib/vagrant-skytap.rb +23 -0
  81. data/locales/en.yml +127 -0
  82. data/skytap-dummy.box +0 -0
  83. data/spec/acceptance/base.rb +2 -0
  84. data/spec/acceptance/provider/halt_spec.rb +3 -0
  85. data/spec/acceptance/shared/context_skytap.rb +3 -0
  86. data/spec/spec_helper.rb +1 -0
  87. data/spec/support/isolated_environment.rb +45 -0
  88. data/spec/unit/base.rb +57 -0
  89. data/spec/unit/config_spec.rb +73 -0
  90. data/spec/unit/environment_spec.rb +144 -0
  91. data/spec/unit/skeletons/empty_environment.json +19 -0
  92. data/spec/unit/skeletons/network1.json +36 -0
  93. data/spec/unit/skeletons/vm1.json +85 -0
  94. data/spec/unit/support/dummy_communicator.rb +83 -0
  95. data/spec/unit/support/dummy_provider.rb +41 -0
  96. data/spec/unit/support/isolated_environment.rb +217 -0
  97. data/spec/unit/support/shared/action_synced_folders_context.rb +15 -0
  98. data/spec/unit/support/shared/base_context.rb +116 -0
  99. data/spec/unit/support/shared/capability_helpers_context.rb +29 -0
  100. data/spec/unit/support/shared/plugin_command_context.rb +12 -0
  101. data/spec/unit/support/shared/skytap_context.rb +3 -0
  102. data/spec/unit/vm_spec.rb +118 -0
  103. data/tasks/acceptance.rake +22 -0
  104. data/tasks/bundler.rake +3 -0
  105. data/tasks/test.rake +14 -0
  106. data/vagrant-skytap.gemspec +62 -0
  107. data/vagrant-spec.config.rb +10 -0
  108. 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