vagrant-openstack-provider 0.1.2 → 0.2.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 (57) hide show
  1. checksums.yaml +13 -5
  2. data/.rubocop.yml +28 -0
  3. data/Appraisals +3 -3
  4. data/CHANGELOG.md +4 -0
  5. data/Gemfile +3 -0
  6. data/Rakefile +6 -2
  7. data/Vagrantfile +4 -15
  8. data/gemfiles/latest_stable.gemfile +2 -0
  9. data/gemfiles/oldest_current.gemfile +2 -0
  10. data/gemfiles/previous_release.gemfile +2 -0
  11. data/lib/vagrant-openstack-provider.rb +18 -13
  12. data/lib/vagrant-openstack-provider/action.rb +112 -46
  13. data/lib/vagrant-openstack-provider/action/connect_openstack.rb +9 -10
  14. data/lib/vagrant-openstack-provider/action/create_server.rb +86 -57
  15. data/lib/vagrant-openstack-provider/action/delete_server.rb +5 -6
  16. data/lib/vagrant-openstack-provider/action/{is_created.rb → message.rb} +4 -3
  17. data/lib/vagrant-openstack-provider/action/read_ssh_info.rb +7 -27
  18. data/lib/vagrant-openstack-provider/action/read_state.rb +7 -9
  19. data/lib/vagrant-openstack-provider/action/resume.rb +20 -0
  20. data/lib/vagrant-openstack-provider/action/start_server.rb +22 -0
  21. data/lib/vagrant-openstack-provider/action/stop_server.rb +22 -0
  22. data/lib/vagrant-openstack-provider/action/suspend.rb +20 -0
  23. data/lib/vagrant-openstack-provider/action/sync_folders.rb +27 -38
  24. data/lib/vagrant-openstack-provider/action/wait_stop.rb +29 -0
  25. data/lib/vagrant-openstack-provider/client/keystone.rb +76 -0
  26. data/lib/vagrant-openstack-provider/client/neutron.rb +32 -0
  27. data/lib/vagrant-openstack-provider/client/nova.rb +166 -0
  28. data/lib/vagrant-openstack-provider/client/openstack.rb +41 -0
  29. data/lib/vagrant-openstack-provider/client/utils.rb +38 -0
  30. data/lib/vagrant-openstack-provider/config.rb +38 -110
  31. data/lib/vagrant-openstack-provider/errors.rb +7 -3
  32. data/lib/vagrant-openstack-provider/plugin.rb +8 -8
  33. data/lib/vagrant-openstack-provider/provider.rb +6 -6
  34. data/lib/vagrant-openstack-provider/version.rb +1 -1
  35. data/locales/en.yml +83 -5
  36. data/numergyrc +22 -0
  37. data/spec/vagrant-openstack-provider/action/create_server_spec.rb +89 -0
  38. data/spec/vagrant-openstack-provider/client/keystone_spec.rb +140 -0
  39. data/spec/vagrant-openstack-provider/client/neutron_spec.rb +53 -0
  40. data/spec/vagrant-openstack-provider/client/nova_spec.rb +373 -0
  41. data/spec/vagrant-openstack-provider/client/utils_spec.rb +125 -0
  42. data/spec/vagrant-openstack-provider/config_spec.rb +117 -0
  43. data/spec/vagrant-openstack-provider/provider_spec.rb +13 -0
  44. data/spec/vagrant-openstack-provider/spec_helper.rb +23 -0
  45. data/vagrant-openstack-provider.gemspec +13 -14
  46. metadata +40 -30
  47. data/features/provision.feature +0 -35
  48. data/features/steps/sdk_steps.rb +0 -13
  49. data/features/steps/server_steps.rb +0 -25
  50. data/features/support/env.rb +0 -37
  51. data/features/support/fog_mock.rb +0 -19
  52. data/features/vagrant-openstack-provider.feature +0 -70
  53. data/lib/vagrant-openstack-provider/action/message_already_created.rb +0 -16
  54. data/lib/vagrant-openstack-provider/action/message_not_created.rb +0 -16
  55. data/lib/vagrant-openstack-provider/openstack_client.rb +0 -98
  56. data/spec/vagrant-openstack/config_spec.rb +0 -184
  57. data/stackrc +0 -31
@@ -0,0 +1,20 @@
1
+ module VagrantPlugins
2
+ module Openstack
3
+ module Action
4
+ class Suspend
5
+ def initialize(app, _env)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ if env[:machine].id
11
+ env[:ui].info I18n.t('vagrant.actions.vm.suspend.suspending')
12
+ env[:openstack_client].nova.suspend_server(env, env[:machine].id)
13
+ end
14
+
15
+ @app.call(env)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,46 +1,44 @@
1
- require "log4r"
1
+ require 'log4r'
2
2
  require 'rbconfig'
3
- require "vagrant/util/subprocess"
3
+ require 'vagrant/util/subprocess'
4
4
 
5
5
  module VagrantPlugins
6
6
  module Openstack
7
7
  module Action
8
-
9
8
  class SyncFolders
10
- def initialize(app, env)
9
+ def initialize(app, _env)
11
10
  @app = app
12
11
  end
13
12
 
14
13
  def call(env)
15
14
  sync_method = env[:machine].provider_config.sync_method
16
- if sync_method == "rsync"
15
+ if sync_method == 'rsync'
17
16
  RsyncFolders.new(@app, env).call(env)
18
- elsif sync_method == "none"
17
+ elsif sync_method == 'none'
19
18
  NoSyncFolders.new(@app, env).call(env)
20
19
  else
21
- raise Errors::SyncMethodError, :sync_method_value => sync_method
20
+ fail Errors::SyncMethodError, sync_method_value: sync_method
22
21
  end
23
22
  end
24
-
25
23
  end
26
24
 
27
25
  class NoSyncFolders
28
- def initialize(app, env)
26
+ def initialize(app, _env)
29
27
  @app = app
30
28
  end
31
29
 
32
30
  def call(env)
33
31
  @app.call(env)
34
- env[:ui].info("Sync folders are disabled")
32
+ env[:ui].info('Sync folders are disabled')
35
33
  end
36
34
  end
37
35
 
38
36
  # This middleware uses `rsync` to sync the folders over to the
39
37
  # remote instance.
40
38
  class RsyncFolders
41
- def initialize(app, env)
39
+ def initialize(app, _env)
42
40
  @app = app
43
- @logger = Log4r::Logger.new("vagrant_openstack::action::sync_folders")
41
+ @logger = Log4r::Logger.new('vagrant_openstack::action::sync_folders')
44
42
  @host_os = RbConfig::CONFIG['host_os']
45
43
  end
46
44
 
@@ -52,22 +50,20 @@ module VagrantPlugins
52
50
  config = env[:machine].provider_config
53
51
  rsync_includes = config.rsync_includes.to_a
54
52
 
55
- env[:machine].config.vm.synced_folders.each do |id, data|
53
+ env[:machine].config.vm.synced_folders.each do |_, data|
56
54
  hostpath = File.expand_path(data[:hostpath], env[:root_path])
57
55
  guestpath = data[:guestpath]
58
56
 
59
57
  # Make sure there is a trailing slash on the host path to
60
58
  # avoid creating an additional directory with rsync
61
59
  hostpath = "#{hostpath}/" if hostpath !~ /\/$/
62
-
60
+
63
61
  # If on Windows, modify the path to work with cygwin rsync
64
62
  if @host_os =~ /mswin|mingw|cygwin/
65
- hostpath = hostpath.sub(/^([A-Za-z]):\//, "/cygdrive/#{$1.downcase}/")
63
+ hostpath = hostpath.sub(/^([A-Za-z]):\//, "/cygdrive/#{Regexp.last_match[1].downcase}/")
66
64
  end
67
65
 
68
- env[:ui].info(I18n.t("vagrant_openstack.rsync_folder",
69
- :hostpath => hostpath,
70
- :guestpath => guestpath))
66
+ env[:ui].info(I18n.t('vagrant_openstack.rsync_folder', hostpath: hostpath, guestpath: guestpath))
71
67
 
72
68
  # Create the guest path
73
69
  env[:machine].communicate.sudo("mkdir -p '#{guestpath}'")
@@ -75,46 +71,39 @@ module VagrantPlugins
75
71
  "chown -R #{ssh_info[:username]} '#{guestpath}'")
76
72
 
77
73
  # Generate rsync include commands
78
- includes = rsync_includes.each_with_object([]) { |incl, incls|
79
- incls << "--include"
74
+ includes = rsync_includes.each_with_object([]) do |incl, incls|
75
+ incls << '--include'
80
76
  incls << incl
81
- }
77
+ end
82
78
 
83
79
  # Rsync over to the guest path using the SSH info. add
84
80
  # .hg/ to exclude list as that isn't covered in
85
81
  # --cvs-exclude
86
82
  command = [
87
- "rsync", "--verbose", "--archive", "-z",
88
- "--cvs-exclude",
89
- "--exclude", ".hg/",
83
+ 'rsync', '--verbose', '--archive', '-z',
84
+ '--cvs-exclude',
85
+ '--exclude', '.hg/',
90
86
  *includes,
91
- "-e", "ssh -p #{ssh_info[:port]} -o StrictHostKeyChecking=no #{ssh_key_options(ssh_info)}",
87
+ '-e', "ssh -p #{ssh_info[:port]} -o StrictHostKeyChecking=no #{ssh_key_options(ssh_info)}",
92
88
  hostpath,
93
89
  "#{ssh_info[:username]}@#{ssh_info[:host]}:#{guestpath}"]
94
90
  command.compact!
95
91
 
96
92
  # during rsync, ignore files specified in .hgignore and
97
93
  # .gitignore traditional .gitignore or .hgignore files
98
- ignore_files = [".hgignore", ".gitignore"]
94
+ ignore_files = ['.hgignore', '.gitignore']
99
95
  ignore_files.each do |ignore_file|
100
- abs_ignore_file = env[:root_path].to_s + "/" + ignore_file
101
- if File.exist?(abs_ignore_file)
102
- command = command + ["--exclude-from", abs_ignore_file]
103
- end
96
+ abs_ignore_file = env[:root_path].to_s + '/' + ignore_file
97
+ command += ['--exclude-from', abs_ignore_file] if File.exist?(abs_ignore_file)
104
98
  end
105
99
 
106
- r = Vagrant::Util::Subprocess.execute(*command)
107
- if r.exit_code != 0
108
- raise Errors::RsyncError,
109
- :guestpath => guestpath,
110
- :hostpath => hostpath,
111
- :stderr => r.stderr
112
- end
100
+ next if Vagrant::Util::Subprocess.execute(*command).exit_code == 0
101
+ ail Errors::RsyncError, guestpath: guestpath, hostpath: hostpath, stderr: r.stderr
113
102
  end
114
103
  end
115
104
 
116
105
  private
117
-
106
+
118
107
  def ssh_key_options(ssh_info)
119
108
  # Ensure that `private_key_path` is an Array (for Vagrant < 1.4)
120
109
  Array(ssh_info[:private_key_path]).map { |path| "-i '#{path}' " }.join
@@ -0,0 +1,29 @@
1
+ require 'log4r'
2
+ require 'timeout'
3
+
4
+ module VagrantPlugins
5
+ module Openstack
6
+ module Action
7
+ class WaitForServerToStop
8
+ def initialize(app, _env)
9
+ @app = app
10
+ @logger = Log4r::Logger.new('vagrant_openstack::action::stop_server')
11
+ end
12
+
13
+ def call(env)
14
+ if env[:machine].id
15
+ env[:ui].info(I18n.t('vagrant_openstack.waiting_stop'))
16
+ client = env[:openstack_client].nova
17
+ timeout(200) do
18
+ while client.get_server_details(env, env[:machine].id)['status'] != 'SHUTOFF'
19
+ sleep 3
20
+ @logger.debug('Waiting for server to stop')
21
+ end
22
+ end
23
+ end
24
+ @app.call(env)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,76 @@
1
+ require 'log4r'
2
+ require 'restclient'
3
+ require 'json'
4
+
5
+ module VagrantPlugins
6
+ module Openstack
7
+ class KeystoneClient
8
+ include Singleton
9
+
10
+ def initialize
11
+ @logger = Log4r::Logger.new('vagrant_openstack::keystone')
12
+ @session = VagrantPlugins::Openstack.session
13
+ end
14
+
15
+ def authenticate(env)
16
+ @logger.debug('Authenticating on Keystone')
17
+ config = env[:machine].provider_config
18
+ env[:ui].info(I18n.t('vagrant_openstack.client.authentication', project: config.tenant_name, user: config.username))
19
+
20
+ authentication = RestClient.post(
21
+ config.openstack_auth_url,
22
+ {
23
+ auth:
24
+ {
25
+ tenantName: config.tenant_name,
26
+ passwordCredentials:
27
+ {
28
+ username: config.username,
29
+ password: config.password
30
+ }
31
+ }
32
+ }.to_json,
33
+ content_type: :json,
34
+ accept: :json)
35
+
36
+ access = JSON.parse(authentication)['access']
37
+
38
+ read_endpoint_catalog(env, access['serviceCatalog'])
39
+ override_endpoint_catalog_with_user_config(env)
40
+ print_endpoint_catalog(env)
41
+
42
+ response_token = access['token']
43
+ @session.token = response_token['id']
44
+ @session.project_id = response_token['tenant']['id']
45
+ end
46
+
47
+ private
48
+
49
+ def read_endpoint_catalog(env, catalog)
50
+ env[:ui].info(I18n.t('vagrant_openstack.client.looking_for_available_endpoints'))
51
+
52
+ catalog.each do |service|
53
+ se = service['endpoints']
54
+ if se.size > 1
55
+ env[:ui].warn I18n.t('vagrant_openstack.client.multiple_endpoint', size: se.size, type: service['type'])
56
+ env[:ui].warn " => #{service['endpoints'][0]['publicURL']}"
57
+ end
58
+ url = se[0]['publicURL'].strip
59
+ @session.endpoints[service['type'].to_sym] = url unless url.empty?
60
+ end
61
+ end
62
+
63
+ def override_endpoint_catalog_with_user_config(env)
64
+ config = env[:machine].provider_config
65
+ @session.endpoints[:compute] = config.openstack_compute_url unless config.openstack_compute_url.nil?
66
+ @session.endpoints[:network] = config.openstack_network_url unless config.openstack_network_url.nil?
67
+ end
68
+
69
+ def print_endpoint_catalog(env)
70
+ @session.endpoints.each do |key, value|
71
+ env[:ui].info(" -- #{key.to_s.ljust 15}: #{value}")
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,32 @@
1
+ require 'log4r'
2
+ require 'restclient'
3
+ require 'json'
4
+
5
+ require 'vagrant-openstack-provider/client/utils'
6
+
7
+ module VagrantPlugins
8
+ module Openstack
9
+ class NeutronClient
10
+ include Singleton
11
+ include VagrantPlugins::Openstack::Utils
12
+
13
+ def initialize
14
+ @logger = Log4r::Logger.new('vagrant_openstack::neutron')
15
+ @session = VagrantPlugins::Openstack.session
16
+ end
17
+
18
+ def get_private_networks(env)
19
+ authenticated(env) do
20
+ networks_json = RestClient.get("#{@session.endpoints[:network]}/networks",
21
+ 'X-Auth-Token' => @session.token,
22
+ :accept => :json) { |res| handle_response(res) }
23
+ networks = []
24
+ JSON.parse(networks_json)['networks'].each do |n|
25
+ networks << { id: n['id'], name: n['name'] } if n['tenant_id'].eql? @session.project_id
26
+ end
27
+ networks
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,166 @@
1
+ require 'log4r'
2
+ require 'restclient'
3
+ require 'json'
4
+
5
+ require 'vagrant-openstack-provider/client/utils'
6
+
7
+ module VagrantPlugins
8
+ module Openstack
9
+ class NovaClient
10
+ include Singleton
11
+ include VagrantPlugins::Openstack::Utils
12
+
13
+ def initialize
14
+ @logger = Log4r::Logger.new('vagrant_openstack::nova')
15
+ @session = VagrantPlugins::Openstack.session
16
+ end
17
+
18
+ def get_all_flavors(env)
19
+ authenticated(env) do
20
+ flavors_json = RestClient.get("#{@session.endpoints[:compute]}/flavors",
21
+ 'X-Auth-Token' => @session.token,
22
+ :accept => :json) { |res| handle_response(res) }
23
+
24
+ return JSON.parse(flavors_json)['flavors'].map { |fl| Item.new(fl['id'], fl['name']) }
25
+ end
26
+ end
27
+
28
+ def get_all_images(env)
29
+ authenticated(env) do
30
+ images_json = RestClient.get(
31
+ "#{@session.endpoints[:compute]}/images",
32
+ 'X-Auth-Token' => @session.token, :accept => :json) { |res| handle_response(res) }
33
+
34
+ JSON.parse(images_json)['images'].map { |im| Item.new(im['id'], im['name']) }
35
+ end
36
+ end
37
+
38
+ def create_server(env, name, image_ref, flavor_ref, networks, keypair)
39
+ authenticated(env) do
40
+ server = {}.tap do |s|
41
+ s['name'] = name
42
+ s['imageRef'] = image_ref
43
+ s['flavorRef'] = flavor_ref
44
+ s['key_name'] = keypair
45
+ unless networks.nil? || networks.empty?
46
+ s['networks'] = []
47
+ networks.each do |uuid|
48
+ s['networks'] << { uuid: uuid }
49
+ end
50
+ end
51
+ end
52
+
53
+ server = RestClient.post(
54
+ "#{@session.endpoints[:compute]}/servers", { server: server }.to_json,
55
+ 'X-Auth-Token' => @session.token,
56
+ :accept => :json,
57
+ :content_type => :json) { |res| handle_response(res) }
58
+
59
+ JSON.parse(server)['server']['id']
60
+ end
61
+ end
62
+
63
+ def delete_server(env, server_id)
64
+ authenticated(env) do
65
+ RestClient.delete(
66
+ "#{@session.endpoints[:compute]}/servers/#{server_id}",
67
+ 'X-Auth-Token' => @session.token,
68
+ :accept => :json) { |res| handle_response(res) }
69
+ end
70
+ end
71
+
72
+ def suspend_server(env, server_id)
73
+ authenticated(env) do
74
+ RestClient.post(
75
+ "#{@session.endpoints[:compute]}/servers/#{server_id}/action", '{ "suspend": null }',
76
+ 'X-Auth-Token' => @session.token,
77
+ :accept => :json,
78
+ :content_type => :json) { |res| handle_response(res) }
79
+ end
80
+ end
81
+
82
+ def resume_server(env, server_id)
83
+ # TODO(julienvey) check status before (if pause->unpause, if suspend->resume...)
84
+ authenticated(env) do
85
+ RestClient.post(
86
+ "#{@session.endpoints[:compute]}/servers/#{server_id}/action", '{ "resume": null }',
87
+ 'X-Auth-Token' => @session.token,
88
+ :accept => :json,
89
+ :content_type => :json) { |res| handle_response(res) }
90
+ end
91
+ end
92
+
93
+ def stop_server(env, server_id)
94
+ authenticated(env) do
95
+ RestClient.post(
96
+ "#{@session.endpoints[:compute]}/servers/#{server_id}/action", '{ "os-stop": null }',
97
+ 'X-Auth-Token' => @session.token,
98
+ :accept => :json,
99
+ :content_type => :json) { |res| handle_response(res) }
100
+ end
101
+ end
102
+
103
+ def start_server(env, server_id)
104
+ authenticated(env) do
105
+ RestClient.post(
106
+ "#{@session.endpoints[:compute]}/servers/#{server_id}/action", '{ "os-start": null }',
107
+ 'X-Auth-Token' => @session.token,
108
+ :accept => :json,
109
+ :content_type => :json) { |res| handle_response(res) }
110
+ end
111
+ end
112
+
113
+ def get_server_details(env, server_id)
114
+ authenticated(env) do
115
+ server_details = RestClient.get(
116
+ "#{@session.endpoints[:compute]}/servers/#{server_id}",
117
+ 'X-Auth-Token' => @session.token,
118
+ :accept => :json) { |res| handle_response(res) }
119
+
120
+ return JSON.parse(server_details)['server']
121
+ end
122
+ end
123
+
124
+ def add_floating_ip(env, server_id, floating_ip)
125
+ authenticated(env) do
126
+ check_floating_ip(env, floating_ip)
127
+ RestClient.post(
128
+ "#{@session.endpoints[:compute]}/servers/#{server_id}/action",
129
+ {
130
+ addFloatingIp:
131
+ {
132
+ address: floating_ip
133
+ }
134
+ }.to_json,
135
+ 'X-Auth-Token' => @session.token,
136
+ :accept => :json,
137
+ :content_type => :json) { |res| handle_response(res) }
138
+ end
139
+ end
140
+
141
+ private
142
+
143
+ def check_floating_ip(_env, floating_ip)
144
+ ip_details = RestClient.get(
145
+ "#{@session.endpoints[:compute]}/os-floating-ips",
146
+ 'X-Auth-Token' => @session.token,
147
+ :accept => :json) { |res| handle_response(res) }
148
+
149
+ JSON.parse(ip_details)['floating_ips'].each do |ip|
150
+ next unless ip['ip'] == floating_ip
151
+ return if ip['instance_id'].nil?
152
+ fail "Floating IP #{floating_ip} already assigned to another server"
153
+ end
154
+ fail "Floating IP #{floating_ip} not available for this tenant"
155
+ end
156
+ end
157
+
158
+ class Item
159
+ attr_accessor :id, :name
160
+ def initialize(id, name)
161
+ @id = id
162
+ @name = name
163
+ end
164
+ end
165
+ end
166
+ end