vagrant-hivemind 0.1.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.
@@ -0,0 +1,95 @@
1
+ require "optparse"
2
+
3
+ require "vagrant/hivemind/constants"
4
+ require "vagrant/hivemind/util"
5
+ require "vagrant/hivemind/host"
6
+
7
+ module Vagrant
8
+ module Hivemind
9
+ module Command
10
+ class Spawn < Vagrant.plugin("2", :command)
11
+ include Vagrant::Hivemind::Constants
12
+ include Vagrant::Hivemind::Util
13
+
14
+ def self.synopsis
15
+ "Creates a new Drone in the Hive"
16
+ end
17
+
18
+ def execute
19
+ options = {}
20
+
21
+ opts = OptionParser.new do |o|
22
+ o.banner = "Usage: vagrant hivemind spawn [options]"
23
+ o.separator ""
24
+ o.separator "Options:"
25
+ o.separator ""
26
+
27
+ o.on("-n", "--hostname HOSTNAME", "The hostname of the Drone (REQUIRED)") do |n|
28
+ options[:hostname] = n
29
+ end
30
+
31
+ o.on("-c", "--control", "Assign the Drone to be a Control Machine") do |c|
32
+ options[:control] = c
33
+ end
34
+
35
+ o.on("-s", "--size SIZE", BOX_SIZES.keys.map(&:to_s), "The Box Size of the Drone #{BOX_SIZES.keys.map(&:to_s)} (default: :small)") do |s|
36
+ options[:size] = s
37
+ end
38
+
39
+ o.on("-t", "--type TYPE", BOX_TYPES.keys.map(&:to_s), "The Box Type of the Drone #{BOX_TYPES.keys.map(&:to_s)} (default: :server)") do |t|
40
+ options[:type] = t
41
+ end
42
+
43
+ o.on("-d", "--directory DIRECTORY", "Specify the directory where '#{HIVE_FILE}' is located (default: current directory)") do |d|
44
+ options[:directory] = []
45
+ options[:directory] << d
46
+ end
47
+ end
48
+
49
+ argv = parse_options(opts)
50
+ return if !argv
51
+
52
+ unless options[:hostname]
53
+ @env.ui.info opts.help
54
+ return 0
55
+ end
56
+
57
+ root_path = Path.get_root_path_from_options options
58
+
59
+ unless HiveFile.exist? root_path
60
+ @env.ui.error "There is no Hive file in the working directory."
61
+ return 1
62
+ end
63
+
64
+ hosts = HiveFile.read_from root_path
65
+
66
+ if hosts.values.map(&:hostname).include? options[:hostname]
67
+ @env.ui.error "The specified hostname already exists!"
68
+ return 1
69
+ end
70
+
71
+ unless Network.is_valid_hostname? options[:hostname]
72
+ @env.ui.error "Invalid hostname format!"
73
+ return 1
74
+ end
75
+
76
+ drone = {
77
+ options[:hostname] => Vagrant::Hivemind::Host.new(
78
+ options[:hostname],
79
+ Network.next_ip_address(hosts),
80
+ {
81
+ is_control: options[:control],
82
+ box_size: options[:size],
83
+ box_type: options[:type]
84
+ })
85
+ }
86
+ HiveFile.write_to hosts.merge(drone), root_path
87
+ @env.ui.info "Spawned the Drone with hostname '#{options[:hostname]}'"
88
+
89
+ 0
90
+ end
91
+
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,102 @@
1
+ require "optparse"
2
+ require "erb"
3
+
4
+ require "vagrant/hivemind/constants"
5
+ require "vagrant/hivemind/util"
6
+ require "vagrant/hivemind/host"
7
+
8
+ module Vagrant
9
+ module Hivemind
10
+ module Command
11
+ class Up < Vagrant.plugin("2", :command)
12
+ include Vagrant::Hivemind::Constants
13
+ include Vagrant::Hivemind::Util
14
+
15
+ def self.synopsis
16
+ "Starts and provisions a Drone in the Hive"
17
+ end
18
+
19
+ def execute
20
+ options = {}
21
+
22
+ opts = OptionParser.new do |o|
23
+ o.banner = "Usage: vagrant hivemind up [options]"
24
+ o.separator ""
25
+ o.separator "Options:"
26
+ o.separator ""
27
+
28
+ o.on("-n", "--hostname HOSTNAME", "The hostname of the Drone (REQUIRED)") do |n|
29
+ options[:hostname] = n
30
+ end
31
+
32
+ o.on("-d", "--directory DIRECTORY", "Specify the directory where '#{HIVE_FILE}' is located (default: current directory)") do |d|
33
+ options[:directory] = []
34
+ options[:directory] << d
35
+ end
36
+ end
37
+
38
+ argv = parse_options(opts)
39
+ return if !argv
40
+
41
+ unless options[:hostname]
42
+ @env.ui.info opts.help
43
+ return 0
44
+ end
45
+
46
+ root_path = Path.get_root_path_from_options options
47
+
48
+ unless HiveFile.exist? root_path
49
+ @env.ui.error "There is no Hive file in the working directory."
50
+ return 1
51
+ end
52
+
53
+ hosts = HiveFile.read_from root_path
54
+
55
+ unless hosts.values.map(&:hostname).include? options[:hostname]
56
+ @env.ui.error "The specified hostname does not exist!"
57
+ return 1
58
+ end
59
+
60
+ host = hosts[options[:hostname]]
61
+
62
+ Vagrant::Hivemind::Util::Vagrantfile.generate_hivemind_vagrantfile @env, host, root_path
63
+ Ansible.generate_hosts_file hosts, Path.local_data_path(root_path)
64
+ HostsFile.generate_hosts_file hosts, Path.local_data_path(root_path)
65
+
66
+ machines = []
67
+ @env.batch do |batch|
68
+ with_target_vms(options[:hostname]) do |machine|
69
+ @env.ui.info(I18n.t("vagrant.commands.up.upping",
70
+ name: machine.name,
71
+ provider: machine.provider_name))
72
+
73
+ machines << machine
74
+
75
+ batch.action(machine, :up, options)
76
+ end
77
+ end
78
+
79
+ if machines.empty?
80
+ @env.ui.info(I18n.t("vagrant.up_no_machines"))
81
+ return 1
82
+ end
83
+
84
+ machines.each do |m|
85
+ next if !m.config.vm.post_up_message
86
+ next if m.config.vm.post_up_message == ""
87
+
88
+ @env.ui.info("", prefix: false)
89
+
90
+ m.ui.success(I18n.t(
91
+ "vagrant.post_up_message",
92
+ name: m.name.to_s,
93
+ message: m.config.vm.post_up_message))
94
+ end
95
+
96
+ 0
97
+ end
98
+
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,38 @@
1
+ module Vagrant
2
+ module Hivemind
3
+ module Constants
4
+ HIVE_FILE = "hive.yml"
5
+ ANSIBLE_HOSTS_FILE = "ansible.hosts"
6
+ SYSTEM_HOSTS_FILE = "system.hosts"
7
+
8
+ BOX_TYPES = {
9
+ :server => { name: "Server", box_id: "napramirez/ubuntu-14.04.2-LTS-amd64-server", is_gui: false },
10
+ :kde => { name: "KDE", box_id: "napramirez/kubuntu-14.04.2-LTS-amd64-lite", is_gui: true },
11
+ :unity => { name: "Unity", box_id: "napramirez/ubuntu-14.04.2-LTS-amd64-desktoplite", is_gui: true },
12
+ :unityi386 => { name: "Unity i386", box_id: "napramirez/ubuntu-14.04.2-LTS-i386-desktoplite", is_gui: true }
13
+ }
14
+
15
+ BOX_SIZES = {
16
+ :extra_small => { name: "Extra Small", short_name: "xs", memory_in_mb: 256 },
17
+ :small => { name: "Small", short_name: "s", memory_in_mb: 512 },
18
+ :medium => { name: "Medium", short_name: "m", memory_in_mb: 1024 },
19
+ :large => { name: "Large", short_name: "l", memory_in_mb: 2048 },
20
+ :extra_large => { name: "Extra Large", short_name: "xl", memory_in_mb: 4096 }
21
+ }
22
+
23
+ CONTROL_HOSTNAME = "control"
24
+
25
+ PRIVATE_NETWORK = "192.168.50.*"
26
+ PRIVATE_NETWORK_START = 100
27
+ PRIVATE_NETWORK_END = 254
28
+ PRIVATE_NETWORK_IP_ADDRESS_POOL = (PRIVATE_NETWORK_START..PRIVATE_NETWORK_END).map { |i| PRIVATE_NETWORK.sub('*', "#{i}") }
29
+
30
+ SIMPLE_HOSTNAME_REGEX = /^[A-Za-z0-9]+([\-]{0,1}[A-Za-z0-9]+)*$/
31
+ SIMPLE_HOSTNAME_MAX_LENGTH = 20
32
+
33
+ IP_ADDRESS_REGEX = /^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$/
34
+
35
+ PORT_PAIR_REGEX = /^[0-9]{1,5}\:[0-9]{1,5}$/
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,34 @@
1
+ require_relative "constants"
2
+
3
+ module Vagrant
4
+ module Hivemind
5
+ class Host
6
+ attr_accessor :hostname, :ip_address, :is_control, :box_size, :box_type, :forwarded_ports
7
+ @control = nil
8
+
9
+ def initialize(hostname, ip_address, options = {})
10
+ @hostname = hostname
11
+ @ip_address = ip_address
12
+ @is_control = options[:is_control] || false
13
+ @box_size = options[:box_size] || :small.to_s
14
+ @box_type = options[:box_type] || :server.to_s
15
+ @forwarded_ports = []
16
+ end
17
+
18
+ def self.control
19
+ unless @control
20
+ @control = self.new(
21
+ Constants::CONTROL_HOSTNAME,
22
+ Util::Network.starting_ip_address,
23
+ {
24
+ is_control: true,
25
+ box_size: :small.to_s,
26
+ box_type: :server.to_s
27
+ })
28
+ end
29
+ @control
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,16 @@
1
+ require "vagrant"
2
+
3
+ module Vagrant
4
+ module Hivemind
5
+ class Plugin < Vagrant.plugin("2")
6
+ name "Hivemind"
7
+ description "Vagrant extension directives for the Hivemind platform"
8
+
9
+ command "hivemind" do
10
+ require_relative "commands/root"
11
+ Command::Root
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,236 @@
1
+ require "yaml"
2
+ require "erb"
3
+
4
+ require_relative "constants"
5
+
6
+ module Vagrant
7
+ module Hivemind
8
+ module Util
9
+
10
+ class HiveFile
11
+ def self.exist?(path = Pathname.new(Dir.pwd))
12
+ File.exist? get_hive_file_from_path path
13
+ end
14
+
15
+ def self.read_from(path = Pathname.new(Dir.pwd))
16
+ hive_file = get_hive_file_from_path path
17
+ hosts_from_hive_file = {}
18
+ hosts_from_hive_file = YAML.load_file(hive_file) if File.exist? hive_file
19
+ hosts_from_hive_file
20
+ end
21
+
22
+ def self.write_to(hosts = {}, path = Pathname.new(Dir.pwd))
23
+ hive_file = get_hive_file_from_path path
24
+ File.open(hive_file, "w+") do |f|
25
+ f.write(hosts.to_yaml)
26
+ end
27
+ end
28
+
29
+ private
30
+ def self.get_hive_file_from_path(path)
31
+ if File.directory? path
32
+ File.join(path, Vagrant::Hivemind::Constants::HIVE_FILE)
33
+ else
34
+ path
35
+ end
36
+ end
37
+ end
38
+
39
+ class Network
40
+ def self.is_valid_hostname?(hostname)
41
+ return false unless hostname and hostname.size > 0 and hostname.size <= Vagrant::Hivemind::Constants::SIMPLE_HOSTNAME_MAX_LENGTH
42
+ return (Vagrant::Hivemind::Constants::SIMPLE_HOSTNAME_REGEX =~ hostname) != nil
43
+ end
44
+
45
+ def self.is_valid_ip_address?(ip_address)
46
+ return false unless ip_address and ip_address.split(".").size == 4
47
+ return (Vagrant::Hivemind::Constants::IP_ADDRESS_REGEX =~ ip_address) != nil
48
+ end
49
+
50
+ def self.is_valid_port_pair?(port_pair)
51
+ return false unless port_pair and port_pair.split(":").size == 2
52
+ return (Vagrant::Hivemind::Constants::PORT_PAIR_REGEX =~ port_pair) != nil
53
+ end
54
+
55
+ def self.is_valid_port_value?(port)
56
+ port >= 0 and port <= 65535
57
+ end
58
+
59
+ def self.port_pair_to_i(port_pair)
60
+ port_pair.split(":").map.each { |n| n.to_i }
61
+ end
62
+
63
+ def self.get_host_port_pair_with_guest_port(guest_port, host)
64
+ (host.forwarded_ports.select do |forwarded_port| forwarded_port["guest_port"] == guest_port end).first
65
+ end
66
+
67
+ def self.get_host_keys_using_host_port(host_port, hosts)
68
+ hosts.keys.select do |key|
69
+ host = hosts[key]
70
+ host.forwarded_ports and (
71
+ host.forwarded_ports.select do |forwarded_port|
72
+ forwarded_port["host_port"] == host_port
73
+ end).size > 0
74
+ end
75
+ end
76
+
77
+ def self.get_network(ip_address)
78
+ split_ip_address = ip_address.split(".")
79
+ return nil unless split_ip_address.size == 4
80
+ split_ip_address[0,3].join(".")
81
+ end
82
+
83
+ def self.starting_ip_address
84
+ Vagrant::Hivemind::Constants::PRIVATE_NETWORK_IP_ADDRESS_POOL[0]
85
+ end
86
+
87
+ def self.highest_ip_address(hosts = {})
88
+ hosts.values.map(&:ip_address).sort.last
89
+ end
90
+
91
+ def self.next_ip_address(hosts = {})
92
+ highest_ip_address_index = Vagrant::Hivemind::Constants::PRIVATE_NETWORK_IP_ADDRESS_POOL.find_index highest_ip_address(hosts)
93
+
94
+ next_ip_address_index = highest_ip_address_index
95
+ loop do
96
+ next_ip_address_index = ((next_ip_address_index + 1) % Vagrant::Hivemind::Constants::PRIVATE_NETWORK_IP_ADDRESS_POOL.size)
97
+ next_ip_address_candidate = Vagrant::Hivemind::Constants::PRIVATE_NETWORK_IP_ADDRESS_POOL[next_ip_address_index]
98
+
99
+ return next_ip_address_candidate unless hosts.values.map(&:ip_address).include? next_ip_address_candidate
100
+
101
+ if next_ip_address_index == highest_ip_address_index
102
+ raise StandardError, "There are no more available ip addresses for the private network!"
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ class Path
109
+ def self.get_root_path_from_options(options = {})
110
+ path = options[:directory] ||= [Dir.pwd]
111
+ root_path path.first
112
+ end
113
+
114
+ def self.cache_path
115
+ cache_path = Pathname.new(Dir.tmpdir).join "hivemind"
116
+ Dir.mkdir cache_path unless cache_path.directory?
117
+ cache_path
118
+ end
119
+
120
+ def self.hivemind_home_path
121
+ Pathname.new File.expand_path("../../../../", __FILE__)
122
+ end
123
+
124
+ def self.root_path(path = Pathname.new(Dir.pwd))
125
+ Pathname.new File.expand_path path, Pathname.new(Dir.pwd)
126
+ end
127
+
128
+ def self.local_data_path(path = Pathname.new(Dir.pwd))
129
+ local_data_path = root_path(path).join ".vagrant"
130
+ Dir.mkdir local_data_path unless local_data_path.directory?
131
+ local_data_path
132
+ end
133
+ end
134
+
135
+ class Vagrantfile
136
+ def self.generate_hivemind_vagrantfile(env, host, path = Pathname.new(Dir.pwd))
137
+ box_types = Vagrant::Hivemind::Constants::BOX_TYPES
138
+ box_sizes = Vagrant::Hivemind::Constants::BOX_SIZES
139
+ cache_path = Path.cache_path
140
+ hivemind_home_path = Path.hivemind_home_path
141
+ local_data_path = Path.local_data_path path
142
+ b = binding
143
+
144
+ template_string = ""
145
+ File.open(File.expand_path("../../../../templates/Vagrantfile.erb", __FILE__), "r") do |f|
146
+ template_string = f.read
147
+ end
148
+
149
+ template = ERB.new template_string
150
+ template_result = template.result(b)
151
+
152
+ tf = Tempfile.new("Hivemind_Vagrantfile", path)
153
+ tf.write template_result
154
+ tf.close
155
+
156
+ env.define_singleton_method :vagrantfile_name= do |vfn| @vagrantfile_name = vfn end
157
+ env.define_singleton_method :root_path= do |root_path| @root_path = root_path end
158
+ env.define_singleton_method :local_data_path= do |local_data_path| @local_data_path = local_data_path end
159
+ env.vagrantfile_name = [File.basename(tf)]
160
+ env.root_path = path
161
+ env.local_data_path = local_data_path
162
+
163
+ nil
164
+ end
165
+ end
166
+
167
+ class Ansible
168
+ def self.generate_hosts_file(hosts = {}, path = Pathname.new(Dir.pwd))
169
+ datetime_now = DateTime.now.strftime "%F %T %p"
170
+ b = binding
171
+
172
+ template_string = ""
173
+ File.open(File.expand_path("../../../../templates/ansible.hosts.erb", __FILE__), "r") do |f|
174
+ template_string = f.read
175
+ end
176
+
177
+ template = ERB.new template_string
178
+ template_result = template.result(b)
179
+
180
+ ansible_hosts_file = get_ansible_hosts_file_from_path path
181
+ File.open(ansible_hosts_file, "w+") do |f|
182
+ f.write(template_result)
183
+ end
184
+ end
185
+
186
+ def self.read_from(path = Pathname.new(Dir.pwd))
187
+ ansible_hosts_file = get_ansible_hosts_file_from_path path
188
+ ansible_hosts = ""
189
+ File.open() do |f|
190
+ ansible_hosts = f.read
191
+ end
192
+ ansible_hosts
193
+ end
194
+
195
+ private
196
+ def self.get_ansible_hosts_file_from_path(path)
197
+ if File.directory? path
198
+ File.join(path, Vagrant::Hivemind::Constants::ANSIBLE_HOSTS_FILE)
199
+ else
200
+ path
201
+ end
202
+ end
203
+ end
204
+
205
+ class HostsFile
206
+ def self.generate_hosts_file(hosts = {}, path = Pathname.new(Dir.pwd))
207
+ datetime_now = DateTime.now.strftime "%F %T %p"
208
+ b = binding
209
+
210
+ template_string = ""
211
+ File.open(File.expand_path("../../../../templates/system.hosts.erb", __FILE__), "r") do |f|
212
+ template_string = f.read
213
+ end
214
+
215
+ template = ERB.new template_string
216
+ template_result = template.result(b)
217
+
218
+ system_hosts_file = get_system_hosts_file_from_path path
219
+ File.open(system_hosts_file, "w+") do |f|
220
+ f.write(template_result)
221
+ end
222
+ end
223
+
224
+ private
225
+ def self.get_system_hosts_file_from_path(path)
226
+ if File.directory? path
227
+ File.join(path, Vagrant::Hivemind::Constants::SYSTEM_HOSTS_FILE)
228
+ else
229
+ path
230
+ end
231
+ end
232
+ end
233
+
234
+ end
235
+ end
236
+ end