openstack-vagrant 1.1.2

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.
@@ -0,0 +1,53 @@
1
+ require "log4r"
2
+ require "json"
3
+
4
+ class ::Hash
5
+ def method_missing(name)
6
+ return self[name] if key? name
7
+ self.each { |k,v| return v if k.to_s.to_sym == name }
8
+ super.method_missing name
9
+ end
10
+ end
11
+
12
+ module VagrantPlugins
13
+ module OpenStack
14
+ module Action
15
+ # This action reads the SSH info for the machine and puts it into the
16
+ # `:machine_ssh_info` key in the environment.
17
+ class ReadSSHInfoFromCache
18
+ def initialize(app, env)
19
+ @app = app
20
+ @logger = Log4r::Logger.new("vagrant_openstack::action::read_ssh_info")
21
+ end
22
+
23
+ def call(env)
24
+ ssh_info = read_ssh_info(env[:machine])
25
+
26
+ if ssh_info and ssh_info[:host] != nil
27
+ env[:machine_ssh_info] = ssh_info
28
+ end
29
+ @app.call(env)
30
+ end
31
+
32
+ def read_ssh_info(machine)
33
+ return nil if machine.id.nil?
34
+
35
+ cached_metadata_file = machine.data_dir.join("cached_metadata")
36
+
37
+ @logger.info("Loading cached metadata from #{cached_metadata_file}")
38
+ server = JSON.load(cached_metadata_file.read) rescue nil
39
+
40
+ config = machine.provider_config
41
+
42
+ host = server.addresses[config.public_network_name].last['addr'] rescue nil
43
+
44
+ return {
45
+ :host => host,
46
+ :port => 22,
47
+ :username => config.ssh_username
48
+ }
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,38 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module OpenStack
5
+ module Action
6
+ # This action reads the state of the machine and puts it in the
7
+ # `:machine_state_id` key in the environment.
8
+ class ReadState
9
+ def initialize(app, env)
10
+ @app = app
11
+ @logger = Log4r::Logger.new("vagrant_openstack::action::read_state")
12
+ end
13
+
14
+ def call(env)
15
+ env[:machine_state_id] = read_state(env[:openstack_compute], env[:machine])
16
+
17
+ @app.call(env)
18
+ end
19
+
20
+ def read_state(openstack, machine)
21
+ return :not_created if machine.id.nil?
22
+
23
+ # Find the machine
24
+ server = openstack.servers.get(machine.id)
25
+ if server.nil? || server.state == "DELETED"
26
+ # The machine can't be found
27
+ @logger.info("Machine not found or deleted, assuming it got destroyed.")
28
+ machine.id = nil
29
+ return :not_created
30
+ end
31
+
32
+ # Return the state
33
+ return server.state.downcase.to_sym
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,58 @@
1
+ require "log4r"
2
+
3
+ require "vagrant/util/subprocess"
4
+
5
+ module VagrantPlugins
6
+ module OpenStack
7
+ module Action
8
+ # This middleware uses `rsync` to sync the folders over to the
9
+ # remote instance.
10
+ class SyncFolders
11
+ def initialize(app, env)
12
+ @app = app
13
+ @logger = Log4r::Logger.new("vagrant_openstack::action::sync_folders")
14
+ end
15
+
16
+ def call(env)
17
+ @app.call(env)
18
+
19
+ ssh_info = env[:machine].ssh_info
20
+
21
+ env[:machine].config.vm.synced_folders.each do |id, data|
22
+ hostpath = File.expand_path(data[:hostpath], env[:root_path])
23
+ guestpath = data[:guestpath]
24
+ next if data[:disabled]
25
+
26
+ # Make sure there is a trailing slash on the host path to
27
+ # avoid creating an additional directory with rsync
28
+ hostpath = "#{hostpath}/" if hostpath !~ /\/$/
29
+
30
+ env[:ui].info(I18n.t("vagrant_openstack.rsync_folder",
31
+ :hostpath => hostpath,
32
+ :guestpath => guestpath))
33
+
34
+ # Create the guest path
35
+ env[:machine].communicate.sudo("mkdir -p '#{guestpath}'")
36
+ env[:machine].communicate.sudo(
37
+ "chown #{ssh_info[:username]} '#{guestpath}'")
38
+
39
+ # Rsync over to the guest path using the SSH info
40
+ command = [
41
+ "rsync", "--verbose", "--archive", "--compress", "--delete",
42
+ "-e", "ssh -p #{ssh_info[:port]} -i '#{ssh_info[:private_key_path][0]}' -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null",
43
+ hostpath,
44
+ "#{ssh_info[:username]}@#{ssh_info[:host]}:#{guestpath}"]
45
+
46
+ r = Vagrant::Util::Subprocess.execute(*command)
47
+ if r.exit_code != 0
48
+ raise Errors::RsyncError,
49
+ :guestpath => guestpath,
50
+ :hostpath => hostpath,
51
+ :stderr => r.stderr
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,129 @@
1
+ require "pathname"
2
+
3
+ require "vagrant/action/builder"
4
+
5
+ module VagrantPlugins
6
+ module OpenStack
7
+ module Action
8
+ # Include the built-in modules so we can use them as top-level things.
9
+ include Vagrant::Action::Builtin
10
+
11
+ # This action is called to destroy the remote machine.
12
+ def self.action_destroy
13
+ Vagrant::Action::Builder.new.tap do |b|
14
+ b.use ConfigValidate
15
+ b.use Call, IsCreated do |env, b2|
16
+ if !env[:result]
17
+ b2.use MessageNotCreated
18
+ next
19
+ end
20
+
21
+ b2.use ConnectOpenStack
22
+ b2.use DeleteServer
23
+ end
24
+ end
25
+ end
26
+
27
+ # This action is called to read the SSH info of the machine. The
28
+ # resulting state is expected to be put into the `:machine_ssh_info`
29
+ # key.
30
+ def self.action_read_ssh_info
31
+ Vagrant::Action::Builder.new.tap do |b|
32
+ b.use ConfigValidate
33
+ b.use Call, ReadSSHInfoFromCache do |env, b2|
34
+ if !env[:machine_ssh_info]
35
+ b2.use ConnectOpenStack
36
+ b2.use ReadSSHInfoFromAPI
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ # This action is called to read the state of the machine. The
43
+ # resulting state is expected to be put into the `:machine_state_id`
44
+ # key.
45
+ def self.action_read_state
46
+ Vagrant::Action::Builder.new.tap do |b|
47
+ b.use ConfigValidate
48
+ b.use ConnectOpenStack
49
+ b.use ReadState
50
+ end
51
+ end
52
+
53
+ def self.action_ssh
54
+ Vagrant::Action::Builder.new.tap do |b|
55
+ b.use ConfigValidate
56
+ b.use Call, IsCreated do |env, b2|
57
+ if !env[:result]
58
+ b2.use MessageNotCreated
59
+ next
60
+ end
61
+
62
+ b2.use SSHExec
63
+ end
64
+ end
65
+ end
66
+
67
+ def self.action_ssh_run
68
+ Vagrant::Action::Builder.new.tap do |b|
69
+ b.use ConfigValidate
70
+ b.use Call, IsCreated do |env, b2|
71
+ if !env[:result]
72
+ b2.use MessageNotCreated
73
+ next
74
+ end
75
+
76
+ b2.use SSHRun
77
+ end
78
+ end
79
+ end
80
+
81
+ def self.action_up
82
+ Vagrant::Action::Builder.new.tap do |b|
83
+ b.use defined?(HandleBox) ? HandleBox : HandleBoxUrl
84
+ b.use ConfigValidate
85
+ b.use Call, IsCreated do |env, b2|
86
+ if env[:result]
87
+ b2.use MessageAlreadyCreated
88
+ next
89
+ end
90
+
91
+ b2.use ConnectOpenStack
92
+ b2.use Provision
93
+ b2.use SyncFolders
94
+ b2.use SetHostname
95
+ b2.use CreateServer
96
+ end
97
+ end
98
+ end
99
+
100
+ def self.action_provision
101
+ Vagrant::Action::Builder.new.tap do |b|
102
+ b.use ConfigValidate
103
+ b.use Call, IsCreated do |env, b2|
104
+ if !env[:result]
105
+ b2.use MessageNotCreated
106
+ next
107
+ end
108
+
109
+ b2.use Provision
110
+ b2.use SyncFolders
111
+ end
112
+ end
113
+ end
114
+
115
+ # The autoload farm
116
+ action_root = Pathname.new(File.expand_path("../action", __FILE__))
117
+ autoload :ConnectOpenStack, action_root.join("connect_openstack")
118
+ autoload :CreateServer, action_root.join("create_server")
119
+ autoload :DeleteServer, action_root.join("delete_server")
120
+ autoload :IsCreated, action_root.join("is_created")
121
+ autoload :MessageAlreadyCreated, action_root.join("message_already_created")
122
+ autoload :MessageNotCreated, action_root.join("message_not_created")
123
+ autoload :ReadSSHInfoFromAPI, action_root.join("read_ssh_info_from_api")
124
+ autoload :ReadSSHInfoFromCache, action_root.join("read_ssh_info_from_cache")
125
+ autoload :ReadState, action_root.join("read_state")
126
+ autoload :SyncFolders, action_root.join("sync_folders")
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,126 @@
1
+ require "vagrant"
2
+
3
+ module VagrantPlugins
4
+ module OpenStack
5
+ class Config < Vagrant.plugin("2", :config)
6
+ # The API key to access OpenStack.
7
+ #
8
+ # @return [String]
9
+ attr_accessor :api_key
10
+
11
+ # The endpoint to access OpenStack.
12
+ #
13
+ # @return [String]
14
+ attr_accessor :endpoint
15
+
16
+ # Openstack region, if your openstack instance uses these.
17
+ # Rackspace typically uses these. You need to provide their three letter acronym (for example: DFW)
18
+ # @return [String]
19
+ attr_accessor :region
20
+
21
+ # The flavor of server to launch, either the ID or name. This
22
+ # can also be a regular expression to partially match a name.
23
+ attr_accessor :flavor
24
+
25
+ # The name or ID of the image to use. This can also be a regular
26
+ # expression to partially match a name.
27
+ attr_accessor :image
28
+
29
+ # The name of the server. This defaults to the name of the machine
30
+ # defined by Vagrant (via `config.vm.define`), but can be overriden
31
+ # here.
32
+ attr_accessor :server_name
33
+
34
+ # The username to access OpenStack.
35
+ #
36
+ # @return [String]
37
+ attr_accessor :username
38
+
39
+ # The name of the keypair to use.
40
+ #
41
+ # @return [String]
42
+ attr_accessor :keypair_name
43
+
44
+ # The SSH username to use with this OpenStack instance. This overrides
45
+ # the `config.ssh.username` variable.
46
+ #
47
+ # @return [String]
48
+ attr_accessor :ssh_username
49
+
50
+ # User data to be sent to the newly created OpenStack instance. Use this
51
+ # e.g. to inject a script at boot time.
52
+ #
53
+ # @return [String]
54
+ attr_accessor :user_data
55
+
56
+ # Metadata to be sent to the newly created OpenStack instance.
57
+ #
58
+ # @return [Hash]
59
+ attr_accessor :metadata
60
+
61
+ # @return [String]
62
+ attr_accessor :public_network_name
63
+
64
+ # @return [String]
65
+ attr_accessor :networks
66
+
67
+ # @return [String]
68
+ attr_accessor :tenant
69
+
70
+ # @return [Hash]
71
+ attr_accessor :scheduler_hints
72
+
73
+ def initialize
74
+ @api_key = UNSET_VALUE
75
+ @endpoint = UNSET_VALUE
76
+ @region = UNSET_VALUE
77
+ @flavor = UNSET_VALUE
78
+ @image = UNSET_VALUE
79
+ @server_name = UNSET_VALUE
80
+ @username = UNSET_VALUE
81
+ @keypair_name = UNSET_VALUE
82
+ @ssh_username = UNSET_VALUE
83
+ @user_data = UNSET_VALUE
84
+ @metadata = UNSET_VALUE
85
+ @public_network_name = UNSET_VALUE
86
+ @networks = UNSET_VALUE
87
+ @tenant = UNSET_VALUE
88
+ @scheduler_hints = UNSET_VALUE
89
+ end
90
+
91
+ def finalize!
92
+ @api_key = nil if @api_key == UNSET_VALUE
93
+ @endpoint = nil if @endpoint == UNSET_VALUE
94
+ @region = nil if @region == UNSET_VALUE
95
+ @flavor = /m1.tiny/ if @flavor == UNSET_VALUE
96
+ @image = /cirros/ if @image == UNSET_VALUE
97
+ @server_name = nil if @server_name == UNSET_VALUE
98
+ @username = nil if @username == UNSET_VALUE
99
+
100
+ # Keypair defaults to nil
101
+ @keypair_name = nil if @keypair_name == UNSET_VALUE
102
+
103
+ # The SSH values by default are nil, and the top-level config
104
+ # `config.ssh` values are used.
105
+ @ssh_username = nil if @ssh_username == UNSET_VALUE
106
+
107
+ @user_data = "" if @user_data == UNSET_VALUE
108
+ @metadata = {} if @metadata == UNSET_VALUE
109
+
110
+ @public_network_name = "public" if @public_network_name == UNSET_VALUE
111
+ @networks = [@public_network_name] if @networks == UNSET_VALUE
112
+ @tenant = nil if @tenant == UNSET_VALUE
113
+ @scheduler_hints = {} if @scheduler_hints == UNSET_VALUE
114
+ end
115
+
116
+ def validate(machine)
117
+ errors = []
118
+
119
+ errors << I18n.t("vagrant_openstack.config.api_key_required") if !@api_key
120
+ errors << I18n.t("vagrant_openstack.config.username_required") if !@username
121
+
122
+ { "OpenStack Provider" => errors }
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,35 @@
1
+ require "vagrant"
2
+
3
+ module VagrantPlugins
4
+ module OpenStack
5
+ module Errors
6
+ class VagrantOpenStackError < Vagrant::Errors::VagrantError
7
+ error_namespace("vagrant_openstack.errors")
8
+ end
9
+
10
+ class CreateBadState < VagrantOpenStackError
11
+ error_key(:create_bad_state)
12
+ end
13
+
14
+ class SshUnavailable < VagrantOpenStackError
15
+ error_key(:ssh_unavailable)
16
+ end
17
+
18
+ class NoMatchingFlavor < VagrantOpenStackError
19
+ error_key(:no_matching_flavor)
20
+ end
21
+
22
+ class NoMatchingImage < VagrantOpenStackError
23
+ error_key(:no_matching_image)
24
+ end
25
+
26
+ class NoMatchingNetwork < VagrantOpenStackError
27
+ error_key(:no_matching_network)
28
+ end
29
+
30
+ class RsyncError < VagrantOpenStackError
31
+ error_key(:rsync_error)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,37 @@
1
+ begin
2
+ require "vagrant"
3
+ rescue LoadError
4
+ raise "The OpenStack Cloud provider must be run within Vagrant."
5
+ end
6
+
7
+ # This is a sanity check to make sure no one is attempting to install
8
+ # this into an early Vagrant version.
9
+ if Vagrant::VERSION < "1.2.0"
10
+ raise "OpenStack Cloud provider is only compatible with Vagrant 1.2+"
11
+ end
12
+
13
+ module VagrantPlugins
14
+ module OpenStack
15
+ class Plugin < Vagrant.plugin("2")
16
+ name "OpenStack Cloud"
17
+ description <<-DESC
18
+ This plugin enables Vagrant to manage machines in OpenStack Cloud.
19
+ DESC
20
+
21
+ config(:openstack, :provider) do
22
+ require_relative "config"
23
+ Config
24
+ end
25
+
26
+ provider(:openstack, parallel: true) do
27
+ # Setup some things
28
+ OpenStack.init_i18n
29
+ OpenStack.init_logging
30
+
31
+ # Load the actual provider
32
+ require_relative "provider"
33
+ Provider
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,50 @@
1
+ require "vagrant"
2
+
3
+ require "vagrant-openstack/action"
4
+
5
+ module VagrantPlugins
6
+ module OpenStack
7
+ class Provider < Vagrant.plugin("2", :provider)
8
+ def initialize(machine)
9
+ @machine = machine
10
+ end
11
+
12
+ def action(name)
13
+ # Attempt to get the action method from the Action class if it
14
+ # exists, otherwise return nil to show that we don't support the
15
+ # given action.
16
+ action_method = "action_#{name}"
17
+ return Action.send(action_method) if Action.respond_to?(action_method)
18
+ nil
19
+ end
20
+
21
+ def ssh_info
22
+ # Run a custom action called "read_ssh_info" which does what it
23
+ # says and puts the resulting SSH info into the `:machine_ssh_info`
24
+ # key in the environment.
25
+ env = @machine.action("read_ssh_info")
26
+ env[:machine_ssh_info]
27
+ end
28
+
29
+ def state
30
+ # Run a custom action we define called "read_state" which does
31
+ # what it says. It puts the state in the `:machine_state_id`
32
+ # key in the environment.
33
+ env = @machine.action("read_state")
34
+
35
+ state_id = env[:machine_state_id]
36
+
37
+ # Get the short and long description
38
+ short = I18n.t("vagrant_openstack.states.short_#{state_id}")
39
+ long = I18n.t("vagrant_openstack.states.long_#{state_id}")
40
+
41
+ # Return the MachineState object
42
+ Vagrant::MachineState.new(state_id, short, long)
43
+ end
44
+
45
+ def to_s
46
+ "OpenStack Cloud"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,5 @@
1
+ module VagrantPlugins
2
+ module OpenStack
3
+ VERSION = "1.1.2"
4
+ end
5
+ end
@@ -0,0 +1,53 @@
1
+ require "pathname"
2
+
3
+ require "vagrant-openstack/plugin"
4
+
5
+ module VagrantPlugins
6
+ module OpenStack
7
+ lib_path = Pathname.new(File.expand_path("../vagrant-openstack", __FILE__))
8
+ autoload :Errors, lib_path.join("errors")
9
+
10
+ # This initializes the i18n load path so that the plugin-specific
11
+ # translations work.
12
+ def self.init_i18n
13
+ I18n.load_path << File.expand_path("locales/en.yml", source_root)
14
+ I18n.reload!
15
+ end
16
+
17
+ # This initializes the logging so that our logs are outputted at
18
+ # the same level as Vagrant core logs.
19
+ def self.init_logging
20
+ # Initialize logging
21
+ level = nil
22
+ begin
23
+ level = Log4r.const_get(ENV["VAGRANT_LOG"].upcase)
24
+ rescue NameError
25
+ # This means that the logging constant wasn't found,
26
+ # which is fine. We just keep `level` as `nil`. But
27
+ # we tell the user.
28
+ level = nil
29
+ end
30
+
31
+ # Some constants, such as "true" resolve to booleans, so the
32
+ # above error checking doesn't catch it. This will check to make
33
+ # sure that the log level is an integer, as Log4r requires.
34
+ level = nil if !level.is_a?(Integer)
35
+
36
+ # Set the logging level on all "vagrant" namespaced
37
+ # logs as long as we have a valid level.
38
+ if level
39
+ logger = Log4r::Logger.new("vagrant_openstack")
40
+ logger.outputters = Log4r::Outputter.stderr
41
+ logger.level = level
42
+ logger = nil
43
+ end
44
+ end
45
+
46
+ # This returns the path to the source of this plugin.
47
+ #
48
+ # @return [Pathname]
49
+ def self.source_root
50
+ @source_root ||= Pathname.new(File.expand_path("../../", __FILE__))
51
+ end
52
+ end
53
+ end
data/locales/en.yml ADDED
@@ -0,0 +1,81 @@
1
+ en:
2
+ vagrant_openstack:
3
+ already_created: |-
4
+ The server is already created.
5
+ deleting_server: |-
6
+ Deleting server...
7
+ finding_flavor: |-
8
+ Finding flavor for server...
9
+ finding_image: |-
10
+ Finding image for server...
11
+ finding_network: |-
12
+ Finding network for server...
13
+ launching_server: |-
14
+ Launching a server with the following settings...
15
+ not_created: |-
16
+ The server hasn't been created yet. Run `vagrant up` first.
17
+ ready: |-
18
+ The server is ready!
19
+ rsync_folder: |-
20
+ Rsyncing folder: %{hostpath} => %{guestpath}
21
+ waiting_for_build: |-
22
+ Waiting for the server to be built...
23
+ waiting_for_ssh: |-
24
+ Waiting for SSH to become available...
25
+
26
+ config:
27
+ api_key_required: |-
28
+ An API key is required.
29
+ username_required: |-
30
+ A username is required.
31
+
32
+ errors:
33
+ create_bad_state: |-
34
+ While creating the server, it transitioned to an unexpected
35
+ state: '%{state}', instead of properly booting. Run `vagrant status`
36
+ to find out what can be done about this state, or `vagrant destroy`
37
+ if you want to start over.
38
+ ssh_unavailable: |-
39
+ The server has been created successfully but it refuses incomming
40
+ ssh connections.
41
+ no_matching_flavor: |-
42
+ No matching flavor was found! Please check your flavor setting
43
+ to make sure you have a valid flavor chosen.
44
+ no_matching_image: |-
45
+ No matching image was found! Please check your image setting to
46
+ make sure you have a valid image chosen.
47
+ no_matching_network: |-
48
+ No network was found for name "%{network_name}". Please check your
49
+ network setting to make sure you have a valid network chosen.
50
+ rsync_error: |-
51
+ There was an error when attemping to rsync a share folder.
52
+ Please inspect the error message below for more info.
53
+
54
+ Host path: %{hostpath}
55
+ Guest path: %{guestpath}
56
+ Error: %{stderr}
57
+
58
+ states:
59
+ short_active: |-
60
+ active
61
+ long_active: |-
62
+ The server is up and running. Run `vagrant ssh` to access it.
63
+ short_build: |-
64
+ building
65
+ long_build: |-
66
+ The server is currently being built. You must wait for this to
67
+ complete before you can access it. You can delete the server, however,
68
+ by running `vagrant destroy`.
69
+ short_shutoff: |-
70
+ shutoff
71
+ long_shutoff: |-
72
+ The server is shutoff.
73
+ short_error: |-
74
+ error
75
+ long_error: |-
76
+ The server is in an erroneous state. Destroy the machine with
77
+ `vagrant destroy`.
78
+ short_not_created: |-
79
+ not created
80
+ long_not_created: |-
81
+ The server is not created. Run `vagrant up` to create it.
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'vagrant-openstack/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "openstack-vagrant"
8
+ gem.version = VagrantPlugins::OpenStack::VERSION
9
+ gem.authors = ["Mitchell Hashimoto"]
10
+ gem.email = ["mitchell@hashicorp.com"]
11
+ gem.description = "Enables Vagrant to manage machines in OpenStack Cloud."
12
+ gem.summary = "Enables Vagrant to manage machines in OpenStack Cloud."
13
+ gem.homepage = "http://www.vagrantup.com"
14
+
15
+ gem.add_runtime_dependency "fog", "~> 1.22"
16
+
17
+ gem.add_development_dependency "rake"
18
+ gem.add_development_dependency "rspec", "~> 2.13.0"
19
+
20
+ gem.files = `git ls-files`.split($/)
21
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
22
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
23
+ gem.require_paths = ["lib"]
24
+ end