knife-voxel 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .chef
6
+ /env.sh
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in knife-voxel.gemspec
4
+ gemspec
data/README ADDED
@@ -0,0 +1,37 @@
1
+ = Introduction =
2
+
3
+ knife-voxel is a plugin for the tool 'knife', part of the configuration
4
+ management system 'chef'. http://www.opscode.com/. It allows you to
5
+ provision Voxel dot Net (www.voxel.net) cloud instances (physical as
6
+ well as virtual) and then bootstrap them as chef clients.
7
+
8
+ This plugin is currently a work in progress, although all major
9
+ functions should work as expected. The command set models Voxel's API
10
+ hAPI (http://api.voxel.net/docs/) . We depend on the voxel-hapi
11
+ library for access to this API. This will be replaced with a fog provider
12
+ based backend in the future.
13
+
14
+
15
+ = Usage =
16
+
17
+ ** VOXEL COMMANDS **
18
+ knife voxel voxcloud delete DEVICE_ID (options)
19
+ knife voxel voxservers inventory list (options)
20
+ knife voxel images list (options)
21
+ knife voxel voxcloud create (options)
22
+ knife voxel devices list (options)
23
+ knife voxel voxservers delete DEVICE_ID (options)
24
+ knife voxel voxservers create (options)
25
+
26
+
27
+ = Examples =
28
+
29
+ > knife voxel voxcloud delete 12345
30
+
31
+ > knife voxel voxcloud create --image-id 55 --hostname app1.domain.com \
32
+ --processing-cores 4 --disk-size 20 --facility LGA8
33
+
34
+ = Author =
35
+
36
+ J. W. Brinkerhoff <jwb@voxel.net>
37
+
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "knife-voxel/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "knife-voxel"
7
+ s.version = Knife::Voxel::VERSION
8
+ s.authors = ["James W. Brinkerhoff"]
9
+ s.email = ["jwb@voxel.net"]
10
+ s.homepage = "http://api.voxel.net/"
11
+ s.summary = "Voxel hAPI Support for Chef's knife command"
12
+ s.description = s.summary
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib"]
18
+
19
+ # specify any dependencies here; for example:
20
+ # s.add_development_dependency "rspec"
21
+ s.add_runtime_dependency "voxel-hapi", ">= 1.1.10"
22
+ end
@@ -0,0 +1,40 @@
1
+ require 'chef/knife'
2
+
3
+ class Chef
4
+ class Knife
5
+ module VoxelBase
6
+ def self.included(includer)
7
+ includer.class_eval do
8
+
9
+ deps do
10
+ require 'hapi'
11
+ end
12
+
13
+ option :voxel_api_key,
14
+ :short => "-K KEY",
15
+ :long => "--voxel-api-key KEY",
16
+ :description => "Voxel hAPI Key",
17
+ :proc => Proc.new { |key| Chef::Config[:knife][:voxel_api_key] = key }
18
+
19
+ option :voxel_api_secret,
20
+ :short => "-S SECRET",
21
+ :long => "--voxel-api-secret SECRET",
22
+ :description => "Voxel hAPI Secret",
23
+ :proc => Proc.new { |secret| Chef::Config[:knife][:voxel_api_secret] = secret }
24
+ end
25
+ end
26
+
27
+ def hapi
28
+ @hapi ||= begin
29
+ hapi = HAPI.new(
30
+ :useauthkey => true,
31
+ :username => Chef::Config[:knife][:voxel_api_key],
32
+ :password => Chef::Config[:knife][:voxel_api_secret],
33
+ :default_format => :ruby,
34
+ :debug => false
35
+ )
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,56 @@
1
+ require 'chef/knife/voxel_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class VoxelDevicesList < Knife
6
+ include Knife::VoxelBase
7
+
8
+ banner "knife voxel devices list (options)"
9
+
10
+ def devices_list
11
+ end
12
+
13
+ def run
14
+ devices = [ ui.color('ID', :bold), ui.color('Name', :bold), ui.color('Type', :bold), ui.color('Status', :bold), ui.color('IP', :bold) ]
15
+ statuses = hapi.helper_devices_status
16
+
17
+ devices_list = hapi.voxel_devices_list['devices']
18
+
19
+ unless devices_list.empty?
20
+ devices_list['device'] = [ devices_list['device'] ] if devices_list['device'].is_a?(Hash)
21
+
22
+ devices_list['device'].each do |device|
23
+ devices << device['id']
24
+ devices << device['label']
25
+ devices << case device['type']['content']
26
+ when "Virtual Server"
27
+ "VoxCLOUD"
28
+ when "Server"
29
+ "VoxSERVER"
30
+ else
31
+ device['type']['content']
32
+ end
33
+
34
+ devices << (statuses.has_key?(device['id']) ? statuses[device['id']] : "N/A")
35
+
36
+ if device.has_key?('ipassignments')
37
+ ips = device['ipassignments']['ipassignment']
38
+
39
+ if ips.is_a?(Hash)
40
+ ips = [ ips ]
41
+ end
42
+
43
+ ip = ips.select { |a| a['type'] == "frontend" }.first
44
+
45
+ devices << (ip.nil? ? "" : ip["content"])
46
+ else
47
+ devices << ""
48
+ end
49
+ end
50
+ end
51
+
52
+ puts ui.list(devices, :columns_across, 5)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,25 @@
1
+ require 'chef/knife/voxel_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class VoxelImagesList < Knife
6
+ include Knife::VoxelBase
7
+
8
+ banner "knife voxel images list (options)"
9
+
10
+ def images_list
11
+ end
12
+
13
+ def run
14
+ images = [ ui.color('ID', :bold), ui.color('Name', :bold) ]
15
+
16
+ hapi.voxel_images_list['images']['image'].each do |image|
17
+ images << image['id']
18
+ images << image['summary']
19
+ end
20
+
21
+ puts ui.list(images, :columns_across, 2)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,191 @@
1
+ require 'chef/knife/voxel_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class VoxelVoxcloudCreate < Knife
6
+ include Knife::VoxelBase
7
+
8
+ deps do
9
+ require 'chef/json_compat'
10
+ require 'chef/knife/bootstrap'
11
+ require 'hapi'
12
+ require 'readline'
13
+
14
+ Chef::Knife::Bootstrap.load_deps
15
+ end
16
+
17
+ banner "knife voxel voxcloud create (options)"
18
+
19
+ option :processing_cores,
20
+ :long => "--processing-cores CORES",
21
+ :description => "Total number of processing cores. Memory is 2048xCORES",
22
+ :default => 1
23
+
24
+ option :disk_size,
25
+ :long => "--disk-size SIZE",
26
+ :description => "Disk Volume Size, in GB",
27
+ :default => 10
28
+
29
+ option :image_id,
30
+ :long => "--image-id IMAGE",
31
+ :description => "Image Id to Install",
32
+ :required => true
33
+
34
+ option :hostname,
35
+ :long => "--hostname NAME",
36
+ :description => "The server's hostname",
37
+ :required => true
38
+
39
+ option :chef_node_name,
40
+ :short => "-N NAME",
41
+ :long => "--node-name NAME",
42
+ :description => "The Chef node name for your new node"
43
+
44
+ option :ssh_user,
45
+ :short => "-x USERNAME",
46
+ :long => "--ssh-user USERNAME",
47
+ :description => "The ssh username; default is 'root'",
48
+ :default => "root"
49
+
50
+ option :ssh_password,
51
+ :short => "-P PASSWORD",
52
+ :long => "--ssh-password PASSWORD",
53
+ :description => "The ssh password"
54
+
55
+ option :facility,
56
+ :long => "--facility FACILITY",
57
+ :description => "Voxel Facility (LDJ1, LGA8, AMS2, SIN1, etc)",
58
+ :required => true
59
+
60
+ option :identity_file,
61
+ :short => "-i IDENTITY_FILE",
62
+ :long => "--identity-file IDENTITY_FILE",
63
+ :description => "The SSH identity file used for authentication"
64
+
65
+ option :prerelease,
66
+ :long => "--prerelease",
67
+ :description => "Install the pre-release chef gems"
68
+
69
+ option :bootstrap_version,
70
+ :long => "--bootstrap-version VERSION",
71
+ :description => "The version of Chef to install",
72
+ :proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v }
73
+
74
+ option :distro,
75
+ :short => "-d DISTRO",
76
+ :long => "--distro DISTRO",
77
+ :description => "Bootstrap a distro using a template; default is 'ubuntu10.04-gems'",
78
+ :proc => Proc.new { |d| Chef::Config[:knife][:distro] = d },
79
+ :default => "ubuntu10.04-gems"
80
+
81
+ option :template_file,
82
+ :long => "--template-file TEMPLATE",
83
+ :description => "Full path to location of template to use",
84
+ :proc => Proc.new { |t| Chef::Config[:knife][:template_file] = t },
85
+ :default => false
86
+
87
+ option :run_list,
88
+ :short => "-r RUN_LIST",
89
+ :long => "--run-list RUN_LIST",
90
+ :description => "Comma separated list of roles/recipes to apply",
91
+ :proc => lambda { |o| o.split(/[\s,]+/) },
92
+ :default => []
93
+
94
+ def bootstrap_for_node(device)
95
+ bootstrap = Chef::Knife::Bootstrap.new
96
+
97
+ bootstrap.name_args = [device['ipassignments']['ipassignment'].select { |i| i['type'] == 'frontend' }.first['content']]
98
+ bootstrap.config[:run_list] = config[:run_list]
99
+ bootstrap.config[:ssh_user] = config[:ssh_user] || "root"
100
+ bootstrap.config[:ssh_password] = device['accessmethods']['accessmethod'].select { |a| a['type'] == 'admin' }.first['password']
101
+ bootstrap.config[:identity_file] = config[:identity_file]
102
+ bootstrap.config[:chef_node_name] = config[:chef_node_name] || "d#{device['id']}"
103
+ bootstrap.config[:prerelease] = config[:prerelease]
104
+ bootstrap.config[:bootstrap_version] = config[:bootstrap_version]
105
+ bootstrap.config[:distro] = config[:distro]
106
+ bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
107
+ bootstrap.config[:template_file] = config[:template_file]
108
+ bootstrap.config[:environment] = config[:environment]
109
+ bootstrap
110
+ end
111
+
112
+ def tcp_test_ssh(hostname)
113
+ begin
114
+ tcp_socket = TCPSocket.new(hostname, 22)
115
+ readable = IO.select([tcp_socket], nil, nil, 5)
116
+ if readable
117
+ Chef::Log.debug("sshd accepting connections on #{hostname}, banner is #{tcp_socket.gets}")
118
+ yield
119
+ true
120
+ else
121
+ false
122
+ end
123
+ rescue Errno::ETIMEDOUT
124
+ false
125
+ rescue Errno::EPERM
126
+ false
127
+ rescue Errno::ECONNREFUSED
128
+ sleep 2
129
+ false
130
+ rescue Errno::EHOSTUNREACH
131
+ sleep 2
132
+ false
133
+ ensure
134
+ tcp_socket && tcp_socket.close
135
+ end
136
+ end
137
+
138
+ def run
139
+ $stdout.sync = true
140
+
141
+ create = hapi.voxel_voxcloud_create(
142
+ :image_id => config[:image_id],
143
+ :hostname => config[:hostname],
144
+ :processing_cores => config[:processing_cores],
145
+ :facility => config[:facility],
146
+ :disk_size => config[:disk_size]
147
+ )
148
+
149
+ if create['stat'] == "fail"
150
+ ui.error(create['err']['msg'])
151
+ else
152
+ sleep 2
153
+
154
+ device = hapi.voxel_devices_list( :device_id => create['device']['id'], :verbosity => 'extended' )
155
+
156
+ if device['stat'] == "fail"
157
+ ui.error("Device Listing Failed: #{device['err']['msg']}")
158
+ else
159
+ device = device['devices']['device']
160
+
161
+ puts "#{ui.color("Device ID", :cyan)}: #{device['id']}"
162
+ puts "#{ui.color("Name", :cyan)}: #{device['label']}"
163
+ puts "#{ui.color("Image Id", :cyan)}: #{config[:image_id]}"
164
+ puts "#{ui.color("Facility", :cyan)}: #{device['location']['facility']['code']}"
165
+ puts "#{ui.color("Public IP Address", :cyan)}: #{device['ipassignments']['ipassignment'].select { |i| i['type'] == 'frontend' }.first['content']}"
166
+ puts "#{ui.color("Private IP Address", :cyan)}: #{device['ipassignments']['ipassignment'].select { |i| i['type'] == 'backend' }.first['content']}"
167
+ puts "#{ui.color("Root Password", :cyan)}: #{device['accessmethods']['accessmethod'].select { |a| a['type'] == 'admin' }.first['password']}"
168
+
169
+ status = hapi.voxel_voxcloud_status( :device_id => device['id'], :verbosity => 'extended' )
170
+
171
+ while %w{ QUEUED IN_PROGRESS }.include?( status['devices']['device']['status'] ) do
172
+ print "."
173
+ status = hapi.voxel_voxcloud_status( :device_id => device['id'], :verbosity => 'extended' )
174
+ sleep 10
175
+ end
176
+
177
+ print "\n#{ui.color("Waiting for sshd", :magenta)}"
178
+
179
+ print(".") until tcp_test_ssh(device['ipassignments']['ipassignment'].select { |i| i['type'] == 'frontend' }.first['content']) { sleep @initial_sleep_delay ||= 10; puts("done") }
180
+
181
+ bootstrap_for_node(device).run
182
+
183
+ puts "#{ui.color("Environment", :cyan)}: #{config[:environment] || '_default'}"
184
+ puts "#{ui.color("Run List", :cyan)}: #{config[:run_list].join(', ')}"
185
+ end
186
+ end
187
+ end
188
+
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,50 @@
1
+ require 'chef/knife/voxel_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class VoxelVoxcloudDelete < Knife
6
+ include Knife::VoxelBase
7
+
8
+ banner "knife voxel voxcloud delete DEVICE_ID (options)"
9
+
10
+ def run
11
+ if @name_args.empty?
12
+ ui.error( "knife voxel voxcloud delete DEVICE_ID" )
13
+ else
14
+
15
+ @name_args.each do |device_id|
16
+ device = hapi.voxel_devices_list( :device_id => device_id, :verbosity => 'extended' )
17
+
18
+ if device['stat'] == "fail"
19
+ ui.error(device['err']['msg'])
20
+ else
21
+ device = device['devices']['device']
22
+
23
+ if device['type']['content'] == "Virtual Server"
24
+ puts "#{ui.color("Device ID", :cyan)}: #{device['id']}"
25
+ puts "#{ui.color("Name", :cyan)}: #{device['label']}"
26
+ puts "#{ui.color("Image Id", :cyan)}: #{config[:image_id]}"
27
+ puts "#{ui.color("Facility", :cyan)}: #{device['location']['facility']['code']}"
28
+ puts "#{ui.color("Public IP Address", :cyan)}: #{device['ipassignments']['ipassignment'].select { |i| i['type'] == 'frontend' }.first['content']}"
29
+ puts "#{ui.color("Private IP Address", :cyan)}: #{device['ipassignments']['ipassignment'].select { |i| i['type'] == 'backend' }.first['content']}"
30
+ puts "\n"
31
+
32
+ confirm("Do you really want to delete this VoxCLOUD device")
33
+
34
+ delete = hapi.voxel_voxcloud_delete( :device_id => device_id )
35
+
36
+ if delete['stat'] == "ok"
37
+ ui.warn("Deleted VoxCLOUD device #{device['id']} named #{device['label']}")
38
+ else
39
+ ui.error("Error removing VoxCLOUD device #{device['label']}")
40
+ end
41
+ else
42
+ ui.warn("Device #{device['id']} is not a VoxCLOUD device.")
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,191 @@
1
+ require 'chef/knife/voxel_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class VoxelVoxserversCreate < Knife
6
+ include Knife::VoxelBase
7
+
8
+ deps do
9
+ require 'chef/json_compat'
10
+ require 'chef/knife/bootstrap'
11
+ require 'hapi'
12
+ require 'readline'
13
+
14
+ Chef::Knife::Bootstrap.load_deps
15
+ end
16
+
17
+ banner "knife voxel voxservers create (options)"
18
+
19
+ option :configuration_id,
20
+ :long => "--configuration-id CONFIGID",
21
+ :description => "VoxSERVER Configuration Id",
22
+ :required => true
23
+
24
+ option :swap_size,
25
+ :long => "--swap-size SIZE",
26
+ :description => "Swap Partition Size, in GB",
27
+ :default => 4
28
+
29
+ option :image_id,
30
+ :long => "--image-id IMAGE",
31
+ :description => "Image Id to Install",
32
+ :required => true
33
+
34
+ option :hostname,
35
+ :long => "--hostname NAME",
36
+ :description => "The server's hostname",
37
+ :required => true
38
+
39
+ option :chef_node_name,
40
+ :short => "-N NAME",
41
+ :long => "--node-name NAME",
42
+ :description => "The Chef node name for your new node"
43
+
44
+ option :ssh_user,
45
+ :short => "-x USERNAME",
46
+ :long => "--ssh-user USERNAME",
47
+ :description => "The ssh username; default is 'root'",
48
+ :default => "root"
49
+
50
+ option :ssh_password,
51
+ :short => "-P PASSWORD",
52
+ :long => "--ssh-password PASSWORD",
53
+ :description => "The ssh password"
54
+
55
+ option :facility,
56
+ :long => "--facility FACILITY",
57
+ :description => "Voxel Facility (LDJ1, LGA8, AMS2, SIN1, etc)",
58
+ :required => true
59
+
60
+ option :identity_file,
61
+ :short => "-i IDENTITY_FILE",
62
+ :long => "--identity-file IDENTITY_FILE",
63
+ :description => "The SSH identity file used for authentication"
64
+
65
+ option :prerelease,
66
+ :long => "--prerelease",
67
+ :description => "Install the pre-release chef gems"
68
+
69
+ option :bootstrap_version,
70
+ :long => "--bootstrap-version VERSION",
71
+ :description => "The version of Chef to install",
72
+ :proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v }
73
+
74
+ option :distro,
75
+ :short => "-d DISTRO",
76
+ :long => "--distro DISTRO",
77
+ :description => "Bootstrap a distro using a template; default is 'ubuntu10.04-gems'",
78
+ :proc => Proc.new { |d| Chef::Config[:knife][:distro] = d },
79
+ :default => "ubuntu10.04-gems"
80
+
81
+ option :template_file,
82
+ :long => "--template-file TEMPLATE",
83
+ :description => "Full path to location of template to use",
84
+ :proc => Proc.new { |t| Chef::Config[:knife][:template_file] = t },
85
+ :default => false
86
+
87
+ option :run_list,
88
+ :short => "-r RUN_LIST",
89
+ :long => "--run-list RUN_LIST",
90
+ :description => "Comma separated list of roles/recipes to apply",
91
+ :proc => lambda { |o| o.split(/[\s,]+/) },
92
+ :default => []
93
+
94
+ def bootstrap_for_node(device)
95
+ bootstrap = Chef::Knife::Bootstrap.new
96
+
97
+ bootstrap.name_args = [device['ipassignments']['ipassignment'].select { |i| i['type'] == 'frontend' }.first['content']]
98
+ bootstrap.config[:run_list] = config[:run_list]
99
+ bootstrap.config[:ssh_user] = config[:ssh_user] || "root"
100
+ bootstrap.config[:ssh_password] = device['accessmethods']['accessmethod'].select { |a| a['type'] == 'admin' }.first['password']
101
+ bootstrap.config[:identity_file] = config[:identity_file]
102
+ bootstrap.config[:chef_node_name] = config[:chef_node_name] || "d#{device['id']}"
103
+ bootstrap.config[:prerelease] = config[:prerelease]
104
+ bootstrap.config[:bootstrap_version] = config[:bootstrap_version]
105
+ bootstrap.config[:distro] = config[:distro]
106
+ bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
107
+ bootstrap.config[:template_file] = config[:template_file]
108
+ bootstrap.config[:environment] = config[:environment]
109
+ bootstrap
110
+ end
111
+
112
+ def tcp_test_ssh(hostname)
113
+ begin
114
+ tcp_socket = TCPSocket.new(hostname, 22)
115
+ readable = IO.select([tcp_socket], nil, nil, 5)
116
+ if readable
117
+ Chef::Log.debug("sshd accepting connections on #{hostname}, banner is #{tcp_socket.gets}")
118
+ yield
119
+ true
120
+ else
121
+ false
122
+ end
123
+ rescue Errno::ETIMEDOUT
124
+ false
125
+ rescue Errno::EPERM
126
+ false
127
+ rescue Errno::ECONNREFUSED
128
+ sleep 2
129
+ false
130
+ rescue Errno::EHOSTUNREACH
131
+ sleep 2
132
+ false
133
+ ensure
134
+ tcp_socket && tcp_socket.close
135
+ end
136
+ end
137
+
138
+ def run
139
+ $stdout.sync = true
140
+
141
+ create = hapi.voxel_voxservers_create(
142
+ :image_id => config[:image_id],
143
+ :hostname => config[:hostname],
144
+ :configuration_id => config[:configuration_id],
145
+ :facility => config[:facility],
146
+ :swap_space => config[:swap_size]
147
+ )
148
+
149
+ if create['stat'] == "fail"
150
+ ui.error(create['err']['msg'])
151
+ else
152
+ sleep 2
153
+
154
+ device = hapi.voxel_devices_list( :device_id => create['device']['id'], :verbosity => 'extended' )
155
+
156
+ if device['stat'] == "fail"
157
+ ui.error(device['err']['msg'])
158
+ else
159
+ device = device['devices']['device']
160
+
161
+ puts "#{ui.color("Device ID", :cyan)}: #{device['id']}"
162
+ puts "#{ui.color("Name", :cyan)}: #{device['label']}"
163
+ puts "#{ui.color("Image Id", :cyan)}: #{config[:image_id]}"
164
+ puts "#{ui.color("Facility", :cyan)}: #{device['location']['facility']['code']}"
165
+ puts "#{ui.color("Public IP Address", :cyan)}: #{device['ipassignments']['ipassignment'].select { |i| i['type'] == 'frontend' }.first['content']}"
166
+ puts "#{ui.color("Private IP Address", :cyan)}: #{device['ipassignments']['ipassignment'].select { |i| i['type'] == 'backend' }.first['content']}"
167
+ puts "#{ui.color("Root Password", :cyan)}: #{device['accessmethods']['accessmethod'].select { |a| a['type'] == 'admin' }.first['password']}"
168
+
169
+ status = hapi.voxel_voxservers_status( :device_id => device['id'], :verbosity => 'extended' )
170
+
171
+ while %w{ QUEUED IN_PROGRESS }.include?( status['devices']['device']['status'] ) do
172
+ print "."
173
+ status = hapi.voxel_voxservers_status( :device_id => device['id'], :verbosity => 'extended' )
174
+ sleep 10
175
+ end
176
+
177
+ print "\n#{ui.color("Waiting for sshd", :magenta)}"
178
+
179
+ print(".") until tcp_test_ssh(device['ipassignments']['ipassignment'].select { |i| i['type'] == 'frontend' }.first['content']) { sleep @initial_sleep_delay ||= 10; puts("done") }
180
+
181
+ bootstrap_for_node(device).run
182
+
183
+ puts "#{ui.color("Environment", :cyan)}: #{config[:environment] || '_default'}"
184
+ puts "#{ui.color("Run List", :cyan)}: #{config[:run_list].join(', ')}"
185
+ end
186
+ end
187
+ end
188
+
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,49 @@
1
+ require 'chef/knife/voxel_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class VoxelVoxserversDelete < Knife
6
+ include Knife::VoxelBase
7
+
8
+ banner "knife voxel voxservers delete DEVICE_ID (options)"
9
+
10
+ def run
11
+ if @name_args.empty?
12
+ ui.error("knife voxel voxservers delete DEVICE_ID (options)")
13
+ else
14
+ @name_args.each do |device_id|
15
+ device = hapi.voxel_devices_list( :device_id => device_id, :verbosity => 'extended' )
16
+
17
+ if device['stat'] == "fail"
18
+ ui.error(device['err']['msg'])
19
+ else
20
+ device = device['devices']['device']
21
+
22
+ if device['type']['content'] == "Server"
23
+ puts "#{ui.color("Device ID", :cyan)}: #{device['id']}"
24
+ puts "#{ui.color("Name", :cyan)}: #{device['label']}"
25
+ puts "#{ui.color("Image Id", :cyan)}: #{config[:image_id]}"
26
+ puts "#{ui.color("Facility", :cyan)}: #{device['location']['facility']['code']}"
27
+ puts "#{ui.color("Public IP Address", :cyan)}: #{device['ipassignments']['ipassignment'].select { |i| i['type'] == 'frontend' }.first['content']}"
28
+ puts "#{ui.color("Private IP Address", :cyan)}: #{device['ipassignments']['ipassignment'].select { |i| i['type'] == 'backend' }.first['content']}"
29
+ puts "\n"
30
+
31
+ confirm("Do you really want to delete this VoxSERVER device")
32
+
33
+ delete = hapi.voxel_voxservers_delete( :device_id => device_id )
34
+
35
+ if delete['stat'] == "ok"
36
+ ui.info("Deleted VoxSERVER device #{device['id']} named #{device['label']}")
37
+ else
38
+ ui.error("Error removing VoxSERVER device #{device['label']}: #{delete['err']['msg']}")
39
+ end
40
+ else
41
+ ui.error("Device #{device['id']} is not a VoxSERVER device.")
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,53 @@
1
+ require 'chef/knife/voxel_base'
2
+
3
+ class Chef
4
+ class Knife
5
+ class VoxelVoxserversInventoryList < Knife
6
+ include Knife::VoxelBase
7
+
8
+ banner "knife voxel voxservers inventory list (options)"
9
+
10
+ def run
11
+ inventory_header = [ ui.color('ID', :bold), ui.color('Summary', :bold) ]
12
+
13
+ facilities = hapi.voxel_voxservers_facilities_list
14
+
15
+ unless facilities['facilities'].empty?
16
+ facilities['facilities']['facility'].each do |facility|
17
+ puts ui.color("#{facility['label']} (#{facility['description']})\n", :bold)
18
+
19
+ available_inventory = hapi.voxel_voxservers_inventory_list( :facility => facility['label'], :verbosity => 'compact' )
20
+
21
+ if available_inventory['facilities'].empty?
22
+ puts "No inventory available at this time."
23
+ else
24
+ inventory = available_inventory['facilities']['facility']['configuration']
25
+ inventory = [ inventory ] if inventory.is_a?(Hash)
26
+
27
+ local_inventory = inventory_header.clone
28
+ inventory.each do |cfg|
29
+ local_inventory << cfg['id']
30
+ local_inventory << cfg['summary']
31
+ end
32
+
33
+ puts ui.list(local_inventory, :columns_across, 2)
34
+ end
35
+
36
+ puts "\n"
37
+ end
38
+ end
39
+
40
+
41
+ # unless available_inventory['facilities'].empty?
42
+ # if available_inventory['facilities']['facility'].is_a?(Hash)
43
+ # available_inventory['facilities']['facility'] = [ available_inventory['facilities']['facility'] ]
44
+ #
45
+ # available_inventory['facilities']['facility'].each do |cfg|
46
+ #
47
+ # end
48
+ # end
49
+ # end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,5 @@
1
+ module Knife
2
+ module Voxel
3
+ VERSION = "0.0.5"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: knife-voxel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - James W. Brinkerhoff
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-10-13 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: voxel-hapi
16
+ requirement: &14538140 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.1.10
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *14538140
25
+ description: Voxel hAPI Support for Chef's knife command
26
+ email:
27
+ - jwb@voxel.net
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - .gitignore
33
+ - Gemfile
34
+ - README
35
+ - Rakefile
36
+ - knife-voxel.gemspec
37
+ - lib/chef/knife/voxel_base.rb
38
+ - lib/chef/knife/voxel_devices_list.rb
39
+ - lib/chef/knife/voxel_images_list.rb
40
+ - lib/chef/knife/voxel_voxcloud_create.rb
41
+ - lib/chef/knife/voxel_voxcloud_delete.rb
42
+ - lib/chef/knife/voxel_voxservers_create.rb
43
+ - lib/chef/knife/voxel_voxservers_delete.rb
44
+ - lib/chef/knife/voxel_voxservers_inventory_list.rb
45
+ - lib/knife-voxel/version.rb
46
+ homepage: http://api.voxel.net/
47
+ licenses: []
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubyforge_project:
66
+ rubygems_version: 1.8.10
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: Voxel hAPI Support for Chef's knife command
70
+ test_files: []