vagrant-openstack-illuin-provider 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.rubocop.yml +40 -0
  4. data/CHANGELOG.md +282 -0
  5. data/Gemfile +18 -0
  6. data/RELEASE.md +15 -0
  7. data/Rakefile +25 -0
  8. data/Vagrantfile +20 -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-openstack-illuin-provider.rb +29 -0
  17. data/lib/vagrant-openstack-illuin-provider/action.rb +344 -0
  18. data/lib/vagrant-openstack-illuin-provider/action/abstract_action.rb +22 -0
  19. data/lib/vagrant-openstack-illuin-provider/action/connect_openstack.rb +60 -0
  20. data/lib/vagrant-openstack-illuin-provider/action/create_server.rb +187 -0
  21. data/lib/vagrant-openstack-illuin-provider/action/create_stack.rb +76 -0
  22. data/lib/vagrant-openstack-illuin-provider/action/delete_server.rb +53 -0
  23. data/lib/vagrant-openstack-illuin-provider/action/delete_stack.rb +73 -0
  24. data/lib/vagrant-openstack-illuin-provider/action/message.rb +19 -0
  25. data/lib/vagrant-openstack-illuin-provider/action/provision.rb +60 -0
  26. data/lib/vagrant-openstack-illuin-provider/action/read_ssh_info.rb +74 -0
  27. data/lib/vagrant-openstack-illuin-provider/action/read_state.rb +43 -0
  28. data/lib/vagrant-openstack-illuin-provider/action/resume.rb +24 -0
  29. data/lib/vagrant-openstack-illuin-provider/action/snapshot_cleanup.rb +32 -0
  30. data/lib/vagrant-openstack-illuin-provider/action/snapshot_delete.rb +32 -0
  31. data/lib/vagrant-openstack-illuin-provider/action/snapshot_list.rb +22 -0
  32. data/lib/vagrant-openstack-illuin-provider/action/snapshot_restore.rb +29 -0
  33. data/lib/vagrant-openstack-illuin-provider/action/snapshot_save.rb +51 -0
  34. data/lib/vagrant-openstack-illuin-provider/action/start_server.rb +24 -0
  35. data/lib/vagrant-openstack-illuin-provider/action/stop_server.rb +25 -0
  36. data/lib/vagrant-openstack-illuin-provider/action/suspend.rb +24 -0
  37. data/lib/vagrant-openstack-illuin-provider/action/sync_folders.rb +138 -0
  38. data/lib/vagrant-openstack-illuin-provider/action/wait_active.rb +33 -0
  39. data/lib/vagrant-openstack-illuin-provider/action/wait_stop.rb +33 -0
  40. data/lib/vagrant-openstack-illuin-provider/cap/snapshot_list.rb +15 -0
  41. data/lib/vagrant-openstack-illuin-provider/catalog/openstack_catalog.rb +90 -0
  42. data/lib/vagrant-openstack-illuin-provider/client/cinder.rb +39 -0
  43. data/lib/vagrant-openstack-illuin-provider/client/domain.rb +163 -0
  44. data/lib/vagrant-openstack-illuin-provider/client/glance.rb +65 -0
  45. data/lib/vagrant-openstack-illuin-provider/client/heat.rb +49 -0
  46. data/lib/vagrant-openstack-illuin-provider/client/http_utils.rb +116 -0
  47. data/lib/vagrant-openstack-illuin-provider/client/keystone.rb +128 -0
  48. data/lib/vagrant-openstack-illuin-provider/client/neutron.rb +48 -0
  49. data/lib/vagrant-openstack-illuin-provider/client/nova.rb +303 -0
  50. data/lib/vagrant-openstack-illuin-provider/client/openstack.rb +59 -0
  51. data/lib/vagrant-openstack-illuin-provider/client/request_logger.rb +23 -0
  52. data/lib/vagrant-openstack-illuin-provider/client/rest_utils.rb +28 -0
  53. data/lib/vagrant-openstack-illuin-provider/command/abstract_command.rb +51 -0
  54. data/lib/vagrant-openstack-illuin-provider/command/flavor_list.rb +24 -0
  55. data/lib/vagrant-openstack-illuin-provider/command/floatingip_list.rb +32 -0
  56. data/lib/vagrant-openstack-illuin-provider/command/image_list.rb +29 -0
  57. data/lib/vagrant-openstack-illuin-provider/command/main.rb +52 -0
  58. data/lib/vagrant-openstack-illuin-provider/command/network_list.rb +25 -0
  59. data/lib/vagrant-openstack-illuin-provider/command/openstack_command.rb +16 -0
  60. data/lib/vagrant-openstack-illuin-provider/command/reset.rb +20 -0
  61. data/lib/vagrant-openstack-illuin-provider/command/subnet_list.rb +22 -0
  62. data/lib/vagrant-openstack-illuin-provider/command/utils.rb +22 -0
  63. data/lib/vagrant-openstack-illuin-provider/command/volume_list.rb +25 -0
  64. data/lib/vagrant-openstack-illuin-provider/config.rb +505 -0
  65. data/lib/vagrant-openstack-illuin-provider/config/http.rb +39 -0
  66. data/lib/vagrant-openstack-illuin-provider/config_resolver.rb +334 -0
  67. data/lib/vagrant-openstack-illuin-provider/errors.rb +187 -0
  68. data/lib/vagrant-openstack-illuin-provider/logging.rb +39 -0
  69. data/lib/vagrant-openstack-illuin-provider/plugin.rb +58 -0
  70. data/lib/vagrant-openstack-illuin-provider/provider.rb +50 -0
  71. data/lib/vagrant-openstack-illuin-provider/utils.rb +81 -0
  72. data/lib/vagrant-openstack-illuin-provider/version.rb +15 -0
  73. data/lib/vagrant-openstack-illuin-provider/version_checker.rb +76 -0
  74. data/locales/en.yml +412 -0
  75. data/spec/vagrant-openstack-illuin-provider/action/connect_openstack_spec.rb +770 -0
  76. data/spec/vagrant-openstack-illuin-provider/action/create_server_spec.rb +260 -0
  77. data/spec/vagrant-openstack-illuin-provider/action/create_stack_spec.rb +99 -0
  78. data/spec/vagrant-openstack-illuin-provider/action/delete_server_spec.rb +89 -0
  79. data/spec/vagrant-openstack-illuin-provider/action/delete_stack_spec.rb +63 -0
  80. data/spec/vagrant-openstack-illuin-provider/action/message_spec.rb +33 -0
  81. data/spec/vagrant-openstack-illuin-provider/action/provision_spec.rb +97 -0
  82. data/spec/vagrant-openstack-illuin-provider/action/read_ssh_info_spec.rb +202 -0
  83. data/spec/vagrant-openstack-illuin-provider/action/read_state_spec.rb +81 -0
  84. data/spec/vagrant-openstack-illuin-provider/action/resume_server_spec.rb +49 -0
  85. data/spec/vagrant-openstack-illuin-provider/action/start_server_spec.rb +49 -0
  86. data/spec/vagrant-openstack-illuin-provider/action/stop_server_spec.rb +49 -0
  87. data/spec/vagrant-openstack-illuin-provider/action/suspend_server_spec.rb +49 -0
  88. data/spec/vagrant-openstack-illuin-provider/action/sync_folders_spec.rb +155 -0
  89. data/spec/vagrant-openstack-illuin-provider/action/wait_active_spec.rb +53 -0
  90. data/spec/vagrant-openstack-illuin-provider/action/wait_stop_spec.rb +53 -0
  91. data/spec/vagrant-openstack-illuin-provider/action_spec.rb +120 -0
  92. data/spec/vagrant-openstack-illuin-provider/client/cinder_spec.rb +129 -0
  93. data/spec/vagrant-openstack-illuin-provider/client/glance_spec.rb +145 -0
  94. data/spec/vagrant-openstack-illuin-provider/client/heat_spec.rb +130 -0
  95. data/spec/vagrant-openstack-illuin-provider/client/keystone_spec.rb +226 -0
  96. data/spec/vagrant-openstack-illuin-provider/client/neutron_spec.rb +173 -0
  97. data/spec/vagrant-openstack-illuin-provider/client/nova_spec.rb +760 -0
  98. data/spec/vagrant-openstack-illuin-provider/client/utils_spec.rb +176 -0
  99. data/spec/vagrant-openstack-illuin-provider/command/flavor_list_spec.rb +43 -0
  100. data/spec/vagrant-openstack-illuin-provider/command/floatingip_list_spec.rb +74 -0
  101. data/spec/vagrant-openstack-illuin-provider/command/image_list_spec.rb +95 -0
  102. data/spec/vagrant-openstack-illuin-provider/command/network_list_spec.rb +65 -0
  103. data/spec/vagrant-openstack-illuin-provider/command/reset_spec.rb +24 -0
  104. data/spec/vagrant-openstack-illuin-provider/command/subnet_list_spec.rb +45 -0
  105. data/spec/vagrant-openstack-illuin-provider/command/volume_list_spec.rb +40 -0
  106. data/spec/vagrant-openstack-illuin-provider/config_resolver_spec.rb +879 -0
  107. data/spec/vagrant-openstack-illuin-provider/config_spec.rb +416 -0
  108. data/spec/vagrant-openstack-illuin-provider/e2e_spec.rb.save +27 -0
  109. data/spec/vagrant-openstack-illuin-provider/provider_spec.rb +13 -0
  110. data/spec/vagrant-openstack-illuin-provider/spec_helper.rb +37 -0
  111. data/spec/vagrant-openstack-illuin-provider/utils_spec.rb +197 -0
  112. data/spec/vagrant-openstack-illuin-provider/version_checker_spec.rb +39 -0
  113. data/stackrc +25 -0
  114. data/vagrant-openstack-illuin-provider.gemspec +35 -0
  115. metadata +379 -0
@@ -0,0 +1,65 @@
1
+ require 'log4r'
2
+ require 'json'
3
+
4
+ require 'vagrant-openstack-illuin-provider/client/http_utils'
5
+ require 'vagrant-openstack-illuin-provider/client/rest_utils'
6
+ require 'vagrant-openstack-illuin-provider/client/domain'
7
+
8
+ module VagrantPlugins
9
+ module Openstack
10
+ class GlanceClient
11
+ include Singleton
12
+ include VagrantPlugins::Openstack::HttpUtils
13
+ include VagrantPlugins::Openstack::Domain
14
+
15
+ def initialize
16
+ @logger = Log4r::Logger.new('vagrant_openstack::glance')
17
+ @session = VagrantPlugins::Openstack.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-openstack-illuin-provider/client/http_utils'
5
+ require 'vagrant-openstack-illuin-provider/client/domain'
6
+
7
+ module VagrantPlugins
8
+ module Openstack
9
+ class HeatClient
10
+ include Singleton
11
+ include VagrantPlugins::Openstack::HttpUtils
12
+ include VagrantPlugins::Openstack::Domain
13
+
14
+ def initialize
15
+ @logger = Log4r::Logger.new('vagrant_openstack::glance')
16
+ @session = VagrantPlugins::Openstack.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-openstack-illuin-provider/client/keystone'
5
+ require 'vagrant-openstack-illuin-provider/client/request_logger'
6
+ require 'vagrant-openstack-illuin-provider/client/rest_utils'
7
+
8
+ module VagrantPlugins
9
+ module Openstack
10
+ module HttpUtils
11
+ include VagrantPlugins::Openstack::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,128 @@
1
+ require 'log4r'
2
+ require 'json'
3
+
4
+ require 'vagrant-openstack-illuin-provider/client/request_logger'
5
+
6
+ module VagrantPlugins
7
+ module Openstack
8
+ class KeystoneClient
9
+ include Singleton
10
+ include VagrantPlugins::Openstack::HttpUtils::RequestLogger
11
+
12
+ def initialize
13
+ @logger = Log4r::Logger.new('vagrant_openstack::keystone')
14
+ @session = VagrantPlugins::Openstack.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
+ if config.identity_api_version == '2'
23
+ post_body = get_body_2 config
24
+ auth_url = get_auth_url_2 env
25
+ elsif config.identity_api_version == '3'
26
+ post_body = get_body_3 config
27
+ auth_url = get_auth_url_3 env
28
+ end
29
+
30
+ headers = {
31
+ content_type: :json,
32
+ accept: :json
33
+ }
34
+
35
+ log_request(:POST, auth_url, post_body.to_json, headers)
36
+
37
+ if config.identity_api_version == '2'
38
+ post_body[:auth][:passwordCredentials][:password] = config.password
39
+ elsif config.identity_api_version == '3'
40
+ post_body[:auth][:identity][:password][:user][:password] = config.password
41
+ end
42
+
43
+ authentication = RestUtils.post(env, auth_url, post_body.to_json, headers) do |response|
44
+ log_response(response)
45
+ case response.code
46
+ when 200
47
+ response
48
+ when 201
49
+ response
50
+ when 401
51
+ fail Errors::AuthenticationFailed
52
+ when 404
53
+ fail Errors::BadAuthenticationEndpoint
54
+ else
55
+ fail Errors::VagrantOpenstackError, message: response.to_s
56
+ end
57
+ end
58
+
59
+ if config.identity_api_version == '2'
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
+ return access['serviceCatalog']
65
+ elsif config.identity_api_version == '3'
66
+ body = JSON.parse(authentication)
67
+ @session.token = authentication.headers[:x_subject_token]
68
+ @session.project_id = body['token']['project']['id']
69
+ return body['token']['catalog']
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def get_body_2(config)
76
+ {
77
+ auth:
78
+ {
79
+ tenantName: config.tenant_name,
80
+ passwordCredentials:
81
+ {
82
+ username: config.username,
83
+ password: '****'
84
+ }
85
+ }
86
+ }
87
+ end
88
+
89
+ def get_body_3(config)
90
+ {
91
+ auth:
92
+ {
93
+ identity: {
94
+ methods: ['password'],
95
+ password: {
96
+ user: {
97
+ name: config.username,
98
+ domain: {
99
+ name: config.domain_name
100
+ },
101
+ password: '****'
102
+ }
103
+ }
104
+ },
105
+ scope: {
106
+ project: {
107
+ name: config.project_name,
108
+ domain: { name: config.domain_name }
109
+ }
110
+ }
111
+ }
112
+ }
113
+ end
114
+
115
+ def get_auth_url_3(env)
116
+ url = env[:machine].provider_config.openstack_auth_url
117
+ return url if url.match(%r{/tokens/*$})
118
+ "#{url}/auth/tokens"
119
+ end
120
+
121
+ def get_auth_url_2(env)
122
+ url = env[:machine].provider_config.openstack_auth_url
123
+ return url if url.match(%r{/tokens/*$})
124
+ "#{url}/tokens"
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,48 @@
1
+ require 'log4r'
2
+ require 'json'
3
+
4
+ require 'vagrant-openstack-illuin-provider/client/http_utils'
5
+ require 'vagrant-openstack-illuin-provider/client/domain'
6
+
7
+ module VagrantPlugins
8
+ module Openstack
9
+ class NeutronClient
10
+ include Singleton
11
+ include VagrantPlugins::Openstack::HttpUtils
12
+ include VagrantPlugins::Openstack::Domain
13
+
14
+ def initialize
15
+ @logger = Log4r::Logger.new('vagrant_openstack::neutron')
16
+ @session = VagrantPlugins::Openstack.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,303 @@
1
+ require 'log4r'
2
+ require 'json'
3
+
4
+ require 'vagrant-openstack-illuin-provider/client/http_utils'
5
+ require 'vagrant-openstack-illuin-provider/client/domain'
6
+
7
+ module VagrantPlugins
8
+ module Openstack
9
+ class NovaClient
10
+ include Singleton
11
+ include VagrantPlugins::Openstack::HttpUtils
12
+ include VagrantPlugins::Openstack::Domain
13
+
14
+ def initialize
15
+ @logger = Log4r::Logger.new('vagrant_openstack::nova')
16
+ @session = VagrantPlugins::Openstack.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, headers = {})
46
+ images_json = get(env, "#{@session.endpoints[:compute]}/images/detail", headers)
47
+ JSON.parse(images_json)['images'].map do |fl|
48
+ Image.new(
49
+ fl['id'],
50
+ fl['name'],
51
+ 'unknown',
52
+ nil,
53
+ fl['minRam'],
54
+ fl['minDisk'],
55
+ fl['metadata']
56
+ )
57
+ end
58
+ end
59
+
60
+ # Get detailed information about an image
61
+ #
62
+ # @param env [Hash] Vagrant action environment
63
+ # @param image_id [String] Image UUID
64
+ #
65
+ # @return [Hash]
66
+ def get_image_details(env, image_id)
67
+ image_json = get(env, "#{@session.endpoints[:compute]}/images/#{image_id}")
68
+ JSON.parse(image_json)['image']
69
+ end
70
+
71
+ def create_server(env, options)
72
+ server = {}.tap do |s|
73
+ s['name'] = options[:name]
74
+ if options[:image_ref].nil?
75
+ s['block_device_mapping'] = [{ volume_id: options[:volume_boot][:id],
76
+ device_name: options[:volume_boot][:device] }] if options[:volume_boot].key?(:id)
77
+ s['block_device_mapping_v2'] = [{ boot_index: '0',
78
+ volume_size: options[:volume_boot][:size],
79
+ uuid: options[:volume_boot][:image],
80
+ device_name: options[:volume_boot][:device],
81
+ source_type: 'image',
82
+ destination_type: 'volume',
83
+ delete_on_termination: options[:volume_boot][:delete_on_destroy] }]\
84
+ if options[:volume_boot].key?(:image)
85
+ else
86
+ s['imageRef'] = options[:image_ref]
87
+ end
88
+ s['flavorRef'] = options[:flavor_ref]
89
+ s['key_name'] = options[:keypair] unless options[:keypair].nil?
90
+ s['availability_zone'] = options[:availability_zone] unless options[:availability_zone].nil?
91
+ s['security_groups'] = options[:security_groups] unless options[:security_groups].nil?
92
+ s['user_data'] = Base64.encode64(options[:user_data]) unless options[:user_data].nil?
93
+ s['metadata'] = options[:metadata] unless options[:metadata].nil?
94
+ s['networks'] = options[:networks] unless options[:networks].nil? || options[:networks].empty?
95
+ end
96
+ object = { server: server }
97
+ object['os:scheduler_hints'] = options[:scheduler_hints] unless options[:scheduler_hints].nil?
98
+ server = post(env, "#{@session.endpoints[:compute]}/servers", object.to_json)
99
+ JSON.parse(server)['server']['id']
100
+ end
101
+
102
+ def delete_server(env, server_id)
103
+ instance_exists do
104
+ delete(env, "#{@session.endpoints[:compute]}/servers/#{server_id}")
105
+ end
106
+ end
107
+
108
+ def suspend_server(env, server_id)
109
+ instance_exists do
110
+ change_server_state(env, server_id, :suspend)
111
+ end
112
+ end
113
+
114
+ def resume_server(env, server_id)
115
+ instance_exists do
116
+ change_server_state(env, server_id, :resume)
117
+ end
118
+ end
119
+
120
+ def stop_server(env, server_id)
121
+ instance_exists do
122
+ change_server_state(env, server_id, :stop)
123
+ end
124
+ end
125
+
126
+ def start_server(env, server_id)
127
+ instance_exists do
128
+ change_server_state(env, server_id, :start)
129
+ end
130
+ end
131
+
132
+ def get_server_details(env, server_id)
133
+ instance_exists do
134
+ server_details = get(env, "#{@session.endpoints[:compute]}/servers/#{server_id}")
135
+ JSON.parse(server_details)['server']
136
+ end
137
+ end
138
+
139
+ def add_floating_ip(env, server_id, floating_ip)
140
+ instance_exists do
141
+ check_floating_ip(env, floating_ip)
142
+
143
+ post(env, "#{@session.endpoints[:compute]}/servers/#{server_id}/action",
144
+ { addFloatingIp: { address: floating_ip } }.to_json)
145
+ end
146
+ end
147
+
148
+ def check_assigned_floating_ip(env, server_id, floating_ip)
149
+ instance_exists do
150
+ addresses = get_server_details(env, server_id)['addresses']
151
+ addresses.each do |_, network|
152
+ network.each do |network_detail|
153
+ return true if network_detail['addr'] == floating_ip
154
+ end
155
+ end
156
+
157
+ return false
158
+ end
159
+ end
160
+
161
+ def import_keypair(env, public_key)
162
+ keyname = "vagrant-generated-#{Kernel.rand(36**8).to_s(36)}"
163
+
164
+ key_details = post(env, "#{@session.endpoints[:compute]}/os-keypairs",
165
+ { keypair:
166
+ {
167
+ name: keyname,
168
+ public_key: public_key
169
+ }
170
+ }.to_json)
171
+ JSON.parse(key_details)['keypair']['name']
172
+ end
173
+
174
+ def import_keypair_from_file(env, public_key_path)
175
+ fail "File specified in public_key_path #{public_key_path} doesn't exist" unless File.exist?(public_key_path)
176
+ file = File.open(public_key_path)
177
+ import_keypair(env, file.read)
178
+ end
179
+
180
+ def delete_keypair_if_vagrant(env, server_id)
181
+ instance_exists do
182
+ keyname = get_server_details(env, server_id)['key_name']
183
+ if keyname
184
+ delete(env, "#{@session.endpoints[:compute]}/os-keypairs/#{keyname}") if keyname.start_with?('vagrant-generated-')
185
+ end
186
+ end
187
+ end
188
+
189
+ def get_floating_ip_pools(env)
190
+ floating_ips = get(env, "#{@session.endpoints[:compute]}/os-floating-ip-pools")
191
+ JSON.parse(floating_ips)['floating_ip_pools']
192
+ end
193
+
194
+ def get_floating_ips(env)
195
+ floating_ips = get(env, "#{@session.endpoints[:compute]}/os-floating-ips")
196
+ JSON.parse(floating_ips)['floating_ips']
197
+ end
198
+
199
+ def attach_volume(env, server_id, volume_id, device = nil)
200
+ instance_exists do
201
+ attachment = post(env, "#{@session.endpoints[:compute]}/servers/#{server_id}/os-volume_attachments",
202
+ {
203
+ volumeAttachment: {
204
+ volumeId: volume_id,
205
+ device: device
206
+ }
207
+ }.to_json)
208
+ JSON.parse(attachment)['volumeAttachment']
209
+ end
210
+ end
211
+
212
+ # List snapshot images associated with a particular server
213
+ #
214
+ # @param env [Hash] Vagrant action environment
215
+ # @param server_id [String] Server UUID
216
+ #
217
+ # @return [Array<VagrantPlugins::Openstack::Domain::Image>]
218
+ def list_snapshots(env, server_id)
219
+ get_all_images(env, params: { server: server_id })
220
+ end
221
+
222
+ # Create a named snapsot for a given VM
223
+ #
224
+ # @param env [Hash] Vagrant action environment
225
+ # @param server_id [String] Server UUID
226
+ # @param snapshot_name [String]
227
+ #
228
+ # @return [void]
229
+ def create_snapshot(env, server_id, snapshot_name)
230
+ instance_exists do
231
+ post(
232
+ env,
233
+ "#{@session.endpoints[:compute]}/servers/#{server_id}/action",
234
+ { createImage: {
235
+ name: snapshot_name,
236
+ metadata: { vagrant_snapshot: 'true' }
237
+ } }.to_json)
238
+ end
239
+ end
240
+
241
+ # Delete an identified snapshot
242
+ #
243
+ # @param env [Hash] Vagrant action environment
244
+ # @param snapshot_id [String] Snapshot UUID
245
+ #
246
+ # @return [void]
247
+ def delete_snapshot(env, snapshot_id)
248
+ delete(
249
+ env,
250
+ "#{@session.endpoints[:compute]}/images/#{snapshot_id}")
251
+ end
252
+
253
+ # Restore a VM to an identified snapshot
254
+ #
255
+ # @param env [Hash] Vagrant action environment
256
+ # @param server_id [String] Server UUID
257
+ # @param snapshot_id [String] Snapshot UUID
258
+ #
259
+ # @return [void]
260
+ def restore_snapshot(env, server_id, snapshot_id)
261
+ instance_exists do
262
+ post(
263
+ env,
264
+ "#{@session.endpoints[:compute]}/servers/#{server_id}/action",
265
+ { rebuild: { imageRef: snapshot_id } }.to_json)
266
+ end
267
+ end
268
+
269
+ private
270
+
271
+ VM_STATES =
272
+ {
273
+ suspend: 'suspend',
274
+ resume: 'resume',
275
+ start: 'os-start',
276
+ stop: 'os-stop'
277
+ }
278
+
279
+ def instance_exists
280
+ return yield
281
+ rescue Errors::VagrantOpenstackError => e
282
+ raise Errors::InstanceNotFound if e.extra_data[:code] == 404
283
+ raise e
284
+ end
285
+
286
+ def change_server_state(env, server_id, new_state)
287
+ post(env, "#{@session.endpoints[:compute]}/servers/#{server_id}/action",
288
+ { :"#{VM_STATES[new_state.to_sym]}" => nil }.to_json)
289
+ end
290
+
291
+ def check_floating_ip(env, floating_ip)
292
+ ip_details = get(env, "#{@session.endpoints[:compute]}/os-floating-ips")
293
+
294
+ JSON.parse(ip_details)['floating_ips'].each do |ip|
295
+ next unless ip['ip'] == floating_ip
296
+ return if ip['instance_id'].nil?
297
+ fail Errors::FloatingIPAlreadyAssigned, floating_ip: floating_ip
298
+ end
299
+ fail Errors::FloatingIPNotAvailable, floating_ip: floating_ip
300
+ end
301
+ end
302
+ end
303
+ end