vagrant-openstack-provider 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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