knife-softlayer 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,177 @@
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'
9
+
10
+ class Chef
11
+ class Knife
12
+ module SoftlayerFlavorBase
13
+
14
+ ##
15
+ # Return the flavors table or the options table.
16
+ # @param [Boolean] show_all
17
+ # @return [Hash]
18
+ def table_info(show_all=false)
19
+ if show_all
20
+ options_table
21
+ else
22
+ flavors_table
23
+ end
24
+ end
25
+
26
+ ##
27
+ # Build table of all VM configuration options.
28
+ # @return [Hash]
29
+ def options_table
30
+ columns = [
31
+ ui.color("| CORES", :bold, :white, :green),
32
+ ui.color("| RAM", :bold, :white, :green),
33
+ ui.color("| DISK", :bold, :white, :green),
34
+ ui.color("| OS", :bold, :white, :green),
35
+ ui.color("| NETWORK [MBS]", :bold, :white, :green),
36
+ ui.color("| DATACENTER", :bold, :white, :green)
37
+ ]
38
+
39
+ opts = connection.getCreateObjectOptions
40
+ cpu = opts['processors']
41
+ ram = opts['memory']
42
+ disk = opts['blockDevices'].sort_by{|d| d['itemPrice']['item']['description'] unless d['itemPrice'].nil? }
43
+ os = opts['operatingSystems']
44
+ net = opts['networkComponents']
45
+ datacenter = opts['datacenters']
46
+
47
+ i = 0
48
+ until i >= opts.keys.map{|key| opts[key].count }.sort.last do
49
+ columns << (cpu[i].nil? ? '| ' : '| ' + cpu[i]['itemPrice']['item']['description'])
50
+ columns << (ram[i].nil? ? '| ' : '| ' + ram[i]['template']['maxMemory'].to_s + " [#{ram[i]['itemPrice']['item']['description']}]")
51
+ columns << (disk[i].nil? ? '| ' : '| ' + disk[i]['itemPrice']['item']['description'])
52
+ columns << (os[i].nil? ? '| ' : '| ' + os[i]['template']['operatingSystemReferenceCode'])
53
+ columns << (net[i].nil? ? '| ' : '| ' + net[i]['template']['networkComponents'].first['maxSpeed'].to_s)
54
+ columns << (datacenter[i].nil? ? '| ' : '| ' + datacenter[i]['template']['datacenter']['name'])
55
+ i+=1
56
+ end
57
+ columns
58
+ end
59
+
60
+ ##
61
+ # Build the VM "flavor" table.
62
+ # @return [Hash]
63
+ def flavors_table
64
+ # We don't have "flavors" actually, you can just pick from the menu.
65
+ # Let's shim in place the standard openstack flavors so people can get started without making loads of decisions.
66
+
67
+ columns = [
68
+ ui.color("| FLAVOR", :bold, :green),
69
+ ui.color("| CORES", :bold, :green),
70
+ ui.color("| RAM", :bold, :green),
71
+ ui.color("| DISK", :bold, :green)
72
+ ]
73
+
74
+ # tiny
75
+ columns << "| tiny"
76
+ columns << "| 1"
77
+ columns << "| 1024"
78
+ columns << "| 25GB [LOCAL]"
79
+
80
+ # small
81
+ columns << "| small"
82
+ columns << "| 2"
83
+ columns << "| 2048"
84
+ columns << "| 100GB [LOCAL]"
85
+
86
+
87
+ # medium
88
+ columns << "| medium"
89
+ columns << "| 4"
90
+ columns << "| 4096"
91
+ columns << "| 150GB [LOCAL]"
92
+
93
+
94
+ # large
95
+ columns << "| large"
96
+ columns << "| 8"
97
+ columns << "| 8192"
98
+ columns << "| 200GB [LOCAL]"
99
+
100
+
101
+ # xlarge
102
+ columns << "| xlarge"
103
+ columns << "| 16"
104
+ columns << "| 16384"
105
+ columns << "| 300GB [LOCAL]"
106
+ end
107
+
108
+ def self.load_flavor(flavor)
109
+ self.send(flavor.to_s)
110
+ end
111
+
112
+
113
+ private
114
+
115
+ ##
116
+ # Set options for a "tiny" instance.
117
+ # @return [Hash]
118
+ def self.tiny
119
+ {
120
+ 'startCpus' => 1,
121
+ 'maxMemory' => 1024,
122
+ 'localDiskFlag' => true,
123
+ 'blockDevices' => [{'device' => 0, 'diskImage' => {'capacity' => 25 } }]
124
+ }
125
+ end
126
+
127
+ ##
128
+ # Set options for a "small" instance.
129
+ # @return [Hash]
130
+ def self.small
131
+ {
132
+ 'startCpus' => 2,
133
+ 'maxMemory' => 2048,
134
+ 'localDiskFlag' => true,
135
+ 'blockDevices' => [{'device' => 0, 'diskImage' => {'capacity' => 100 } }]
136
+ }
137
+ end
138
+
139
+ ##
140
+ # Set options for a "medium" instance.
141
+ # @return [Hash]
142
+ def self.medium
143
+ {
144
+ 'startCpus' => 4,
145
+ 'maxMemory' => 4096,
146
+ 'localDiskFlag' => true,
147
+ 'blockDevices' => [{'device' => 0, 'diskImage' => {'capacity' => 25 } },{'device' => 2, 'diskImage' => {'capacity' => 150 } }]
148
+ }
149
+ end
150
+
151
+ ##
152
+ # Set options for a "large" instance.
153
+ # @return [Hash]
154
+ def self.large
155
+ {
156
+ 'startCpus' => 8,
157
+ 'maxMemory' => 8192,
158
+ 'localDiskFlag' => true,
159
+ 'blockDevices' => [{'device' => 0, 'diskImage' => {'capacity' => 25 } },{'device' => 2, 'diskImage' => {'capacity' => 200 } }]
160
+ }
161
+ end
162
+
163
+ ##
164
+ # Set options for an "xlarge" instance.
165
+ # @return [Hash]
166
+ def self.xlarge
167
+ {
168
+ 'startCpus' => 16,
169
+ 'maxMemory' => 16384,
170
+ 'localDiskFlag' => true,
171
+ 'blockDevices' => [{'device' => 0, 'diskImage' => {'capacity' => 25 } },{'device' => 2, 'diskImage' => {'capacity' => 300 } }]
172
+ }
173
+ end
174
+
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,14 @@
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 "knife-softlayer/version"
9
+
10
+ module Knife
11
+ module Softlayer
12
+ # Your code goes here...
13
+ end
14
+ end
@@ -0,0 +1,167 @@
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'
9
+
10
+ class Chef
11
+ class Knife
12
+ module SoftlayerBase
13
+
14
+ # :nodoc:
15
+ def self.included(includer)
16
+ includer.class_eval do
17
+
18
+ deps do
19
+ require 'softlayer_api'
20
+ require 'readline'
21
+ require 'chef/json_compat'
22
+ require 'net/ssh'
23
+ require 'net/ssh/multi'
24
+ end
25
+
26
+ option :softlayer_credential_file,
27
+ :long => "--softlayer-credential-file FILE",
28
+ :description => "File containing SoftLayer credentials as used by `softlayer_api` Ruby gem.",
29
+ :proc => Proc.new { |key| Chef::Config[:knife][:softlayer_credential_file] = key }
30
+
31
+ option :softlayer_username,
32
+ :short => "-U USERNAME",
33
+ :long => "--softlayer-username KEY",
34
+ :description => "Your SoftLayer Username",
35
+ :proc => Proc.new { |key| Chef::Config[:knife][:softlayer_access_key_id] = key }
36
+
37
+ option :softlayer_api_key,
38
+ :short => "-K SECRET",
39
+ :long => "--softlayer-api-key SECRET",
40
+ :description => "Your SoftLayer API Key",
41
+ :proc => Proc.new { |key| Chef::Config[:knife][:softlayer_secret_access_key] = key }
42
+ end
43
+ end
44
+
45
+ ##
46
+ # Returns a connection to a SoftLayer API Service Endpoint.
47
+ # @param [Symbol] service
48
+ # @return [SoftLayer::Service]
49
+ def connection(service=:cci)
50
+ SoftLayer::Service.new(
51
+ SoftlayerBase.send(service),
52
+ :username => Chef::Config[:knife][:softlayer_username],
53
+ :api_key => Chef::Config[:knife][:softlayer_api_key]
54
+ )
55
+ end
56
+
57
+ ##
58
+ # Returns identifier string for the SoftLayer Virtual Guest service.
59
+ # @return [String]
60
+ def self.cci
61
+ 'SoftLayer_Virtual_Guest'
62
+ end
63
+
64
+ ##
65
+ # Returns identifier string for the SoftLayer Product Package service.
66
+ # @return [String]
67
+ def self.package
68
+ 'SoftLayer_Product_Package'
69
+ end
70
+
71
+ ##
72
+ # Returns identifier string for the SoftLayer Product Order service.
73
+ # @return [String]
74
+ def self.order
75
+ 'SoftLayer_Product_Order'
76
+ end
77
+
78
+ ##
79
+ # Returns identifier string for the SoftLayer Subnet Ordering service.
80
+ # @return [String]
81
+ def self.subnet
82
+ 'SoftLayer_Container_Product_Order_Network_Subnet'
83
+ end
84
+
85
+ ##
86
+ # Returns identifier string for the SoftLayer User Account service.
87
+ # @return [String]
88
+ def self.account
89
+ 'SoftLayer_Account'
90
+ end
91
+
92
+ ##
93
+ # Returns identifier string for the SoftLayer Global IP service.
94
+ # @return [String]
95
+ def self.global_ip
96
+ 'SoftLayer_Network_Subnet_IpAddress_Global'
97
+ end
98
+
99
+ ##
100
+ # Returns id of a particular SoftLayer ordering package.
101
+ # @return [String]
102
+ def self.non_server_package_id
103
+ 0 # this package contains everything that isn't a server on the SoftLayer API
104
+ end
105
+
106
+ ##
107
+ # Queries the SoftLayer API and returns the "category code" required for ordering a Global IPv4 address.
108
+ # @return [Integer]
109
+ def self.global_ipv4_cat_code
110
+ SoftLayer::Service.new(
111
+ SoftlayerBase.send(:package),
112
+ :username => Chef::Config[:knife][:softlayer_username],
113
+ :api_key => Chef::Config[:knife][:softlayer_api_key]
114
+ ).object_with_id(non_server_package_id).object_mask('isRequired', 'itemCategory').getConfiguration.map do |item|
115
+ item['itemCategory']['id'] if item['itemCategory']['categoryCode'] == 'global_ipv4'
116
+ end.compact.first
117
+ end
118
+
119
+ ##
120
+ # Queries the SoftLayer API and returns the "price code" required for ordering a Global IPv4 address.
121
+ # @return [Integer]
122
+ def self.global_ipv4_price_code
123
+ SoftLayer::Service.new(
124
+ SoftlayerBase.send(:package),
125
+ :username => Chef::Config[:knife][:softlayer_username],
126
+ :api_key => Chef::Config[:knife][:softlayer_api_key]
127
+ ).object_with_id(non_server_package_id).object_mask('id', 'item.description', 'categories.id').getItemPrices.map do |item|
128
+ item['id'] if item['categories'][0]['id'] == SoftlayerBase.global_ipv4_cat_code
129
+ end.compact.first
130
+ end
131
+
132
+ ##
133
+ # Constructs an order required for purchasing a Global IPv4 address.
134
+ # @return [Hash]
135
+ def self.build_global_ipv4_order
136
+ {
137
+ "complexType" => SoftlayerBase.subnet,
138
+ "packageId" => non_server_package_id,
139
+ "prices" => [{"id"=>SoftlayerBase.global_ipv4_price_code}],
140
+ "quantity" => 1
141
+ }
142
+ end
143
+
144
+ ##
145
+ # Locates a config value.
146
+ # @param [String] key
147
+ # @return [String]
148
+ def locate_config_value(key)
149
+ key = key.to_sym
150
+ config[key] || Chef::Config[:knife][key]
151
+ end
152
+
153
+ ##
154
+ # A CLI output formatting wrapper.
155
+ # @param [String] label
156
+ # @param [String] value
157
+ # @param [Symbol] color
158
+ # @return [String]
159
+ def msg_pair(label, value, color=:cyan)
160
+ if value && !value.to_s.empty?
161
+ puts "#{ui.color(label, color)}: #{value}"
162
+ end
163
+ end
164
+
165
+ end
166
+ end
167
+ end
@@ -0,0 +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
+ #
@@ -0,0 +1,44 @@
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
+ require 'chef/knife/flavor/base'
10
+
11
+ class Chef
12
+ class Knife
13
+ class SoftlayerFlavorList < Knife
14
+
15
+ include Knife::SoftlayerBase
16
+ include Knife::SoftlayerFlavorBase
17
+
18
+ banner 'knife softlayer flavor list (options)'
19
+
20
+ option :all,
21
+ :short => "-a",
22
+ :long => "--all",
23
+ :description => "Display all available configuration options for launching an instance.",
24
+ :default => false
25
+
26
+ ##
27
+ # Run the procedure to list softlayer VM flavors or display all available options.
28
+ # @return [nil]
29
+ def run
30
+ table_data = table_info(config[:all])
31
+ if config[:all]
32
+ puts ui.list(table_data, :columns_across, 6)
33
+ msg = "These options can be used in place of 'flavors'; See `knife softlayer server create --help` for details.\n"
34
+ else
35
+ puts ui.list(table_data, :columns_across, 4)
36
+ msg = "'flavors' provided here for convenience; SoftLayer allows you to choose a configuration a la carte.\nFor a full list of available instance options use --all with the `knife softlayer flavor list` subcommand."
37
+ end
38
+ puts ui.color("\nNOTICE: ", :yellow)
39
+ puts ui.color(msg)
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +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
+ #
@@ -0,0 +1,343 @@
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
+ require 'chef/knife/flavor/base'
10
+
11
+ class Chef
12
+ class Knife
13
+ class SoftlayerServerCreateError < StandardError; end
14
+ class SoftlayerServerCreate < Knife
15
+
16
+ attr_reader :cci
17
+
18
+ include Knife::SoftlayerBase
19
+
20
+ banner 'knife softlayer server create (options)'
21
+
22
+ option :flavor,
23
+ :long => '--flavor FLAVOR',
24
+ :short => '-f FLAVOR',
25
+ :description => 'Pre-configured packages of computing resources. See `knife softlayer flavor list` for details.'
26
+
27
+ option :hostname,
28
+ :long => '--hostname VALUE',
29
+ :short => '-H VALUE',
30
+ :description => 'The hostname SoftLayer will assign to the VM instance.'
31
+
32
+ option :domain,
33
+ :long => '--domain VALUE',
34
+ :short => '-D VALUE',
35
+ :description => 'The FQDN SoftLayer will assign to the VM instance.',
36
+ :default => 'example.com'
37
+
38
+ option :cores,
39
+ :long => '--cores VALUE',
40
+ :short => '-C VALUE',
41
+ :description => 'The number of virtual cores SoftLayer will assign to the VM instance.',
42
+ :default => 1
43
+
44
+ option :os_code,
45
+ :long => '--os-code VALUE',
46
+ :short => '-O VALUE',
47
+ :description => 'A valid SoftLayer operating system code. See `knife softlayer flavor list --all` for a list of valid codes.',
48
+ :default => 'UBUNTU_LATEST'
49
+
50
+ option :ram,
51
+ :long => '--ram VALUE',
52
+ :short => '-R VALUE',
53
+ :description => 'The number of virtual cores SoftLayer will assign to the VM instance.',
54
+ :default => 1024
55
+
56
+ option :block_storage,
57
+ :long => '--block-storage VALUE',
58
+ :short => '-B VALUE',
59
+ :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.] ',
60
+ :default => '0:25'
61
+
62
+ option :nic,
63
+ :long => '--network-interface-speed VALUE',
64
+ :short => '-n VALUE',
65
+ :description => 'The maximum speed of the public NIC available to the instance.',
66
+ :default => 10
67
+
68
+ option :bill_monthly,
69
+ :long => '--bill-monthly',
70
+ :description => 'Flag to bill monthly instead of hourly, minimum charge of one month.',
71
+ :default => false
72
+
73
+ option :single_tenant,
74
+ :long => '--single-tenant',
75
+ :description => 'Create a CCI VM with a dedicated physical host.',
76
+ :boolean => true,
77
+ :default => false
78
+
79
+ option :san_storage,
80
+ :long => '--san-storage',
81
+ :description => 'Create a CCI VM with SAN based block storage [disk].',
82
+ :boolean => true,
83
+ :default => false
84
+
85
+ option :datacenter,
86
+ :long => '--datacenter VALUE',
87
+ :description => 'Create a CCI VI in a particular datacenter.'
88
+
89
+ option :tags,
90
+ :short => "-T T=V[,T=V,...]",
91
+ :long => "--tags Tag=Value[,Tag=Value...]",
92
+ :description => "The tags for this server",
93
+ :proc => Proc.new { |tags| tags.split(',') }
94
+
95
+ option :chef_node_name,
96
+ :short => "-N NAME",
97
+ :long => "--node-name NAME",
98
+ :description => "The Chef node name for your new node",
99
+ :proc => Proc.new { |key| Chef::Config[:knife][:chef_node_name] = key }
100
+
101
+
102
+ option :ssh_user,
103
+ :short => "-x USERNAME",
104
+ :long => "--ssh-user USERNAME",
105
+ :description => "The ssh username",
106
+ :default => "root"
107
+
108
+ option :ssh_password,
109
+ :short => "-P PASSWORD",
110
+ :long => "--ssh-password PASSWORD",
111
+ :description => "The ssh password"
112
+
113
+ option :ssh_port,
114
+ :short => "-p PORT",
115
+ :long => "--ssh-port PORT",
116
+ :description => "The ssh port",
117
+ :default => "22",
118
+ :proc => Proc.new { |key| Chef::Config[:knife][:ssh_port] = key }
119
+
120
+ option :ssh_gateway,
121
+ :short => "-w GATEWAY",
122
+ :long => "--ssh-gateway GATEWAY",
123
+ :description => "The ssh gateway server",
124
+ :proc => Proc.new { |key| Chef::Config[:knife][:ssh_gateway] = key }
125
+
126
+ option :identity_file,
127
+ :short => "-i IDENTITY_FILE",
128
+ :long => "--identity-file IDENTITY_FILE",
129
+ :description => "The SSH identity file used for authentication"
130
+
131
+ option :prerelease,
132
+ :long => "--prerelease",
133
+ :description => "Install the pre-release chef gems"
134
+
135
+ option :bootstrap_version,
136
+ :long => "--bootstrap-version VERSION",
137
+ :description => "The version of Chef to install",
138
+ :proc => Proc.new { |v| Chef::Config[:knife][:bootstrap_version] = v }
139
+
140
+ option :bootstrap_proxy,
141
+ :long => "--bootstrap-proxy PROXY_URL",
142
+ :description => "The proxy server for the node being bootstrapped",
143
+ :proc => Proc.new { |p| Chef::Config[:knife][:bootstrap_proxy] = p }
144
+
145
+ option :distro,
146
+ :short => "-d DISTRO",
147
+ :long => "--distro DISTRO",
148
+ :description => "Bootstrap a distro using a template; default is 'chef-full'",
149
+ :proc => Proc.new { |d| Chef::Config[:knife][:distro] = d },
150
+ :default => "chef-full"
151
+
152
+ option :template_file,
153
+ :long => "--template-file TEMPLATE",
154
+ :description => "Full path to location of template to use",
155
+ :proc => Proc.new { |t| Chef::Config[:knife][:template_file] = t },
156
+ :default => false
157
+
158
+ option :run_list,
159
+ :short => "-r RUN_LIST",
160
+ :long => "--run-list RUN_LIST",
161
+ :description => "Comma separated list of roles/recipes to apply",
162
+ :proc => lambda { |o| o.split(/[\s,]+/) }
163
+
164
+ option :secret,
165
+ :short => "-s SECRET",
166
+ :long => "--secret ",
167
+ :description => "The secret key to use to encrypt data bag item values",
168
+ :proc => lambda { |s| Chef::Config[:knife][:secret] = s }
169
+
170
+ option :secret_file,
171
+ :long => "--secret-file SECRET_FILE",
172
+ :description => "A file containing the secret key to use to encrypt data bag item values",
173
+ :proc => lambda { |sf| Chef::Config[:knife][:secret_file] = sf }
174
+
175
+ option :json_attributes,
176
+ :short => "-j JSON",
177
+ :long => "--json-attributes JSON",
178
+ :description => "A JSON string to be added to the first run of chef-client",
179
+ :proc => lambda { |o| JSON.parse(o) }
180
+
181
+ option :host_key_verify,
182
+ :long => "--[no-]host-key-verify",
183
+ :description => "Verify host key, enabled by default.",
184
+ :boolean => true,
185
+ :default => true
186
+
187
+ option :bootstrap_protocol,
188
+ :long => "--bootstrap-protocol protocol",
189
+ :description => "protocol to bootstrap windows servers. options: winrm/ssh",
190
+ :proc => Proc.new { |key| Chef::Config[:knife][:bootstrap_protocol] = key },
191
+ :default => nil
192
+
193
+ option :fqdn,
194
+ :long => "--fqdn FQDN",
195
+ :description => "Pre-defined FQDN",
196
+ :proc => Proc.new { |key| Chef::Config[:knife][:fqdn] = key },
197
+ :default => nil
198
+
199
+ option :assign_global_ip,
200
+ :long => "--assign-global-ip IpAdress",
201
+ :description => "Assign an existing SoftLayer Global IP address.",
202
+ :default => nil
203
+
204
+ option :new_global_ip,
205
+ :long => "--new-global-ip",
206
+ :description => "Order a new SoftLayer Global IP address and assign it to the instance."
207
+
208
+ option :hint,
209
+ :long => "--hint HINT_NAME[=HINT_FILE]",
210
+ :description => "Specify Ohai Hint to be set on the bootstrap target. Use multiple --hint options to specify multiple hints.",
211
+ :proc => Proc.new { |h|
212
+ Chef::Config[:knife][:hints] ||= {}
213
+ name, path = h.split("=")
214
+ Chef::Config[:knife][:hints][name] = path ? JSON.parse(::File.read(path)) : Hash.new
215
+ }
216
+
217
+ ##
218
+ # Run the procedure to create a SoftLayer VM and bootstrap it.
219
+ # @return [nil]
220
+ def run
221
+
222
+ $stdout.sync = true
223
+
224
+ config[:os_code] =~ /^WIN_/ and raise SoftlayerServerCreateError, "#{ui.color("Windows VMs not currently supported.", :red)}"
225
+
226
+ if config[:flavor]
227
+ @template = SoftlayerFlavorBase.load_flavor(config[:flavor])
228
+ else
229
+ @template = {}
230
+ @template['startCpus'] = config[:cores]
231
+ @template['maxMemory'] = config[:ram]
232
+ @template['localDiskFlag'] = !config[:san_storage]
233
+ @template['blockDevices'] = config[:block_storage].split(',').map do |i|
234
+ dev, cap = i.split(':')
235
+ {'device' => dev, 'diskImage' => {'capacity' => cap } }
236
+ end
237
+ end
238
+
239
+ @template['complexType'] = SoftlayerBase.cci
240
+ @template['hostname'] = config[:hostname]
241
+ @template['domain'] = config[:domain]
242
+ @template['dedicatedAccountHostOnlyFlag'] = config[:single_tenant]
243
+ @template['operatingSystemReferenceCode'] = config[:os_code]
244
+ @template['hourlyBillingFlag'] = config[:bill_monthly]
245
+ @template['networkComponents'] = [{ 'maxSpeed' => config[:nic]}]
246
+
247
+ @template['datacenter'] = { 'name' => config[:datacenter] } if config[:datacenter]
248
+
249
+ @response = connection.createObject(@template)
250
+
251
+ puts ui.color("Launching SoftLayer CCI, this may take a few minutes.", :green)
252
+
253
+ begin
254
+ @cci = connection.object_mask('mask.operatingSystem.passwords.password').object_with_id(@response['id']).getObject
255
+ sleep 1
256
+ putc('.')
257
+ end while @cci['operatingSystem'].nil? or @cci['operatingSystem']['passwords'].empty?
258
+
259
+ linux_bootstrap(@cci).run
260
+
261
+ if config[:new_global_ip] || config[:assign_global_ip]
262
+ if config[:new_global_ip]
263
+ begin
264
+ order = SoftlayerBase.build_global_ipv4_order
265
+ response = connection(:order).placeOrder(order)
266
+ global_ip_id = response['placedOrder']['account']['globalIpv4Records'].first['id']
267
+
268
+ if global_ip_id
269
+ puts ui.color('Global IP Address successfully created.', :green)
270
+ else
271
+ raise 'Unable to find Global IP Address ID. Address not created.'
272
+ end
273
+ rescue Exception => e
274
+ puts ui.color('We have encountered a problem ordering the requested global IP. The transaction may not have completed.', :red)
275
+ puts ui.color(e.message, :yellow)
276
+ end
277
+ end
278
+
279
+ if config[:assign_global_ip]
280
+ global_ip_id = connection(:account).object_mask('ipAddress').getGlobalIpv4Records.map do |global_ip|
281
+ global_ip['id'] if global_ip['ipAddress']['ipAddress'] == config[:assign_global_ip]
282
+ end.compact.first
283
+ if global_ip_id
284
+ puts ui.color('Global IP Address ID found.', :green)
285
+ end
286
+ end
287
+
288
+ puts ui.color('Assigning Global IP Address to Instance.', :green)
289
+ connection(:global_ip).object_with_id(global_ip_id).route(@cci['primaryIpAddress'])
290
+
291
+ puts ui.color('Global IP Address has been assigned.', :green)
292
+ 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)
293
+
294
+ end
295
+
296
+ end
297
+
298
+ # @param [Hash] cci
299
+ # @return [Chef::Knife::Bootstrap]
300
+ def linux_bootstrap(cci)
301
+ bootstrap = Chef::Knife::Bootstrap.new
302
+ bootstrap.name_args = [cci['primaryIpAddress']]
303
+ bootstrap.config[:ssh_user] = config[:ssh_user]
304
+ bootstrap.config[:ssh_password] = cci['operatingSystem']['passwords'].first['password']
305
+ bootstrap.config[:ssh_port] = config[:ssh_port]
306
+ bootstrap.config[:ssh_gateway] = config[:ssh_gateway]
307
+ bootstrap.config[:identity_file] = config[:identity_file]
308
+ bootstrap.config[:chef_node_name] = locate_config_value(:chef_node_name) || cci['id']
309
+ bootstrap.config[:use_sudo] = true unless config[:ssh_user] == 'root'
310
+ bootstrap.config[:host_key_verify] = config[:host_key_verify]
311
+ shared_bootstrap(bootstrap)
312
+ end
313
+
314
+ # @param [Chef::Knife::Bootstrap] bootstrap
315
+ # @return [Chef::Knife::Bootstrap]
316
+ def shared_bootstrap(bootstrap)
317
+ bootstrap.config[:run_list] = config[:run_list]
318
+ bootstrap.config[:bootstrap_version] = locate_config_value(:bootstrap_version)
319
+ bootstrap.config[:distro] = locate_config_value(:distro)
320
+ bootstrap.config[:template_file] = locate_config_value(:template_file)
321
+ bootstrap.config[:environment] = locate_config_value(:environment)
322
+ bootstrap.config[:prerelease] = config[:prerelease]
323
+ bootstrap.config[:first_boot_attributes] = locate_config_value(:json_attributes) || {}
324
+ bootstrap.config[:encrypted_data_bag_secret] = locate_config_value(:encrypted_data_bag_secret)
325
+ bootstrap.config[:encrypted_data_bag_secret_file] = locate_config_value(:encrypted_data_bag_secret_file)
326
+ bootstrap.config[:secret] = locate_config_value(:secret)
327
+ bootstrap.config[:secret_file] = locate_config_value(:secret_file)
328
+ bootstrap.config[:tags] = locate_config_value(:tags)
329
+ bootstrap.config[:fqdn] = locate_config_value(:fqdn)
330
+ Chef::Config[:knife][:hints] ||= {}
331
+ Chef::Config[:knife][:hints]['softlayer'] ||= {}
332
+ bootstrap
333
+ end
334
+
335
+ def windows_bootstrap(server, fqdn)
336
+ # TODO: Windows support....
337
+ end
338
+
339
+ end
340
+ end
341
+ end
342
+
343
+