vagrant-conoha 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.rubocop.yml +35 -0
  4. data/CHANGELOG.md +3 -0
  5. data/Gemfile +19 -0
  6. data/LICENSE +23 -0
  7. data/Rakefile +25 -0
  8. data/Vagrantfile +71 -0
  9. data/dummy.box +0 -0
  10. data/example_box/README.md +13 -0
  11. data/example_box/metadata.json +3 -0
  12. data/functional_tests/Vagrantfile +58 -0
  13. data/functional_tests/keys/vagrant-openstack +27 -0
  14. data/functional_tests/keys/vagrant-openstack.pub +1 -0
  15. data/functional_tests/run_tests.sh +142 -0
  16. data/lib/vagrant-conoha.rb +29 -0
  17. data/lib/vagrant-conoha/action.rb +227 -0
  18. data/lib/vagrant-conoha/action/abstract_action.rb +22 -0
  19. data/lib/vagrant-conoha/action/connect_openstack.rb +60 -0
  20. data/lib/vagrant-conoha/action/create_server.rb +154 -0
  21. data/lib/vagrant-conoha/action/create_stack.rb +68 -0
  22. data/lib/vagrant-conoha/action/delete_server.rb +53 -0
  23. data/lib/vagrant-conoha/action/delete_stack.rb +73 -0
  24. data/lib/vagrant-conoha/action/message.rb +19 -0
  25. data/lib/vagrant-conoha/action/provision.rb +60 -0
  26. data/lib/vagrant-conoha/action/read_ssh_info.rb +72 -0
  27. data/lib/vagrant-conoha/action/read_state.rb +43 -0
  28. data/lib/vagrant-conoha/action/resume.rb +24 -0
  29. data/lib/vagrant-conoha/action/start_server.rb +24 -0
  30. data/lib/vagrant-conoha/action/stop_server.rb +25 -0
  31. data/lib/vagrant-conoha/action/suspend.rb +24 -0
  32. data/lib/vagrant-conoha/action/sync_folders.rb +129 -0
  33. data/lib/vagrant-conoha/action/wait_accessible.rb +61 -0
  34. data/lib/vagrant-conoha/action/wait_active.rb +33 -0
  35. data/lib/vagrant-conoha/action/wait_stop.rb +33 -0
  36. data/lib/vagrant-conoha/catalog/openstack_catalog.rb +67 -0
  37. data/lib/vagrant-conoha/client/cinder.rb +39 -0
  38. data/lib/vagrant-conoha/client/domain.rb +159 -0
  39. data/lib/vagrant-conoha/client/glance.rb +65 -0
  40. data/lib/vagrant-conoha/client/heat.rb +49 -0
  41. data/lib/vagrant-conoha/client/http_utils.rb +116 -0
  42. data/lib/vagrant-conoha/client/keystone.rb +77 -0
  43. data/lib/vagrant-conoha/client/neutron.rb +48 -0
  44. data/lib/vagrant-conoha/client/nova.rb +212 -0
  45. data/lib/vagrant-conoha/client/openstack.rb +59 -0
  46. data/lib/vagrant-conoha/client/request_logger.rb +23 -0
  47. data/lib/vagrant-conoha/client/rest_utils.rb +25 -0
  48. data/lib/vagrant-conoha/command/abstract_command.rb +51 -0
  49. data/lib/vagrant-conoha/command/flavor_list.rb +24 -0
  50. data/lib/vagrant-conoha/command/image_list.rb +29 -0
  51. data/lib/vagrant-conoha/command/main.rb +51 -0
  52. data/lib/vagrant-conoha/command/network_list.rb +25 -0
  53. data/lib/vagrant-conoha/command/openstack_command.rb +16 -0
  54. data/lib/vagrant-conoha/command/reset.rb +20 -0
  55. data/lib/vagrant-conoha/command/subnet_list.rb +22 -0
  56. data/lib/vagrant-conoha/command/utils.rb +22 -0
  57. data/lib/vagrant-conoha/command/volume_list.rb +25 -0
  58. data/lib/vagrant-conoha/config.rb +390 -0
  59. data/lib/vagrant-conoha/config/http.rb +39 -0
  60. data/lib/vagrant-conoha/config_resolver.rb +285 -0
  61. data/lib/vagrant-conoha/errors.rb +187 -0
  62. data/lib/vagrant-conoha/logging.rb +39 -0
  63. data/lib/vagrant-conoha/plugin.rb +48 -0
  64. data/lib/vagrant-conoha/provider.rb +50 -0
  65. data/lib/vagrant-conoha/utils.rb +26 -0
  66. data/lib/vagrant-conoha/version.rb +15 -0
  67. data/lib/vagrant-conoha/version_checker.rb +76 -0
  68. data/locales/en.yml +393 -0
  69. data/spec/vagrant-conoha/action/connect_openstack_spec.rb +695 -0
  70. data/spec/vagrant-conoha/action/create_server_spec.rb +225 -0
  71. data/spec/vagrant-conoha/action/create_stack_spec.rb +99 -0
  72. data/spec/vagrant-conoha/action/delete_server_spec.rb +89 -0
  73. data/spec/vagrant-conoha/action/delete_stack_spec.rb +63 -0
  74. data/spec/vagrant-conoha/action/message_spec.rb +33 -0
  75. data/spec/vagrant-conoha/action/provision_spec.rb +104 -0
  76. data/spec/vagrant-conoha/action/read_ssh_info_spec.rb +190 -0
  77. data/spec/vagrant-conoha/action/read_state_spec.rb +81 -0
  78. data/spec/vagrant-conoha/action/resume_server_spec.rb +49 -0
  79. data/spec/vagrant-conoha/action/start_server_spec.rb +49 -0
  80. data/spec/vagrant-conoha/action/stop_server_spec.rb +49 -0
  81. data/spec/vagrant-conoha/action/suspend_server_spec.rb +49 -0
  82. data/spec/vagrant-conoha/action/sync_folders_spec.rb +155 -0
  83. data/spec/vagrant-conoha/action/wait_accessible_spec.rb +67 -0
  84. data/spec/vagrant-conoha/action/wait_active_spec.rb +53 -0
  85. data/spec/vagrant-conoha/action/wait_stop_spec.rb +53 -0
  86. data/spec/vagrant-conoha/action_spec.rb +120 -0
  87. data/spec/vagrant-conoha/client/cinder_spec.rb +127 -0
  88. data/spec/vagrant-conoha/client/glance_spec.rb +143 -0
  89. data/spec/vagrant-conoha/client/heat_spec.rb +128 -0
  90. data/spec/vagrant-conoha/client/keystone_spec.rb +150 -0
  91. data/spec/vagrant-conoha/client/neutron_spec.rb +171 -0
  92. data/spec/vagrant-conoha/client/nova_spec.rb +757 -0
  93. data/spec/vagrant-conoha/client/utils_spec.rb +176 -0
  94. data/spec/vagrant-conoha/command/flavor_list_spec.rb +43 -0
  95. data/spec/vagrant-conoha/command/image_list_spec.rb +95 -0
  96. data/spec/vagrant-conoha/command/network_list_spec.rb +65 -0
  97. data/spec/vagrant-conoha/command/reset_spec.rb +24 -0
  98. data/spec/vagrant-conoha/command/subnet_list_spec.rb +45 -0
  99. data/spec/vagrant-conoha/command/volume_list_spec.rb +40 -0
  100. data/spec/vagrant-conoha/config_resolver_spec.rb +860 -0
  101. data/spec/vagrant-conoha/config_spec.rb +373 -0
  102. data/spec/vagrant-conoha/e2e_spec.rb.save +27 -0
  103. data/spec/vagrant-conoha/provider_spec.rb +13 -0
  104. data/spec/vagrant-conoha/spec_helper.rb +37 -0
  105. data/spec/vagrant-conoha/utils_spec.rb +129 -0
  106. data/spec/vagrant-conoha/version_checker_spec.rb +39 -0
  107. data/stackrc +25 -0
  108. data/vagrant-conoha.gemspec +32 -0
  109. metadata +343 -0
@@ -0,0 +1,65 @@
1
+ require 'log4r'
2
+ require 'json'
3
+
4
+ require 'vagrant-conoha/client/http_utils'
5
+ require 'vagrant-conoha/client/rest_utils'
6
+ require 'vagrant-conoha/client/domain'
7
+
8
+ module VagrantPlugins
9
+ module ConoHa
10
+ class GlanceClient
11
+ include Singleton
12
+ include VagrantPlugins::ConoHa::HttpUtils
13
+ include VagrantPlugins::ConoHa::Domain
14
+
15
+ def initialize
16
+ @logger = Log4r::Logger.new('vagrant_openstack::glance')
17
+ @session = VagrantPlugins::ConoHa.session
18
+ end
19
+
20
+ def get_api_version_list(env)
21
+ json = RestUtils.get(env, @session.endpoints[:image],
22
+ 'X-Auth-Token' => @session.token,
23
+ :accept => :json) do |response|
24
+ log_response(response)
25
+ case response.code
26
+ when 200, 300
27
+ response
28
+ when 401
29
+ fail Errors::AuthenticationFailed
30
+ else
31
+ fail Errors::VagrantOpenstackError, message: response.to_s
32
+ end
33
+ end
34
+ JSON.parse(json)['versions']
35
+ end
36
+
37
+ # Endpoint /images exists on both v1 and v2 API
38
+ # The attribute 'visibility' is used to detect
39
+ # if the call has been made on v1 or v2
40
+ #
41
+ # In case of v2 we have all the needed information,
42
+ # but in case of v1 we don't and we have to call
43
+ # /images/detail to get full details
44
+ #
45
+ def get_all_images(env)
46
+ images_json = get(env, "#{@session.endpoints[:image]}/images")
47
+ images = JSON.parse(images_json)['images']
48
+
49
+ return images if images.empty?
50
+
51
+ is_v1 = false
52
+ unless images[0].key? 'visibility'
53
+ is_v1 = true
54
+ images_json = get(env, "#{@session.endpoints[:image]}/images/detail")
55
+ images = JSON.parse(images_json)['images']
56
+ end
57
+
58
+ images.map do |i|
59
+ i['visibility'] = i['is_public'] ? 'public' : 'private' if is_v1
60
+ Image.new(i['id'], i['name'], i['visibility'], i['size'], i['min_ram'], i['min_disk'])
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,49 @@
1
+ require 'log4r'
2
+ require 'json'
3
+
4
+ require 'vagrant-conoha/client/http_utils'
5
+ require 'vagrant-conoha/client/domain'
6
+
7
+ module VagrantPlugins
8
+ module ConoHa
9
+ class HeatClient
10
+ include Singleton
11
+ include VagrantPlugins::ConoHa::HttpUtils
12
+ include VagrantPlugins::ConoHa::Domain
13
+
14
+ def initialize
15
+ @logger = Log4r::Logger.new('vagrant_openstack::glance')
16
+ @session = VagrantPlugins::ConoHa.session
17
+ end
18
+
19
+ def create_stack(env, options)
20
+ stack = {}.tap do |s|
21
+ s['stack_name'] = options[:name] if options[:name]
22
+ s['template'] = options[:template]
23
+ end
24
+ stack_res = post(env, "#{@session.endpoints[:orchestration]}/stacks", stack.to_json)
25
+ JSON.parse(stack_res)['stack']['id']
26
+ end
27
+
28
+ def get_stack_details(env, stack_name, stack_id)
29
+ stack_exists do
30
+ server_details = get(env, "#{@session.endpoints[:orchestration]}/stacks/#{stack_name}/#{stack_id}")
31
+ JSON.parse(server_details)['stack']
32
+ end
33
+ end
34
+
35
+ def delete_stack(env, stack_name, stack_id)
36
+ stack_exists do
37
+ delete(env, "#{@session.endpoints[:orchestration]}/stacks/#{stack_name}/#{stack_id}")
38
+ end
39
+ end
40
+
41
+ def stack_exists
42
+ return yield
43
+ rescue Errors::VagrantOpenstackError => e
44
+ raise Errors::StackNotFound if e.extra_data[:code] == 404
45
+ raise e
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,116 @@
1
+ require 'log4r'
2
+ require 'json'
3
+
4
+ require 'vagrant-conoha/client/keystone'
5
+ require 'vagrant-conoha/client/request_logger'
6
+ require 'vagrant-conoha/client/rest_utils'
7
+
8
+ module VagrantPlugins
9
+ module ConoHa
10
+ module HttpUtils
11
+ include VagrantPlugins::ConoHa::HttpUtils::RequestLogger
12
+
13
+ def get(env, url, headers = {})
14
+ calling_method = caller[0][/`.*'/][1..-2]
15
+ @logger.debug("#{calling_method} - start")
16
+
17
+ headers.merge!('X-Auth-Token' => @session.token, :accept => :json)
18
+
19
+ log_request(:GET, url, headers)
20
+
21
+ authenticated(env) do
22
+ RestUtils.get(env, url, headers) { |res| handle_response(res) }.tap do
23
+ @logger.debug("#{calling_method} - end")
24
+ end
25
+ end
26
+ end
27
+
28
+ def post(env, url, body = nil, headers = {})
29
+ calling_method = caller[0][/`.*'/][1..-2]
30
+ @logger.debug("#{calling_method} - start")
31
+
32
+ headers.merge!('X-Auth-Token' => @session.token, :accept => :json, :content_type => :json)
33
+
34
+ log_request(:POST, url, body, headers)
35
+
36
+ authenticated(env) do
37
+ RestUtils.post(env, url, body, headers) { |res| handle_response(res) }.tap do
38
+ @logger.debug("#{calling_method} - end")
39
+ end
40
+ end
41
+ end
42
+
43
+ def delete(env, url, headers = {})
44
+ calling_method = caller[0][/`.*'/][1..-2]
45
+ @logger.debug("#{calling_method} - start")
46
+
47
+ headers.merge!('X-Auth-Token' => @session.token, :accept => :json, :content_type => :json)
48
+
49
+ log_request(:DELETE, url, headers)
50
+
51
+ authenticated(env) do
52
+ RestUtils.delete(env, url, headers) { |res| handle_response(res) }.tap do
53
+ @logger.debug("#{calling_method} - end")
54
+ end
55
+ end
56
+ end
57
+
58
+ def get_api_version_list(env, service_type)
59
+ url = @session.endpoints[service_type]
60
+ headers = { 'X-Auth-Token' => @session.token, :accept => :json }
61
+ log_request(:GET, url, headers)
62
+
63
+ json = RestUtils.get(env, url, headers) do |response|
64
+ log_response(response)
65
+ case response.code
66
+ when 200, 300
67
+ response
68
+ when 401
69
+ fail Errors::AuthenticationFailed
70
+ else
71
+ fail Errors::VagrantOpenstackError, message: response.to_s
72
+ end
73
+ end
74
+ JSON.parse(json)['versions']
75
+ end
76
+
77
+ private
78
+
79
+ ERRORS =
80
+ {
81
+ '400' => 'badRequest',
82
+ '404' => 'itemNotFound',
83
+ '409' => 'conflictingRequest'
84
+ }
85
+
86
+ def handle_response(response)
87
+ log_response(response)
88
+ case response.code
89
+ when 200, 201, 202, 204
90
+ response
91
+ when 401
92
+ fail Errors::AuthenticationRequired
93
+ when 400, 404, 409
94
+ message = JSON.parse(response.to_s)[ERRORS[response.code.to_s]]['message']
95
+ fail Errors::VagrantOpenstackError, message: message, code: response.code
96
+ else
97
+ fail Errors::VagrantOpenstackError, message: response.to_s, code: response.code
98
+ end
99
+ end
100
+
101
+ def authenticated(env)
102
+ nb_retry = 0
103
+ begin
104
+ return yield
105
+ rescue Errors::AuthenticationRequired => e
106
+ nb_retry += 1
107
+ env[:ui].warn(e)
108
+ env[:ui].warn(I18n.t('vagrant_openstack.trying_authentication'))
109
+ env[:openstack_client].keystone.authenticate(env)
110
+ retry if nb_retry < 3
111
+ raise e
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,77 @@
1
+ require 'log4r'
2
+ require 'json'
3
+
4
+ require 'vagrant-conoha/client/request_logger'
5
+
6
+ module VagrantPlugins
7
+ module ConoHa
8
+ class KeystoneClient
9
+ include Singleton
10
+ include VagrantPlugins::ConoHa::HttpUtils::RequestLogger
11
+
12
+ def initialize
13
+ @logger = Log4r::Logger.new('vagrant_openstack::keystone')
14
+ @session = VagrantPlugins::ConoHa.session
15
+ end
16
+
17
+ def authenticate(env)
18
+ @logger.info('Authenticating on Keystone')
19
+ config = env[:machine].provider_config
20
+ @logger.info(I18n.t('vagrant_openstack.client.authentication', project: config.tenant_name, user: config.username))
21
+
22
+ post_body =
23
+ {
24
+ auth:
25
+ {
26
+ tenantName: config.tenant_name,
27
+ passwordCredentials:
28
+ {
29
+ username: config.username,
30
+ password: '****'
31
+ }
32
+ }
33
+ }
34
+
35
+ auth_url = get_auth_url_v2 env
36
+
37
+ headers = {
38
+ content_type: :json,
39
+ accept: :json
40
+ }
41
+
42
+ log_request(:POST, auth_url, post_body.to_json, headers)
43
+
44
+ post_body[:auth][:passwordCredentials][:password] = config.password
45
+
46
+ authentication = RestUtils.post(env, auth_url, post_body.to_json, headers) do |response|
47
+ log_response(response)
48
+ case response.code
49
+ when 200
50
+ response
51
+ when 401
52
+ fail Errors::AuthenticationFailed
53
+ when 404
54
+ fail Errors::BadAuthenticationEndpoint
55
+ else
56
+ fail Errors::VagrantOpenstackError, message: response.to_s
57
+ end
58
+ end
59
+
60
+ access = JSON.parse(authentication)['access']
61
+ response_token = access['token']
62
+ @session.token = response_token['id']
63
+ @session.project_id = response_token['tenant']['id']
64
+
65
+ access['serviceCatalog']
66
+ end
67
+
68
+ private
69
+
70
+ def get_auth_url_v2(env)
71
+ url = env[:machine].provider_config.openstack_auth_url
72
+ return url if url.match(%r{/tokens/*$})
73
+ "#{url}/tokens"
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,48 @@
1
+ require 'log4r'
2
+ require 'json'
3
+
4
+ require 'vagrant-conoha/client/http_utils'
5
+ require 'vagrant-conoha/client/domain'
6
+
7
+ module VagrantPlugins
8
+ module ConoHa
9
+ class NeutronClient
10
+ include Singleton
11
+ include VagrantPlugins::ConoHa::HttpUtils
12
+ include VagrantPlugins::ConoHa::Domain
13
+
14
+ def initialize
15
+ @logger = Log4r::Logger.new('vagrant_openstack::neutron')
16
+ @session = VagrantPlugins::ConoHa.session
17
+ end
18
+
19
+ def get_private_networks(env)
20
+ get_networks(env, false)
21
+ end
22
+
23
+ def get_all_networks(env)
24
+ get_networks(env, true)
25
+ end
26
+
27
+ def get_subnets(env)
28
+ subnets_json = get(env, "#{@session.endpoints[:network]}/subnets")
29
+ subnets = []
30
+ JSON.parse(subnets_json)['subnets'].each do |n|
31
+ subnets << Subnet.new(n['id'], n['name'], n['cidr'], n['enable_dhcp'], n['network_id'])
32
+ end
33
+ subnets
34
+ end
35
+
36
+ private
37
+
38
+ def get_networks(env, all)
39
+ networks_json = get(env, "#{@session.endpoints[:network]}/networks")
40
+ networks = []
41
+ JSON.parse(networks_json)['networks'].each do |n|
42
+ networks << Item.new(n['id'], n['name']) if all || n['tenant_id'].eql?(@session.project_id)
43
+ end
44
+ networks
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,212 @@
1
+ require 'log4r'
2
+ require 'json'
3
+
4
+ require 'vagrant-conoha/client/http_utils'
5
+ require 'vagrant-conoha/client/domain'
6
+
7
+ module VagrantPlugins
8
+ module ConoHa
9
+ class NovaClient
10
+ include Singleton
11
+ include VagrantPlugins::ConoHa::HttpUtils
12
+ include VagrantPlugins::ConoHa::Domain
13
+
14
+ def initialize
15
+ @logger = Log4r::Logger.new('vagrant_openstack::nova')
16
+ @session = VagrantPlugins::ConoHa.session
17
+ end
18
+
19
+ def get_all_flavors(env)
20
+ flavors_json = get(env, "#{@session.endpoints[:compute]}/flavors/detail")
21
+ JSON.parse(flavors_json)['flavors'].map do |fl|
22
+ Flavor.new(fl['id'], fl['name'], fl['vcpus'], fl['ram'], fl['disk'])
23
+ end
24
+ end
25
+
26
+ def get_all_floating_ips(env)
27
+ ips_json = get(env, "#{@session.endpoints[:compute]}/os-floating-ips",
28
+ 'X-Auth-Token' => @session.token,
29
+ :accept => :json)
30
+ JSON.parse(ips_json)['floating_ips'].map { |n| FloatingIP.new(n['ip'], n['pool'], n['instance_id']) }
31
+ end
32
+
33
+ def allocate_floating_ip(env, pool)
34
+ ips_json = post(env, "#{@session.endpoints[:compute]}/os-floating-ips",
35
+ {
36
+ pool: pool
37
+ }.to_json,
38
+ 'X-Auth-Token' => @session.token,
39
+ :accept => :json,
40
+ :content_type => :json)
41
+ floating_ip = JSON.parse(ips_json)['floating_ip']
42
+ FloatingIP.new(floating_ip['ip'], floating_ip['pool'], floating_ip['instance_id'])
43
+ end
44
+
45
+ def get_all_images(env)
46
+ images_json = get(env, "#{@session.endpoints[:compute]}/images")
47
+ JSON.parse(images_json)['images'].map { |fl| Image.new(fl['id'], fl['name'], 'unknown') }
48
+ end
49
+
50
+ def create_server(env, options)
51
+ server = {}.tap do |s|
52
+ s['name'] = options[:name]
53
+ if options[:image_ref].nil?
54
+ s['block_device_mapping'] = [{ volume_id: options[:volume_boot][:id],
55
+ device_name: options[:volume_boot][:device] }] if options[:volume_boot].key?(:id)
56
+ s['block_device_mapping_v2'] = [{ boot_index: '0',
57
+ volume_size: options[:volume_boot][:size],
58
+ uuid: options[:volume_boot][:image],
59
+ device_name: options[:volume_boot][:device],
60
+ source_type: 'image',
61
+ destination_type: 'volume',
62
+ delete_on_termination: options[:volume_boot][:delete_on_destroy] }]\
63
+ if options[:volume_boot].key?(:image)
64
+ else
65
+ s['imageRef'] = options[:image_ref]
66
+ end
67
+ s['flavorRef'] = options[:flavor_ref]
68
+ s['key_name'] = options[:keypair]
69
+ s['availability_zone'] = options[:availability_zone] unless options[:availability_zone].nil?
70
+ s['security_groups'] = options[:security_groups] unless options[:security_groups].nil?
71
+ s['user_data'] = Base64.encode64(options[:user_data]) unless options[:user_data].nil?
72
+ s['metadata'] = options[:metadata] unless options[:metadata].nil?
73
+ s['networks'] = options[:networks] unless options[:networks].nil? || options[:networks].empty?
74
+ end
75
+ object = { server: server }
76
+ object['os:scheduler_hints'] = options[:scheduler_hints] unless options[:scheduler_hints].nil?
77
+ server = post(env, "#{@session.endpoints[:compute]}/servers", object.to_json)
78
+ JSON.parse(server)['server']['id']
79
+ end
80
+
81
+ def delete_server(env, server_id)
82
+ instance_exists do
83
+ delete(env, "#{@session.endpoints[:compute]}/servers/#{server_id}")
84
+ end
85
+ end
86
+
87
+ def suspend_server(env, server_id)
88
+ instance_exists do
89
+ change_server_state(env, server_id, :suspend)
90
+ end
91
+ end
92
+
93
+ def resume_server(env, server_id)
94
+ instance_exists do
95
+ change_server_state(env, server_id, :resume)
96
+ end
97
+ end
98
+
99
+ def stop_server(env, server_id)
100
+ instance_exists do
101
+ change_server_state(env, server_id, :stop)
102
+ end
103
+ end
104
+
105
+ def start_server(env, server_id)
106
+ instance_exists do
107
+ change_server_state(env, server_id, :start)
108
+ end
109
+ end
110
+
111
+ def get_server_details(env, server_id)
112
+ instance_exists do
113
+ server_details = get(env, "#{@session.endpoints[:compute]}/servers/#{server_id}")
114
+ JSON.parse(server_details)['server']
115
+ end
116
+ end
117
+
118
+ def add_floating_ip(env, server_id, floating_ip)
119
+ instance_exists do
120
+ check_floating_ip(env, floating_ip)
121
+
122
+ post(env, "#{@session.endpoints[:compute]}/servers/#{server_id}/action",
123
+ { addFloatingIp: { address: floating_ip } }.to_json)
124
+ end
125
+ end
126
+
127
+ def import_keypair(env, public_key)
128
+ keyname = "vagrant-generated-#{Kernel.rand(36**8).to_s(36)}"
129
+
130
+ key_details = post(env, "#{@session.endpoints[:compute]}/os-keypairs",
131
+ { keypair:
132
+ {
133
+ name: keyname,
134
+ public_key: public_key
135
+ }
136
+ }.to_json)
137
+ JSON.parse(key_details)['keypair']['name']
138
+ end
139
+
140
+ def import_keypair_from_file(env, public_key_path)
141
+ fail "File specified in public_key_path #{public_key_path} doesn't exist" unless File.exist?(public_key_path)
142
+ file = File.open(public_key_path)
143
+ import_keypair(env, file.read)
144
+ end
145
+
146
+ def delete_keypair_if_vagrant(env, server_id)
147
+ instance_exists do
148
+ keyname = get_server_details(env, server_id)['key_name']
149
+ if keyname
150
+ delete(env, "#{@session.endpoints[:compute]}/os-keypairs/#{keyname}") if keyname.start_with?('vagrant-generated-')
151
+ end
152
+ end
153
+ end
154
+
155
+ def get_floating_ip_pools(env)
156
+ floating_ips = get(env, "#{@session.endpoints[:compute]}/os-floating-ip-pools")
157
+ JSON.parse(floating_ips)['floating_ip_pools']
158
+ end
159
+
160
+ def get_floating_ips(env)
161
+ floating_ips = get(env, "#{@session.endpoints[:compute]}/os-floating-ips")
162
+ JSON.parse(floating_ips)['floating_ips']
163
+ end
164
+
165
+ def attach_volume(env, server_id, volume_id, device = nil)
166
+ instance_exists do
167
+ attachment = post(env, "#{@session.endpoints[:compute]}/servers/#{server_id}/os-volume_attachments",
168
+ {
169
+ volumeAttachment: {
170
+ volumeId: volume_id,
171
+ device: device
172
+ }
173
+ }.to_json)
174
+ JSON.parse(attachment)['volumeAttachment']
175
+ end
176
+ end
177
+
178
+ private
179
+
180
+ VM_STATES =
181
+ {
182
+ suspend: 'suspend',
183
+ resume: 'resume',
184
+ start: 'os-start',
185
+ stop: 'os-stop'
186
+ }
187
+
188
+ def instance_exists
189
+ return yield
190
+ rescue Errors::VagrantOpenstackError => e
191
+ raise Errors::InstanceNotFound if e.extra_data[:code] == 404
192
+ raise e
193
+ end
194
+
195
+ def change_server_state(env, server_id, new_state)
196
+ post(env, "#{@session.endpoints[:compute]}/servers/#{server_id}/action",
197
+ { :"#{VM_STATES[new_state.to_sym]}" => nil }.to_json)
198
+ end
199
+
200
+ def check_floating_ip(env, floating_ip)
201
+ ip_details = get(env, "#{@session.endpoints[:compute]}/os-floating-ips")
202
+
203
+ JSON.parse(ip_details)['floating_ips'].each do |ip|
204
+ next unless ip['ip'] == floating_ip
205
+ return if ip['instance_id'].nil?
206
+ fail Errors::FloatingIPAlreadyAssigned, floating_ip: floating_ip
207
+ end
208
+ fail Errors::FloatingIPNotAvailable, floating_ip: floating_ip
209
+ end
210
+ end
211
+ end
212
+ end