vagrant-openstack-illuin-provider 0.12.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 (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