knife-voxel 0.0.5

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,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: []