knife-softlayer 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+