knife-softlayer 0.4.0 → 0.4.1
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.
- checksums.yaml +5 -13
- data/.gitignore +19 -19
- data/.travis.yml +20 -22
- data/CHANGELOG.md +19 -19
- data/CONTRIBUTING.md +31 -31
- data/CONTRIBUTORS.md +2 -2
- data/Gemfile +4 -4
- data/LICENSE.txt +72 -72
- data/README.md +83 -83
- data/Rakefile +37 -37
- data/docs/cla-corporate.md +133 -133
- data/docs/cla-individual.md +84 -84
- data/examples/datacenter.md +94 -94
- data/examples/flavor.md +58 -58
- data/examples/global_ip.md +22 -22
- data/examples/image.md +34 -34
- data/examples/key_pair.md +56 -56
- data/examples/server.md +82 -81
- data/examples/vlan.md +85 -85
- data/knife-softlayer.gemspec +39 -39
- data/lib/chef/knife/flavor/base.rb +52 -52
- data/lib/chef/knife/softlayer.rb +14 -14
- data/lib/chef/knife/softlayer_base.rb +112 -112
- data/lib/chef/knife/softlayer_datacenter_list.rb +28 -28
- data/lib/chef/knife/softlayer_datacenter_show.rb +45 -45
- data/lib/chef/knife/softlayer_delete.rb +6 -6
- data/lib/chef/knife/softlayer_flavor_list.rb +53 -53
- data/lib/chef/knife/softlayer_global_ip_list.rb +34 -34
- data/lib/chef/knife/softlayer_image_list.rb +26 -26
- data/lib/chef/knife/softlayer_key_pair_create.rb +37 -37
- data/lib/chef/knife/softlayer_key_pair_list.rb +28 -28
- data/lib/chef/knife/softlayer_list.rb +6 -6
- data/lib/chef/knife/softlayer_server_create.rb +459 -453
- data/lib/chef/knife/softlayer_server_destroy.rb +146 -146
- data/lib/chef/knife/softlayer_server_list.rb +27 -27
- data/lib/chef/knife/softlayer_server_relaunch.rb +208 -208
- data/lib/chef/knife/softlayer_vlan_create.rb +37 -37
- data/lib/chef/knife/softlayer_vlan_list.rb +28 -28
- data/lib/chef/knife/softlayer_vlan_show.rb +40 -40
- data/lib/knife-softlayer/version.rb +12 -12
- data/spec/spec_helper.rb +21 -21
- data/spec/unit/softlayer_base_spec.rb +26 -26
- data/spec/unit/softlayer_server_create_spec.rb +105 -105
- data/spec/unit/softlayer_server_destroy_spec.rb +47 -47
- metadata +29 -29
@@ -1,34 +1,34 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Matt Eldridge (<matt.eldridge@us.ibm.com>)
|
3
|
-
# © Copyright IBM Corporation 2014.
|
4
|
-
#
|
5
|
-
# LICENSE: Apache 2.0 (http://www.apache.org/licenses/)
|
6
|
-
#
|
7
|
-
|
8
|
-
require 'chef/knife/softlayer_base'
|
9
|
-
|
10
|
-
class Chef
|
11
|
-
class Knife
|
12
|
-
class SoftlayerGlobalIpList < Knife
|
13
|
-
|
14
|
-
include Knife::SoftlayerBase
|
15
|
-
|
16
|
-
banner 'knife softlayer global ip list (options)'
|
17
|
-
|
18
|
-
def run
|
19
|
-
$stdout.sync = true
|
20
|
-
|
21
|
-
if connection(:network).get_global_ip_records.body.empty?
|
22
|
-
puts ui.color("No global ip addresses found.", :green)
|
23
|
-
else
|
24
|
-
puts ui.color("This operation can take several minutes. ", :yellow)
|
25
|
-
table_data = connection(:network).ips.map do |ip|
|
26
|
-
{:address => ip.address, :destination => ip.destination_ip.respond_to?(:address) ? ip.destination_ip.address : 'NOT ROUTED'} if ip.global?
|
27
|
-
end.compact
|
28
|
-
puts Formatador.display_table(table_data, [:address, :destination])
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
1
|
+
#
|
2
|
+
# Author:: Matt Eldridge (<matt.eldridge@us.ibm.com>)
|
3
|
+
# © Copyright IBM Corporation 2014.
|
4
|
+
#
|
5
|
+
# LICENSE: Apache 2.0 (http://www.apache.org/licenses/)
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'chef/knife/softlayer_base'
|
9
|
+
|
10
|
+
class Chef
|
11
|
+
class Knife
|
12
|
+
class SoftlayerGlobalIpList < Knife
|
13
|
+
|
14
|
+
include Knife::SoftlayerBase
|
15
|
+
|
16
|
+
banner 'knife softlayer global ip list (options)'
|
17
|
+
|
18
|
+
def run
|
19
|
+
$stdout.sync = true
|
20
|
+
|
21
|
+
if connection(:network).get_global_ip_records.body.empty?
|
22
|
+
puts ui.color("No global ip addresses found.", :green)
|
23
|
+
else
|
24
|
+
puts ui.color("This operation can take several minutes. ", :yellow)
|
25
|
+
table_data = connection(:network).ips.map do |ip|
|
26
|
+
{:address => ip.address, :destination => ip.destination_ip.respond_to?(:address) ? ip.destination_ip.address : 'NOT ROUTED'} if ip.global?
|
27
|
+
end.compact
|
28
|
+
puts Formatador.display_table(table_data, [:address, :destination])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -1,26 +1,26 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Matt Eldridge (<matt.eldridge@us.ibm.com>)
|
3
|
-
# © Copyright IBM Corporation 2014.
|
4
|
-
#
|
5
|
-
# LICENSE: Apache 2.0 (http://www.apache.org/licenses/)
|
6
|
-
#
|
7
|
-
|
8
|
-
require 'chef/knife/softlayer_base'
|
9
|
-
|
10
|
-
class Chef
|
11
|
-
class Knife
|
12
|
-
class SoftlayerImageList < Knife
|
13
|
-
|
14
|
-
include Knife::SoftlayerBase
|
15
|
-
|
16
|
-
banner 'knife softlayer image list'
|
17
|
-
|
18
|
-
def run
|
19
|
-
$stdout.sync = true
|
20
|
-
table_data = connection(:compute).images.map { |i| {:id => i.id, :name => i.name, :access => i.public? ? 'PUBLIC' : 'PRIVATE' } }
|
21
|
-
puts Formatador.display_table(table_data, [:id, :access, :name,])
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
1
|
+
#
|
2
|
+
# Author:: Matt Eldridge (<matt.eldridge@us.ibm.com>)
|
3
|
+
# © Copyright IBM Corporation 2014.
|
4
|
+
#
|
5
|
+
# LICENSE: Apache 2.0 (http://www.apache.org/licenses/)
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'chef/knife/softlayer_base'
|
9
|
+
|
10
|
+
class Chef
|
11
|
+
class Knife
|
12
|
+
class SoftlayerImageList < Knife
|
13
|
+
|
14
|
+
include Knife::SoftlayerBase
|
15
|
+
|
16
|
+
banner 'knife softlayer image list'
|
17
|
+
|
18
|
+
def run
|
19
|
+
$stdout.sync = true
|
20
|
+
table_data = connection(:compute).images.map { |i| {:id => i.id, :name => i.name, :access => i.public? ? 'PUBLIC' : 'PRIVATE' } }
|
21
|
+
puts Formatador.display_table(table_data, [:id, :access, :name,])
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,37 +1,37 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Matt Eldridge (<matt.eldridge@us.ibm.com>)
|
3
|
-
# © Copyright IBM Corporation 2014.
|
4
|
-
#
|
5
|
-
# LICENSE: Apache 2.0 (http://www.apache.org/licenses/)
|
6
|
-
#
|
7
|
-
|
8
|
-
require 'chef/knife/softlayer_base'
|
9
|
-
|
10
|
-
class Chef
|
11
|
-
class Knife
|
12
|
-
class SoftlayerKeyPairCreate < Knife
|
13
|
-
|
14
|
-
include Knife::SoftlayerBase
|
15
|
-
|
16
|
-
banner 'knife softlayer key pair create'
|
17
|
-
|
18
|
-
def run
|
19
|
-
$stdout.sync = true
|
20
|
-
opts = {
|
21
|
-
:label => ui.ask_question("Enter the label for this key pair: "),
|
22
|
-
:key => ui.ask("Enter path to the public key: ", lambda{ |answer| IO.read(answer) })
|
23
|
-
}
|
24
|
-
|
25
|
-
key_pair = connection(:compute).key_pairs.create(opts)
|
26
|
-
|
27
|
-
if !!key_pair
|
28
|
-
puts "#{ui.color("Key pair successfully created. Provisioning may take a few minutes to complete.", :green)}"
|
29
|
-
puts "#{ui.color("Key pair ID is: ", :green)} #{key_pair.id}"
|
30
|
-
else
|
31
|
-
puts "#{ui.color("Encountered a problem verifying key pair creation. Please try again.", :yellow)}"
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
1
|
+
#
|
2
|
+
# Author:: Matt Eldridge (<matt.eldridge@us.ibm.com>)
|
3
|
+
# © Copyright IBM Corporation 2014.
|
4
|
+
#
|
5
|
+
# LICENSE: Apache 2.0 (http://www.apache.org/licenses/)
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'chef/knife/softlayer_base'
|
9
|
+
|
10
|
+
class Chef
|
11
|
+
class Knife
|
12
|
+
class SoftlayerKeyPairCreate < Knife
|
13
|
+
|
14
|
+
include Knife::SoftlayerBase
|
15
|
+
|
16
|
+
banner 'knife softlayer key pair create'
|
17
|
+
|
18
|
+
def run
|
19
|
+
$stdout.sync = true
|
20
|
+
opts = {
|
21
|
+
:label => ui.ask_question("Enter the label for this key pair: "),
|
22
|
+
:key => ui.ask("Enter path to the public key: ", lambda{ |answer| IO.read(answer) })
|
23
|
+
}
|
24
|
+
|
25
|
+
key_pair = connection(:compute).key_pairs.create(opts)
|
26
|
+
|
27
|
+
if !!key_pair
|
28
|
+
puts "#{ui.color("Key pair successfully created. Provisioning may take a few minutes to complete.", :green)}"
|
29
|
+
puts "#{ui.color("Key pair ID is: ", :green)} #{key_pair.id}"
|
30
|
+
else
|
31
|
+
puts "#{ui.color("Encountered a problem verifying key pair creation. Please try again.", :yellow)}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -1,28 +1,28 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Matt Eldridge (<matt.eldridge@us.ibm.com>)
|
3
|
-
# © Copyright IBM Corporation 2014.
|
4
|
-
#
|
5
|
-
# LICENSE: Apache 2.0 (http://www.apache.org/licenses/)
|
6
|
-
#
|
7
|
-
|
8
|
-
require 'chef/knife/softlayer_base'
|
9
|
-
|
10
|
-
class Chef
|
11
|
-
class Knife
|
12
|
-
class SoftlayerKeyPairList < Knife
|
13
|
-
|
14
|
-
include Knife::SoftlayerBase
|
15
|
-
|
16
|
-
banner 'knife softlayer key pair list'
|
17
|
-
|
18
|
-
def run
|
19
|
-
$stdout.sync = true
|
20
|
-
table_data = connection(:compute).key_pairs.map do |kp|
|
21
|
-
{:id => kp.id, :label => kp.label, :create_date => kp.create_date, :modify_date => kp.modify_date }
|
22
|
-
end
|
23
|
-
puts Formatador.display_table(table_data, [:id, :label, :create_date, :modify_date])
|
24
|
-
end
|
25
|
-
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
1
|
+
#
|
2
|
+
# Author:: Matt Eldridge (<matt.eldridge@us.ibm.com>)
|
3
|
+
# © Copyright IBM Corporation 2014.
|
4
|
+
#
|
5
|
+
# LICENSE: Apache 2.0 (http://www.apache.org/licenses/)
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'chef/knife/softlayer_base'
|
9
|
+
|
10
|
+
class Chef
|
11
|
+
class Knife
|
12
|
+
class SoftlayerKeyPairList < Knife
|
13
|
+
|
14
|
+
include Knife::SoftlayerBase
|
15
|
+
|
16
|
+
banner 'knife softlayer key pair list'
|
17
|
+
|
18
|
+
def run
|
19
|
+
$stdout.sync = true
|
20
|
+
table_data = connection(:compute).key_pairs.map do |kp|
|
21
|
+
{:id => kp.id, :label => kp.label, :create_date => kp.create_date, :modify_date => kp.modify_date }
|
22
|
+
end
|
23
|
+
puts Formatador.display_table(table_data, [:id, :label, :create_date, :modify_date])
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Matt Eldridge (<matt.eldridge@us.ibm.com>)
|
3
|
-
# © Copyright IBM Corporation 2014.
|
4
|
-
#
|
5
|
-
# LICENSE: Apache 2.0 (http://www.apache.org/licenses/)
|
6
|
-
#
|
1
|
+
#
|
2
|
+
# Author:: Matt Eldridge (<matt.eldridge@us.ibm.com>)
|
3
|
+
# © Copyright IBM Corporation 2014.
|
4
|
+
#
|
5
|
+
# LICENSE: Apache 2.0 (http://www.apache.org/licenses/)
|
6
|
+
#
|
@@ -1,453 +1,459 @@
|
|
1
|
-
#
|
2
|
-
# Author:: Matt Eldridge (<matt.eldridge@us.ibm.com>)
|
3
|
-
# © Copyright IBM Corporation 2014.
|
4
|
-
#
|
5
|
-
# LICENSE: Apache 2.0 (http://www.apache.org/licenses/)
|
6
|
-
#
|
7
|
-
|
8
|
-
require 'chef/knife/softlayer_base'
|
9
|
-
|
10
|
-
class Chef
|
11
|
-
class Knife
|
12
|
-
class SoftlayerServerCreateError < StandardError; end
|
13
|
-
class SoftlayerServerCreate < Knife
|
14
|
-
|
15
|
-
attr_reader :cci
|
16
|
-
|
17
|
-
include Knife::SoftlayerBase
|
18
|
-
|
19
|
-
banner 'knife softlayer server create (options)'
|
20
|
-
|
21
|
-
option :flavor,
|
22
|
-
:long => '--flavor FLAVOR',
|
23
|
-
:short => '-f FLAVOR',
|
24
|
-
:description => 'Pre-configured packages of computing resources. See `knife softlayer flavor list` for details.'
|
25
|
-
|
26
|
-
option :hostname,
|
27
|
-
:long => '--hostname VALUE',
|
28
|
-
:short => '-H VALUE',
|
29
|
-
:description => 'The hostname SoftLayer will assign to the VM instance.'
|
30
|
-
|
31
|
-
option :domain,
|
32
|
-
:long => '--domain VALUE',
|
33
|
-
:short => '-D VALUE',
|
34
|
-
:description => 'The FQDN SoftLayer will assign to the VM instance.',
|
35
|
-
:default => Chef::Config[:knife][:softlayer_default_domain]
|
36
|
-
|
37
|
-
option :cores,
|
38
|
-
:long => '--cores VALUE',
|
39
|
-
:short => '-C VALUE',
|
40
|
-
:description => 'The number of virtual cores SoftLayer will assign to the VM instance.'
|
41
|
-
|
42
|
-
option :os_code,
|
43
|
-
:long => '--os-code VALUE',
|
44
|
-
:short => '-O VALUE',
|
45
|
-
:description => 'A valid SoftLayer operating system code. See `knife softlayer flavor list --all` for a list of valid codes.'
|
46
|
-
|
47
|
-
option :ram,
|
48
|
-
:long => '--ram VALUE',
|
49
|
-
:short => '-R VALUE',
|
50
|
-
:description => 'The number of virtual cores SoftLayer will assign to the VM instance.'
|
51
|
-
|
52
|
-
|
53
|
-
option :block_storage,
|
54
|
-
:long => '--block-storage VALUE',
|
55
|
-
:short => '-B VALUE',
|
56
|
-
:description => 'The size in GB of the block storage devices (disks) for this instance. Specify 1 - 5 entries in a comma separated list following the format "dev:size". Example: "0:25,2:500" would be a 25GB volume on device 0 (the root partition) and a 100GB volume on on device 2. [NOTE: SoftLayer VMs always reserve device 1 for a swap device.] ',
|
57
|
-
:proc => Proc.new { |devs| devs.split(',').map{ |dev| device, capacity = dev.split(':'); {"device"=>device, "diskImage"=>{"capacity"=>capacity}} } }
|
58
|
-
|
59
|
-
option :nic,
|
60
|
-
:long => '--network-interface-speed VALUE',
|
61
|
-
:short => '-n VALUE',
|
62
|
-
:description => 'The maximum speed of the public NIC available to the instance.',
|
63
|
-
:default => 10
|
64
|
-
|
65
|
-
option :bill_monthly,
|
66
|
-
:long => '--bill-monthly',
|
67
|
-
:description => 'Flag to bill monthly instead of hourly, minimum charge of one month.',
|
68
|
-
:boolean => true,
|
69
|
-
:default => false
|
70
|
-
|
71
|
-
option :vlan,
|
72
|
-
:long => '--vlan VLAN-ID',
|
73
|
-
:description => 'Internal SoftLayer ID of the public VLAN into which the compute instance should be placed.'
|
74
|
-
|
75
|
-
option :private_vlan,
|
76
|
-
:long => '--private-vlan VLAN-ID',
|
77
|
-
:description => 'Internal SoftLayer ID of the private VLAN into which the compute instance should be placed.'
|
78
|
-
|
79
|
-
option :image_id,
|
80
|
-
:long => '--image-id IMAGE-ID',
|
81
|
-
:description => 'Internal SoftLayer uuid specifying the image template from which the compute instance should be booted.'
|
82
|
-
|
83
|
-
option :private_network_only,
|
84
|
-
:long => '--private-network-only',
|
85
|
-
:description => 'Flag to be passed when the compute instance should have no public facing network interface.',
|
86
|
-
:boolean => true
|
87
|
-
|
88
|
-
option :use_private_network,
|
89
|
-
:long => '--use-private-network',
|
90
|
-
:description => 'Flag to be passwed when bootstrap is preferred over the private network.',
|
91
|
-
:boolean => true
|
92
|
-
|
93
|
-
option :from_file,
|
94
|
-
:long => '--from-file PATH',
|
95
|
-
:description => 'Path to JSON file containing arguments for provisoning and bootstrap.'
|
96
|
-
|
97
|
-
#option :single_tenant,
|
98
|
-
# :long => '--single-tenant',
|
99
|
-
# :description => 'Create a VM with a dedicated physical host.',
|
100
|
-
# :boolean => true,
|
101
|
-
# :default => false
|
102
|
-
|
103
|
-
option :local_storage,
|
104
|
-
:long => '--local-storage',
|
105
|
-
:description => 'Force local block storage instead of SAN storage.',
|
106
|
-
:boolean => true,
|
107
|
-
:default => false
|
108
|
-
|
109
|
-
option :datacenter,
|
110
|
-
:long => '--datacenter VALUE',
|
111
|
-
:description => 'Create a VM in a particular datacenter.',
|
112
|
-
:default => Chef::Config[:knife][:softlayer_default_datacenter]
|
113
|
-
|
114
|
-
option :tags,
|
115
|
-
:short => "-T T=V[,T=V,...]",
|
116
|
-
:long => "--tags Tag=Value[,Tag=Value...]",
|
117
|
-
:description => "The tags for this server",
|
118
|
-
:proc => Proc.new { |tags| tags.split(',') }
|
119
|
-
|
120
|
-
option :chef_node_name,
|
121
|
-
:short => "-N NAME",
|
122
|
-
:long => "--node-name NAME",
|
123
|
-
:description => "The Chef node name for your new node",
|
124
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:chef_node_name] = key }
|
125
|
-
|
126
|
-
option :ssh_user,
|
127
|
-
:short => "-x USERNAME",
|
128
|
-
:long => "--ssh-user USERNAME",
|
129
|
-
:description => "The ssh username",
|
130
|
-
:default => "root"
|
131
|
-
|
132
|
-
option :ssh_password,
|
133
|
-
:short => "-P PASSWORD",
|
134
|
-
:long => "--ssh-password PASSWORD",
|
135
|
-
:description => "The ssh password"
|
136
|
-
|
137
|
-
option :ssh_keys,
|
138
|
-
:short => "-S KEY",
|
139
|
-
:long => "--ssh-keys KEY",
|
140
|
-
:description => "The ssh keys for the SoftLayer Virtual Guest environment. Accepts a space separated list of integers.",
|
141
|
-
:proc => Proc.new { |ssh_keys| ssh_keys.split(' ').map { |k| {:id => k}} }
|
142
|
-
|
143
|
-
option :ssh_port,
|
144
|
-
:short => "-p PORT",
|
145
|
-
:long => "--ssh-port PORT",
|
146
|
-
:description => "The ssh port",
|
147
|
-
:default => "22",
|
148
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key }
|
149
|
-
|
150
|
-
option :ssh_gateway,
|
151
|
-
:short => "-w GATEWAY",
|
152
|
-
:long => "--ssh-gateway GATEWAY",
|
153
|
-
:description => "The ssh gateway server",
|
154
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key }
|
155
|
-
|
156
|
-
option :identity_file,
|
157
|
-
:short => "-i IDENTITY_FILE",
|
158
|
-
:long => "--identity-file IDENTITY_FILE",
|
159
|
-
:description => "The SSH identity file used for authentication"
|
160
|
-
|
161
|
-
option :prerelease,
|
162
|
-
:long => "--prerelease",
|
163
|
-
:description => "Install the pre-release chef gems"
|
164
|
-
|
165
|
-
option :bootstrap_version,
|
166
|
-
:long => "--bootstrap-version VERSION",
|
167
|
-
:description => "The version of Chef to install",
|
168
|
-
:proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v }
|
169
|
-
|
170
|
-
option :bootstrap_proxy,
|
171
|
-
:long => "--bootstrap-proxy PROXY_URL",
|
172
|
-
:description => "The proxy server for the node being bootstrapped",
|
173
|
-
:proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p }
|
174
|
-
|
175
|
-
option :distro,
|
176
|
-
:short => "-d DISTRO",
|
177
|
-
:long => "--distro DISTRO",
|
178
|
-
:description => "Bootstrap a distro using a template; default is 'chef-full'",
|
179
|
-
:proc => Proc.new { |d| Chef::Config[:knife][:distro] = d },
|
180
|
-
:default => "chef-full"
|
181
|
-
|
182
|
-
option :template_file,
|
183
|
-
:long => "--template-file TEMPLATE",
|
184
|
-
:description => "Full path to location of template to use",
|
185
|
-
:proc => Proc.new { |t| Chef::Config[:knife][:template_file] = t },
|
186
|
-
:default => false
|
187
|
-
|
188
|
-
option :run_list,
|
189
|
-
:short => "-r RUN_LIST",
|
190
|
-
:long => "--run-list RUN_LIST",
|
191
|
-
:description => "Comma separated list of roles/recipes to apply",
|
192
|
-
:proc => lambda { |o| o.split(/[\s,]+/) }
|
193
|
-
|
194
|
-
option :secret,
|
195
|
-
:short => "-s SECRET",
|
196
|
-
:long => "--secret ",
|
197
|
-
:description => "The secret key to use to encrypt data bag item values",
|
198
|
-
:proc => lambda { |s| Chef::Config[:knife][:secret] = s }
|
199
|
-
|
200
|
-
option :secret_file,
|
201
|
-
:long => "--secret-file SECRET_FILE",
|
202
|
-
:description => "A file containing the secret key to use to encrypt data bag item values",
|
203
|
-
:proc => lambda { |sf| Chef::Config[:knife][:secret_file] = sf }
|
204
|
-
|
205
|
-
option :json_attributes,
|
206
|
-
:short => "-j JSON",
|
207
|
-
:long => "--json-attributes JSON",
|
208
|
-
:description => "A JSON string to be added to the first run of chef-client",
|
209
|
-
:proc => lambda { |o| JSON.parse(o) }
|
210
|
-
|
211
|
-
option :host_key_verify,
|
212
|
-
:long => "--[no-]host-key-verify",
|
213
|
-
:description => "Verify host key, enabled by default.",
|
214
|
-
:boolean => true,
|
215
|
-
:default => true
|
216
|
-
|
217
|
-
option :bootstrap_protocol,
|
218
|
-
:long => "--bootstrap-protocol protocol",
|
219
|
-
:description => "protocol to bootstrap windows servers. options: winrm/ssh",
|
220
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:bootstrap_protocol] = key },
|
221
|
-
:default => nil
|
222
|
-
|
223
|
-
option :fqdn,
|
224
|
-
:long => "--fqdn FQDN",
|
225
|
-
:description => "Pre-defined FQDN",
|
226
|
-
:proc => Proc.new { |key| Chef::Config[:knife][:fqdn] = key },
|
227
|
-
:default => nil
|
228
|
-
|
229
|
-
option :assign_global_ip,
|
230
|
-
:long => "--assign-global-ip IpAdress",
|
231
|
-
:description => "Assign an existing SoftLayer Global IP address.",
|
232
|
-
:default => nil
|
233
|
-
|
234
|
-
option :new_global_ip,
|
235
|
-
:long => "--new-global-ip VERSION",
|
236
|
-
:description => "Order a new SoftLayer Global IP address and assign it to the instance."
|
237
|
-
|
238
|
-
option :hint,
|
239
|
-
:long => "--hint HINT_NAME[=HINT_FILE]",
|
240
|
-
:description => "Specify Ohai Hint to be set on the bootstrap target. Use multiple --hint options to specify multiple hints.",
|
241
|
-
:proc => Proc.new { |h|
|
242
|
-
Chef::Config[:knife][:hints] ||= {}
|
243
|
-
name, path = h.split("=")
|
244
|
-
Chef::Config[:knife][:hints][name] = path ? JSON.parse(::File.read(path)) : Hash.new
|
245
|
-
}
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
#
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
# TODO:
|
264
|
-
#
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
# TODO: create a pre-launch method for clean up tasks.
|
269
|
-
# TODO: create a pre-launch method for clean up tasks.
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
:
|
278
|
-
:
|
279
|
-
:
|
280
|
-
:
|
281
|
-
:
|
282
|
-
:
|
283
|
-
:
|
284
|
-
:
|
285
|
-
:
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
if config[:
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
global_ip
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
bootstrap
|
380
|
-
|
381
|
-
bootstrap.
|
382
|
-
bootstrap.config[:
|
383
|
-
bootstrap.config[:
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
bootstrap
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
bootstrap.config[:
|
397
|
-
bootstrap.config[:
|
398
|
-
bootstrap.config[:
|
399
|
-
bootstrap.config[:
|
400
|
-
bootstrap.config[:
|
401
|
-
bootstrap.config[:
|
402
|
-
bootstrap.config[:
|
403
|
-
|
404
|
-
|
405
|
-
bootstrap
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
end
|
452
|
-
|
453
|
-
|
1
|
+
#
|
2
|
+
# Author:: Matt Eldridge (<matt.eldridge@us.ibm.com>)
|
3
|
+
# © Copyright IBM Corporation 2014.
|
4
|
+
#
|
5
|
+
# LICENSE: Apache 2.0 (http://www.apache.org/licenses/)
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'chef/knife/softlayer_base'
|
9
|
+
|
10
|
+
class Chef
|
11
|
+
class Knife
|
12
|
+
class SoftlayerServerCreateError < StandardError; end
|
13
|
+
class SoftlayerServerCreate < Knife
|
14
|
+
|
15
|
+
attr_reader :cci
|
16
|
+
|
17
|
+
include Knife::SoftlayerBase
|
18
|
+
|
19
|
+
banner 'knife softlayer server create (options)'
|
20
|
+
|
21
|
+
option :flavor,
|
22
|
+
:long => '--flavor FLAVOR',
|
23
|
+
:short => '-f FLAVOR',
|
24
|
+
:description => 'Pre-configured packages of computing resources. See `knife softlayer flavor list` for details.'
|
25
|
+
|
26
|
+
option :hostname,
|
27
|
+
:long => '--hostname VALUE',
|
28
|
+
:short => '-H VALUE',
|
29
|
+
:description => 'The hostname SoftLayer will assign to the VM instance.'
|
30
|
+
|
31
|
+
option :domain,
|
32
|
+
:long => '--domain VALUE',
|
33
|
+
:short => '-D VALUE',
|
34
|
+
:description => 'The FQDN SoftLayer will assign to the VM instance.',
|
35
|
+
:default => Chef::Config[:knife][:softlayer_default_domain]
|
36
|
+
|
37
|
+
option :cores,
|
38
|
+
:long => '--cores VALUE',
|
39
|
+
:short => '-C VALUE',
|
40
|
+
:description => 'The number of virtual cores SoftLayer will assign to the VM instance.'
|
41
|
+
|
42
|
+
option :os_code,
|
43
|
+
:long => '--os-code VALUE',
|
44
|
+
:short => '-O VALUE',
|
45
|
+
:description => 'A valid SoftLayer operating system code. See `knife softlayer flavor list --all` for a list of valid codes.'
|
46
|
+
|
47
|
+
option :ram,
|
48
|
+
:long => '--ram VALUE',
|
49
|
+
:short => '-R VALUE',
|
50
|
+
:description => 'The number of virtual cores SoftLayer will assign to the VM instance.'
|
51
|
+
|
52
|
+
|
53
|
+
option :block_storage,
|
54
|
+
:long => '--block-storage VALUE',
|
55
|
+
:short => '-B VALUE',
|
56
|
+
:description => 'The size in GB of the block storage devices (disks) for this instance. Specify 1 - 5 entries in a comma separated list following the format "dev:size". Example: "0:25,2:500" would be a 25GB volume on device 0 (the root partition) and a 100GB volume on on device 2. [NOTE: SoftLayer VMs always reserve device 1 for a swap device.] ',
|
57
|
+
:proc => Proc.new { |devs| devs.split(',').map{ |dev| device, capacity = dev.split(':'); {"device"=>device, "diskImage"=>{"capacity"=>capacity}} } }
|
58
|
+
|
59
|
+
option :nic,
|
60
|
+
:long => '--network-interface-speed VALUE',
|
61
|
+
:short => '-n VALUE',
|
62
|
+
:description => 'The maximum speed of the public NIC available to the instance.',
|
63
|
+
:default => 10
|
64
|
+
|
65
|
+
option :bill_monthly,
|
66
|
+
:long => '--bill-monthly',
|
67
|
+
:description => 'Flag to bill monthly instead of hourly, minimum charge of one month.',
|
68
|
+
:boolean => true,
|
69
|
+
:default => false
|
70
|
+
|
71
|
+
option :vlan,
|
72
|
+
:long => '--vlan VLAN-ID',
|
73
|
+
:description => 'Internal SoftLayer ID of the public VLAN into which the compute instance should be placed.'
|
74
|
+
|
75
|
+
option :private_vlan,
|
76
|
+
:long => '--private-vlan VLAN-ID',
|
77
|
+
:description => 'Internal SoftLayer ID of the private VLAN into which the compute instance should be placed.'
|
78
|
+
|
79
|
+
option :image_id,
|
80
|
+
:long => '--image-id IMAGE-ID',
|
81
|
+
:description => 'Internal SoftLayer uuid specifying the image template from which the compute instance should be booted.'
|
82
|
+
|
83
|
+
option :private_network_only,
|
84
|
+
:long => '--private-network-only',
|
85
|
+
:description => 'Flag to be passed when the compute instance should have no public facing network interface.',
|
86
|
+
:boolean => true
|
87
|
+
|
88
|
+
option :use_private_network,
|
89
|
+
:long => '--use-private-network',
|
90
|
+
:description => 'Flag to be passwed when bootstrap is preferred over the private network.',
|
91
|
+
:boolean => true
|
92
|
+
|
93
|
+
option :from_file,
|
94
|
+
:long => '--from-file PATH',
|
95
|
+
:description => 'Path to JSON file containing arguments for provisoning and bootstrap.'
|
96
|
+
|
97
|
+
#option :single_tenant,
|
98
|
+
# :long => '--single-tenant',
|
99
|
+
# :description => 'Create a VM with a dedicated physical host.',
|
100
|
+
# :boolean => true,
|
101
|
+
# :default => false
|
102
|
+
|
103
|
+
option :local_storage,
|
104
|
+
:long => '--local-storage',
|
105
|
+
:description => 'Force local block storage instead of SAN storage.',
|
106
|
+
:boolean => true,
|
107
|
+
:default => false
|
108
|
+
|
109
|
+
option :datacenter,
|
110
|
+
:long => '--datacenter VALUE',
|
111
|
+
:description => 'Create a VM in a particular datacenter.',
|
112
|
+
:default => Chef::Config[:knife][:softlayer_default_datacenter]
|
113
|
+
|
114
|
+
option :tags,
|
115
|
+
:short => "-T T=V[,T=V,...]",
|
116
|
+
:long => "--tags Tag=Value[,Tag=Value...]",
|
117
|
+
:description => "The tags for this server",
|
118
|
+
:proc => Proc.new { |tags| tags.split(',') }
|
119
|
+
|
120
|
+
option :chef_node_name,
|
121
|
+
:short => "-N NAME",
|
122
|
+
:long => "--node-name NAME",
|
123
|
+
:description => "The Chef node name for your new node",
|
124
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:chef_node_name] = key }
|
125
|
+
|
126
|
+
option :ssh_user,
|
127
|
+
:short => "-x USERNAME",
|
128
|
+
:long => "--ssh-user USERNAME",
|
129
|
+
:description => "The ssh username",
|
130
|
+
:default => "root"
|
131
|
+
|
132
|
+
option :ssh_password,
|
133
|
+
:short => "-P PASSWORD",
|
134
|
+
:long => "--ssh-password PASSWORD",
|
135
|
+
:description => "The ssh password"
|
136
|
+
|
137
|
+
option :ssh_keys,
|
138
|
+
:short => "-S KEY",
|
139
|
+
:long => "--ssh-keys KEY",
|
140
|
+
:description => "The ssh keys for the SoftLayer Virtual Guest environment. Accepts a space separated list of integers.",
|
141
|
+
:proc => Proc.new { |ssh_keys| ssh_keys.split(' ').map { |k| {:id => k}} }
|
142
|
+
|
143
|
+
option :ssh_port,
|
144
|
+
:short => "-p PORT",
|
145
|
+
:long => "--ssh-port PORT",
|
146
|
+
:description => "The ssh port",
|
147
|
+
:default => "22",
|
148
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key }
|
149
|
+
|
150
|
+
option :ssh_gateway,
|
151
|
+
:short => "-w GATEWAY",
|
152
|
+
:long => "--ssh-gateway GATEWAY",
|
153
|
+
:description => "The ssh gateway server",
|
154
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key }
|
155
|
+
|
156
|
+
option :identity_file,
|
157
|
+
:short => "-i IDENTITY_FILE",
|
158
|
+
:long => "--identity-file IDENTITY_FILE",
|
159
|
+
:description => "The SSH identity file used for authentication"
|
160
|
+
|
161
|
+
option :prerelease,
|
162
|
+
:long => "--prerelease",
|
163
|
+
:description => "Install the pre-release chef gems"
|
164
|
+
|
165
|
+
option :bootstrap_version,
|
166
|
+
:long => "--bootstrap-version VERSION",
|
167
|
+
:description => "The version of Chef to install",
|
168
|
+
:proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v }
|
169
|
+
|
170
|
+
option :bootstrap_proxy,
|
171
|
+
:long => "--bootstrap-proxy PROXY_URL",
|
172
|
+
:description => "The proxy server for the node being bootstrapped",
|
173
|
+
:proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p }
|
174
|
+
|
175
|
+
option :distro,
|
176
|
+
:short => "-d DISTRO",
|
177
|
+
:long => "--distro DISTRO",
|
178
|
+
:description => "Bootstrap a distro using a template; default is 'chef-full'",
|
179
|
+
:proc => Proc.new { |d| Chef::Config[:knife][:distro] = d },
|
180
|
+
:default => "chef-full"
|
181
|
+
|
182
|
+
option :template_file,
|
183
|
+
:long => "--template-file TEMPLATE",
|
184
|
+
:description => "Full path to location of template to use",
|
185
|
+
:proc => Proc.new { |t| Chef::Config[:knife][:template_file] = t },
|
186
|
+
:default => false
|
187
|
+
|
188
|
+
option :run_list,
|
189
|
+
:short => "-r RUN_LIST",
|
190
|
+
:long => "--run-list RUN_LIST",
|
191
|
+
:description => "Comma separated list of roles/recipes to apply",
|
192
|
+
:proc => lambda { |o| o.split(/[\s,]+/) }
|
193
|
+
|
194
|
+
option :secret,
|
195
|
+
:short => "-s SECRET",
|
196
|
+
:long => "--secret ",
|
197
|
+
:description => "The secret key to use to encrypt data bag item values",
|
198
|
+
:proc => lambda { |s| Chef::Config[:knife][:secret] = s }
|
199
|
+
|
200
|
+
option :secret_file,
|
201
|
+
:long => "--secret-file SECRET_FILE",
|
202
|
+
:description => "A file containing the secret key to use to encrypt data bag item values",
|
203
|
+
:proc => lambda { |sf| Chef::Config[:knife][:secret_file] = sf }
|
204
|
+
|
205
|
+
option :json_attributes,
|
206
|
+
:short => "-j JSON",
|
207
|
+
:long => "--json-attributes JSON",
|
208
|
+
:description => "A JSON string to be added to the first run of chef-client",
|
209
|
+
:proc => lambda { |o| JSON.parse(o) }
|
210
|
+
|
211
|
+
option :host_key_verify,
|
212
|
+
:long => "--[no-]host-key-verify",
|
213
|
+
:description => "Verify host key, enabled by default.",
|
214
|
+
:boolean => true,
|
215
|
+
:default => true
|
216
|
+
|
217
|
+
option :bootstrap_protocol,
|
218
|
+
:long => "--bootstrap-protocol protocol",
|
219
|
+
:description => "protocol to bootstrap windows servers. options: winrm/ssh",
|
220
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:bootstrap_protocol] = key },
|
221
|
+
:default => nil
|
222
|
+
|
223
|
+
option :fqdn,
|
224
|
+
:long => "--fqdn FQDN",
|
225
|
+
:description => "Pre-defined FQDN",
|
226
|
+
:proc => Proc.new { |key| Chef::Config[:knife][:fqdn] = key },
|
227
|
+
:default => nil
|
228
|
+
|
229
|
+
option :assign_global_ip,
|
230
|
+
:long => "--assign-global-ip IpAdress",
|
231
|
+
:description => "Assign an existing SoftLayer Global IP address.",
|
232
|
+
:default => nil
|
233
|
+
|
234
|
+
option :new_global_ip,
|
235
|
+
:long => "--new-global-ip VERSION",
|
236
|
+
:description => "Order a new SoftLayer Global IP address and assign it to the instance."
|
237
|
+
|
238
|
+
option :hint,
|
239
|
+
:long => "--hint HINT_NAME[=HINT_FILE]",
|
240
|
+
:description => "Specify Ohai Hint to be set on the bootstrap target. Use multiple --hint options to specify multiple hints.",
|
241
|
+
:proc => Proc.new { |h|
|
242
|
+
Chef::Config[:knife][:hints] ||= {}
|
243
|
+
name, path = h.split("=")
|
244
|
+
Chef::Config[:knife][:hints][name] = path ? JSON.parse(::File.read(path)) : Hash.new
|
245
|
+
}
|
246
|
+
|
247
|
+
option :user_data,
|
248
|
+
:short => "-u USERDATA",
|
249
|
+
:long => "--user-data USERDATA",
|
250
|
+
:description => "Optional user data to pass on to SoftLayer compute instance"
|
251
|
+
|
252
|
+
require 'chef/knife/bootstrap'
|
253
|
+
# Make the base bootstrap options available on topo bootstrap
|
254
|
+
self.options = (Chef::Knife::Bootstrap.options).merge(self.options)
|
255
|
+
|
256
|
+
##
|
257
|
+
# Run the procedure to create a SoftLayer VM and bootstrap it.
|
258
|
+
# @return [nil]
|
259
|
+
def run
|
260
|
+
$stdout.sync = true
|
261
|
+
config.merge!(slurp_from_file(config[:from_file])) if config[:from_file]
|
262
|
+
|
263
|
+
# TODO: Windows support.
|
264
|
+
raise SoftlayerServerCreateError, "#{ui.color("Windows VMs not currently supported.", :red)}" if config[:os_code] =~ /^WIN_/
|
265
|
+
raise SoftlayerServerCreateError, "#{ui.color("identity file (-i) option is incompatible with password (-P) option required.", :red)}" if !!config[:identity_file] and !!config[:ssh_password]
|
266
|
+
raise SoftlayerServerCreateError, "#{ui.color("--new-global-ip value must be 'v4' or 'v6'.", :red)}" if config[:new_global_ip] and !config[:new_global_ip].to_s.match(/^v[4,6]$/i)
|
267
|
+
|
268
|
+
# TODO: create a pre-launch method for clean up tasks.
|
269
|
+
# TODO: create a pre-launch method for clean up tasks.
|
270
|
+
config[:vlan] = config[:vlan].to_i if config[:vlan]
|
271
|
+
config[:private_vlan] = config[:private_vlan].to_i if config[:private_vlan]
|
272
|
+
Fog.credentials[:private_key_path] = config[:identity_file] if config[:identity_file]
|
273
|
+
# TODO: create a pre-launch method for clean up tasks.
|
274
|
+
# TODO: create a pre-launch method for clean up tasks.
|
275
|
+
|
276
|
+
opts = {
|
277
|
+
:flavor => :flavor_id,
|
278
|
+
:hostname => :name,
|
279
|
+
:domain => nil,
|
280
|
+
:cores => :cpu,
|
281
|
+
:os_code => nil,
|
282
|
+
:ram => nil,
|
283
|
+
:block_storage => :disk,
|
284
|
+
:local_storage => :ephemeral_storage,
|
285
|
+
:datacenter => nil,
|
286
|
+
:ssh_keys => :key_pairs,
|
287
|
+
:vlan => nil,
|
288
|
+
:private_vlan => nil,
|
289
|
+
:image_id => nil,
|
290
|
+
:private_network_only => nil,
|
291
|
+
#:tags => nil,
|
292
|
+
:user_data => nil
|
293
|
+
}
|
294
|
+
|
295
|
+
|
296
|
+
opts.keys.each do |opt|
|
297
|
+
if opts[opt].nil?
|
298
|
+
opts[opt] = config[opt]
|
299
|
+
else
|
300
|
+
opts[opts.delete(opt)] = config[opt] # clever shit like this is why I like programming :-]
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
# FIXME: make the above deal with nested opts and get rid of this one-off
|
305
|
+
opts[:network_components] = [ {:speed => config[:nic]} ] if !!config[:nic]
|
306
|
+
|
307
|
+
opts.delete_if { |k,v| v.nil? }
|
308
|
+
|
309
|
+
puts ui.color("Launching SoftLayer VM, this may take a few minutes.", :green)
|
310
|
+
instance = connection.servers.create(opts)
|
311
|
+
if config[:private_network_only] || config[:use_private_network]
|
312
|
+
instance.ssh_ip_address = Proc.new {|server| server.private_ip_address }
|
313
|
+
end
|
314
|
+
progress Proc.new { instance.wait_for { ready? and sshable? } }
|
315
|
+
putc("\n")
|
316
|
+
|
317
|
+
if config[:tags]
|
318
|
+
puts ui.color("Applying tags to SoftLayer instance.", :green)
|
319
|
+
progress Proc.new { instance.add_tags(config[:tags]) }
|
320
|
+
putc("\n")
|
321
|
+
end
|
322
|
+
|
323
|
+
|
324
|
+
if config[:new_global_ip] || config[:assign_global_ip]
|
325
|
+
if config[:new_global_ip] # <— the value of this will be v4 or v6
|
326
|
+
begin
|
327
|
+
puts ui.color('Provisioning new Global IP' + config[:new_global_ip].downcase + ', this may take a few minutes.', :green)
|
328
|
+
create_global_ip = Proc.new do
|
329
|
+
existing_records = connection(:network).get_global_ip_records.body
|
330
|
+
connection(:network).send('create_new_global_ip' + config[:new_global_ip].downcase) or raise SoftlayerServerCreateError, "Unable to create new Global IP Address.";
|
331
|
+
sleep 20 # if we look for the new record too quickly it won't be there yet...
|
332
|
+
new_record_global_id = (connection(:network).get_global_ip_records.body - existing_records).reduce['id']
|
333
|
+
connection(:network).ips.select { |ip| ip.global_id == new_record_global_id }.reduce
|
334
|
+
end
|
335
|
+
global_ip = progress(create_global_ip) or raise SoftlayerServerCreateError, "Error encountered creating Global IP Address."
|
336
|
+
puts ui.color('Global IP Address successfully created.', :green)
|
337
|
+
rescue SoftlayerServerCreateError => e
|
338
|
+
puts ui.color('We have encountered a problem ordering the requested global IP. The transaction may not have completed.', :red)
|
339
|
+
puts ui.color(e.message, :yellow)
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
if config[:assign_global_ip]
|
344
|
+
case config[:assign_global_ip].to_s
|
345
|
+
#ipv4
|
346
|
+
when /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/
|
347
|
+
global_ip = connection(:network).ips.by_address(config[:assign_global_ip])
|
348
|
+
#ipv6
|
349
|
+
when /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/
|
350
|
+
global_ip = connection(:network).ips.by_address(config[:assign_global_ip])
|
351
|
+
else
|
352
|
+
raise SoftlayerServerCreateError, "--assign-global-ip value must be valid IPv4 or IPv6 address"
|
353
|
+
end
|
354
|
+
global_ip or raise SoftlayerServerCreateError, "Global IP address not found. Please check the address or id supplied and try again."
|
355
|
+
global_ip.reload
|
356
|
+
end
|
357
|
+
|
358
|
+
route_global_ip = Proc.new do
|
359
|
+
puts ui.color('Routing Global IP Address to Instance.', :green)
|
360
|
+
global_ip.route(connection(:network).ips.by_address(instance.public_ip_address)) or raise SoftlayerServerCreateError, "Global IP address failed to route."
|
361
|
+
puts ui.color('Global IP Address has been assigned.', :green)
|
362
|
+
puts ui.color('Global IP Address will not function without networking rules on the endpoint operating system. See http://knowledgelayer.softlayer.com/learning/global-ip-addresses for details.', :yellow)
|
363
|
+
end
|
364
|
+
progress(route_global_ip)
|
365
|
+
|
366
|
+
end
|
367
|
+
|
368
|
+
puts ui.color('Bootstrapping Chef node, this may take a few minutes.', :green)
|
369
|
+
linux_bootstrap(instance).run
|
370
|
+
|
371
|
+
puts ui.color("Applying tags to Chef node.", :green)
|
372
|
+
progress apply_tags(instance)
|
373
|
+
|
374
|
+
end
|
375
|
+
|
376
|
+
# @param [Hash] instance
|
377
|
+
# @return [Chef::Knife::Bootstrap]
|
378
|
+
def linux_bootstrap(instance)
|
379
|
+
bootstrap = Chef::Knife::Bootstrap.new
|
380
|
+
instance.ssh_ip_address = instance.private_ip_address if config[:private_network_only]
|
381
|
+
bootstrap.name_args = [instance.ssh_ip_address]
|
382
|
+
bootstrap.config[:ssh_user] = config[:ssh_user]
|
383
|
+
bootstrap.config[:ssh_password] = config[:ssh_password] if config[:ssh_password]
|
384
|
+
bootstrap.config[:identity_file] = config[:identity_file] if config[:identity_file]
|
385
|
+
bootstrap.config[:ssh_port] = config[:ssh_port]
|
386
|
+
bootstrap.config[:ssh_gateway] = config[:ssh_gateway]
|
387
|
+
bootstrap.config[:chef_node_name] = locate_config_value(:chef_node_name) || instance.id
|
388
|
+
bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
|
389
|
+
bootstrap.config[:host_key_verify] = config[:host_key_verify]
|
390
|
+
shared_bootstrap(bootstrap)
|
391
|
+
end
|
392
|
+
|
393
|
+
# @param [Chef::Knife::Bootstrap] bootstrap
|
394
|
+
# @return [Chef::Knife::Bootstrap]
|
395
|
+
def shared_bootstrap(bootstrap)
|
396
|
+
bootstrap.config[:run_list] = config[:run_list]
|
397
|
+
bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
|
398
|
+
bootstrap.config[:distro] = locate_config_value(:distro)
|
399
|
+
bootstrap.config[:template_file] = locate_config_value(:template_file)
|
400
|
+
bootstrap.config[:environment] = locate_config_value(:environment)
|
401
|
+
bootstrap.config[:prerelease] = config[:prerelease]
|
402
|
+
bootstrap.config[:first_boot_attributes] = locate_config_value(:json_attributes) || {}
|
403
|
+
bootstrap.config[:encrypted_data_bag_secret] = locate_config_value(:encrypted_data_bag_secret)
|
404
|
+
bootstrap.config[:encrypted_data_bag_secret_file] = locate_config_value(:encrypted_data_bag_secret_file)
|
405
|
+
bootstrap.config[:secret] = locate_config_value(:secret)
|
406
|
+
bootstrap.config[:secret_file] = locate_config_value(:secret_file)
|
407
|
+
bootstrap.config[:tags] = locate_config_value(:tags)
|
408
|
+
bootstrap.config[:fqdn] = locate_config_value(:fqdn)
|
409
|
+
Chef::Config[:knife][:hints] ||= {}
|
410
|
+
Chef::Config[:knife][:hints]['softlayer'] ||= {}
|
411
|
+
bootstrap
|
412
|
+
end
|
413
|
+
|
414
|
+
def windows_bootstrap(server, fqdn)
|
415
|
+
# TODO: Windows support....
|
416
|
+
end
|
417
|
+
|
418
|
+
def progress(proc)
|
419
|
+
t = Thread.new { Thread.current[:output] = proc.call }
|
420
|
+
i = 0
|
421
|
+
while t.alive?
|
422
|
+
sleep 0.5
|
423
|
+
putc('.')
|
424
|
+
i += 1
|
425
|
+
putc("\n") if i == 76
|
426
|
+
i = 0 if i == 76
|
427
|
+
end
|
428
|
+
putc("\n")
|
429
|
+
t.join
|
430
|
+
t[:output]
|
431
|
+
end
|
432
|
+
|
433
|
+
def slurp_from_file(path)
|
434
|
+
args = JSON.parse(IO.read(path))
|
435
|
+
args.keys.each { |key| args[key.gsub('-', '_').to_sym] = args.delete(key) }
|
436
|
+
# TODO: Something less ugly than the empty rescue block below. The :proc Procs/Lambdas aren't idempotent...
|
437
|
+
args.keys.each { |key| begin; args[key] = options[key][:proc] ? options[key][:proc].call(args[key]) : args[key]; rescue; end }
|
438
|
+
args
|
439
|
+
end
|
440
|
+
|
441
|
+
def apply_tags(instance)
|
442
|
+
Proc.new do
|
443
|
+
chef = Chef::Search::Query.new
|
444
|
+
chef.search('node', "name:#{locate_config_value(:chef_node_name) || instance.id}") do |n|
|
445
|
+
config[:tags] = [] if config[:tags].nil? # we're going to tag every Chef node with the SL id no matter what
|
446
|
+
config[:tags] << "slid=#{instance.id}"
|
447
|
+
config[:tags].each do |tag|
|
448
|
+
n.tag(tag)
|
449
|
+
end
|
450
|
+
n.save
|
451
|
+
end
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
end
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
|