knife-azure 1.6.0.rc.0 → 1.6.0

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +304 -8
  3. data/lib/azure/azure_interface.rb +81 -0
  4. data/lib/azure/custom_errors.rb +35 -0
  5. data/lib/azure/helpers.rb +44 -0
  6. data/lib/azure/resource_management/ARM_base.rb +29 -0
  7. data/lib/azure/resource_management/ARM_deployment_template.rb +561 -0
  8. data/lib/azure/resource_management/ARM_interface.rb +795 -0
  9. data/lib/azure/resource_management/windows_credentials.rb +136 -0
  10. data/lib/azure/service_management/ASM_interface.rb +301 -0
  11. data/lib/azure/{ag.rb → service_management/ag.rb} +2 -2
  12. data/lib/azure/{certificate.rb → service_management/certificate.rb} +2 -2
  13. data/lib/azure/service_management/connection.rb +102 -0
  14. data/lib/azure/{deploy.rb → service_management/deploy.rb} +8 -2
  15. data/lib/azure/{disk.rb → service_management/disk.rb} +2 -2
  16. data/lib/azure/{host.rb → service_management/host.rb} +2 -2
  17. data/lib/azure/{image.rb → service_management/image.rb} +2 -2
  18. data/lib/azure/{loadbalancer.rb → service_management/loadbalancer.rb} +4 -18
  19. data/lib/azure/{rest.rb → service_management/rest.rb} +15 -10
  20. data/lib/azure/{role.rb → service_management/role.rb} +174 -6
  21. data/lib/azure/{storageaccount.rb → service_management/storageaccount.rb} +2 -2
  22. data/lib/azure/{utility.rb → service_management/utility.rb} +0 -0
  23. data/lib/azure/{vnet.rb → service_management/vnet.rb} +2 -2
  24. data/lib/chef/knife/azure_ag_create.rb +3 -6
  25. data/lib/chef/knife/azure_ag_list.rb +2 -16
  26. data/lib/chef/knife/azure_base.rb +89 -22
  27. data/lib/chef/knife/azure_image_list.rb +3 -7
  28. data/lib/chef/knife/azure_internal-lb_create.rb +2 -5
  29. data/lib/chef/knife/azure_internal-lb_list.rb +2 -16
  30. data/lib/chef/knife/azure_server_create.rb +122 -501
  31. data/lib/chef/knife/azure_server_delete.rb +15 -38
  32. data/lib/chef/knife/azure_server_list.rb +2 -27
  33. data/lib/chef/knife/azure_server_show.rb +4 -60
  34. data/lib/chef/knife/azure_vnet_create.rb +2 -7
  35. data/lib/chef/knife/azure_vnet_list.rb +2 -17
  36. data/lib/chef/knife/azurerm_base.rb +228 -0
  37. data/lib/chef/knife/azurerm_server_create.rb +393 -0
  38. data/lib/chef/knife/azurerm_server_delete.rb +121 -0
  39. data/lib/chef/knife/azurerm_server_list.rb +18 -0
  40. data/lib/chef/knife/azurerm_server_show.rb +37 -0
  41. data/lib/chef/knife/bootstrap/bootstrap_options.rb +105 -0
  42. data/lib/chef/knife/bootstrap/bootstrapper.rb +343 -0
  43. data/lib/chef/knife/bootstrap/common_bootstrap_options.rb +116 -0
  44. data/lib/chef/knife/bootstrap_azure.rb +110 -0
  45. data/lib/chef/knife/bootstrap_azurerm.rb +116 -0
  46. data/lib/knife-azure/version.rb +1 -2
  47. metadata +132 -16
  48. data/lib/azure/connection.rb +0 -99
@@ -0,0 +1,136 @@
1
+ #
2
+ # Author:: Nimisha Sharad (nimisha.sharad@clogeny.com)
3
+ # Copyright:: Copyright (c) 2015-2016 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ # XPLAT stores the access token and other information in windows credential manager.
20
+ # Using FFI to call CredRead function
21
+ require 'chef'
22
+ require 'mixlib/shellout'
23
+ require 'ffi'
24
+
25
+ module Azure::ARM
26
+
27
+ module ReadCred
28
+
29
+ extend FFI::Library
30
+
31
+ ffi_lib 'Advapi32'
32
+
33
+ CRED_TYPE_GENERIC = 1
34
+ CRED_TYPE_DOMAIN_PASSWORD = 2
35
+ CRED_TYPE_DOMAIN_CERTIFICATE = 3
36
+ CRED_TYPE_DOMAIN_VISIBLE_PASSWORD = 4
37
+
38
+ # Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724284(v=vs.85).aspx
39
+ class FILETIME < FFI::Struct
40
+ layout :dwLowDateTime, :uint32,
41
+ :dwHighDateTime, :uint32
42
+ end
43
+
44
+ # Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/aa374790(v=vs.85).aspx
45
+ class CREDENTIAL_ATTRIBUTE < FFI::Struct
46
+ layout :Keyword, :pointer,
47
+ :Flags, :uint32,
48
+ :ValueSize, :uint32,
49
+ :Value, :pointer
50
+ end
51
+
52
+ # Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/aa374788(v=vs.85).aspx
53
+ class CREDENTIAL_OBJECT < FFI::Struct
54
+ layout :Flags, :uint32,
55
+ :Type, :uint32,
56
+ :TargetName, :pointer,
57
+ :Comment, :pointer,
58
+ :LastWritten, FILETIME,
59
+ :CredentialBlobSize, :uint32,
60
+ :CredentialBlob, :pointer,
61
+ :Persist, :uint32,
62
+ :AttributeCount, :uint32,
63
+ :Attributes, CREDENTIAL_ATTRIBUTE,
64
+ :TargetAlias, :pointer,
65
+ :UserName, :pointer
66
+ end
67
+
68
+ # Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/aa374804(v=vs.85).aspx
69
+ attach_function :CredReadW, [:pointer, :uint32, :uint32, :pointer], :bool
70
+ end
71
+
72
+
73
+ module WindowsCredentials
74
+
75
+ include Chef::Mixin::WideString
76
+ include ReadCred
77
+
78
+ def token_details_for_windows
79
+ target = target_name
80
+
81
+ if target
82
+ target_pointer = wstring(target)
83
+ info_ptr = FFI::MemoryPointer.new(:pointer)
84
+ cred = CREDENTIAL_OBJECT.new info_ptr
85
+ cred_result = CredReadW(target_pointer, CRED_TYPE_GENERIC, 0, cred)
86
+ translated_cred = CREDENTIAL_OBJECT.new(info_ptr.read_pointer)
87
+
88
+ target_obj = translated_cred[:TargetName].read_wstring.split("::") if translated_cred[:TargetName].read_wstring
89
+ cred_blob = translated_cred[:CredentialBlob].get_bytes(0, translated_cred[:CredentialBlobSize]).split("::")
90
+
91
+ tokentype = target_obj.select { |obj| obj.include? "tokenType" }
92
+ user = target_obj.select { |obj| obj.include? "userId" }
93
+ clientid = target_obj.select { |obj| obj.include? "clientId" }
94
+ expiry_time = target_obj.select { |obj| obj.include? "expiresOn" }
95
+ access_token = cred_blob.select { |obj| obj.include? "a:" }
96
+ refresh_token = cred_blob.select { |obj| obj.include? "r:" }
97
+
98
+ credential = {}
99
+ credential[:tokentype] = tokentype[0].split(":")[1] if tokentype
100
+ credential[:user] = user[0].split(":")[1] if user
101
+ credential[:token] = access_token[0].split(":")[1] if access_token
102
+ credential[:refresh_token] = refresh_token[0].split(":")[1] if refresh_token
103
+ credential[:clientid] = clientid[0].split(":")[1] if clientid
104
+ credential[:expiry_time] = expiry_time[0].split("expiresOn:")[1].gsub("\\","") if expiry_time
105
+ else
106
+ raise "TargetName Not Found"
107
+ end
108
+ credential
109
+ end
110
+
111
+ def target_name
112
+ # cmdkey command is used for accessing windows credential manager
113
+ xplat_creds_cmd = Mixlib::ShellOut.new("cmdkey /list | findstr AzureXplatCli")
114
+ result = xplat_creds_cmd.run_command
115
+
116
+ target_name = ""
117
+ if result.stdout.empty?
118
+ raise "Azure Credentials not found. Please run xplat's 'azure login' command"
119
+ else
120
+ result.stdout.split("\n").each do |target|
121
+ # Three credentials get created in windows credential manager for xplat-cli
122
+ # One of them is for common tanent id, which can't be used
123
+ # Two of them end with --0-2 and --1-2. The one ending with --1-2 doesn't have
124
+ # accessToken and refreshToken in the credentialBlob.
125
+ # Selecting the one ending with --0-2
126
+ if !target.include?("common::") && target.include?("--0-2")
127
+ target_name = target.gsub("Target:","").strip
128
+ break
129
+ end
130
+ end
131
+ end
132
+
133
+ target_name
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,301 @@
1
+ #
2
+ # Author::
3
+ # Copyright:: Copyright (c) 2016 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'azure/azure_interface'
20
+ require 'azure/service_management/rest'
21
+ require 'azure/service_management/connection'
22
+
23
+ module Azure
24
+ class ServiceManagement
25
+ class ASMInterface < AzureInterface
26
+ include AzureAPI
27
+
28
+ attr_accessor :connection
29
+
30
+ def initialize(params = {})
31
+ @rest = Rest.new(params)
32
+ @connection = Azure::ServiceManagement::Connection.new(@rest)
33
+ super
34
+ end
35
+
36
+ def list_images
37
+ connection.images.all
38
+ end
39
+
40
+ def list_servers
41
+ servers = connection.roles.all
42
+ cols = ['DNS Name', 'VM Name', 'Status', 'IP Address', 'SSH Port', 'WinRM Port' ]
43
+ rows = []
44
+ servers.each do |server|
45
+ rows << server.hostedservicename.to_s+".cloudapp.net" # Info about the DNS name at http://msdn.microsoft.com/en-us/library/ee460806.aspx
46
+ rows << server.name.to_s
47
+ rows << begin
48
+ state = server.status.to_s.downcase
49
+ case state
50
+ when 'shutting-down','terminated','stopping','stopped'
51
+ ui.color(state, :red)
52
+ when 'pending'
53
+ ui.color(state, :yellow)
54
+ else
55
+ ui.color('ready', :green)
56
+ end
57
+ end
58
+ rows << server.publicipaddress.to_s
59
+ rows << server.sshport.to_s
60
+ rows << server.winrmport.to_s
61
+ end
62
+ display_list(ui, cols, rows)
63
+ end
64
+
65
+ def find_server(params = {})
66
+ server = connection.roles.find(params[:name], params = { :azure_dns_name => params[:azure_dns_name] })
67
+ end
68
+
69
+ def delete_server(params = {})
70
+ server = find_server({name: params[:name], azure_dns_name: params[:azure_dns_name]})
71
+
72
+ unless server
73
+ puts "\n"
74
+ ui.error("Server #{params[:name]} does not exist")
75
+ exit!
76
+ end
77
+
78
+ puts "\n"
79
+ msg_pair(ui, 'DNS Name', server.hostedservicename + ".cloudapp.net")
80
+ msg_pair(ui, 'VM Name', server.name)
81
+ msg_pair(ui, 'Size', server.size)
82
+ msg_pair(ui, 'Public Ip Address', server.publicipaddress)
83
+ puts "\n"
84
+
85
+ begin
86
+ ui.confirm('Do you really want to delete this server')
87
+ rescue SystemExit # Need to handle this as confirming with N/n raises SystemExit exception
88
+ server = nil # Cleanup is implicitly performed in other cloud plugins
89
+ exit!
90
+ end
91
+
92
+ params[:azure_dns_name] = server.hostedservicename
93
+
94
+ connection.roles.delete(params)
95
+
96
+ puts "\n"
97
+ ui.warn("Deleted server #{server.name}")
98
+ end
99
+
100
+ def show_server(name)
101
+ begin
102
+ role = connection.roles.find name
103
+
104
+ puts ''
105
+ if (role)
106
+ details = Array.new
107
+ details << ui.color('Role name', :bold, :cyan)
108
+ details << role.name
109
+ details << ui.color('Status', :bold, :cyan)
110
+ details << role.status
111
+ details << ui.color('Size', :bold, :cyan)
112
+ details << role.size
113
+ details << ui.color('Hosted service name', :bold, :cyan)
114
+ details << role.hostedservicename
115
+ details << ui.color('Deployment name', :bold, :cyan)
116
+ details << role.deployname
117
+ details << ui.color('Host name', :bold, :cyan)
118
+ details << role.hostname
119
+ unless role.sshport.nil?
120
+ details << ui.color('SSH port', :bold, :cyan)
121
+ details << role.sshport
122
+ end
123
+ unless role.winrmport.nil?
124
+ details << ui.color('WinRM port', :bold, :cyan)
125
+ details << role.winrmport
126
+ end
127
+ details << ui.color('Public IP', :bold, :cyan)
128
+ details << role.publicipaddress.to_s
129
+
130
+ unless role.thumbprint.empty?
131
+ details << ui.color('Thumbprint', :bold, :cyan)
132
+ details << role.thumbprint
133
+ end
134
+ puts ui.list(details, :columns_across, 2)
135
+ if role.tcpports.length > 0 || role.udpports.length > 0
136
+ details.clear
137
+ details << ui.color('Ports open', :bold, :cyan)
138
+ details << ui.color('Local port', :bold, :cyan)
139
+ details << ui.color('IP', :bold, :cyan)
140
+ details << ui.color('Public port', :bold, :cyan)
141
+ if role.tcpports.length > 0
142
+ role.tcpports.each do |port|
143
+ details << 'tcp'
144
+ details << port['LocalPort']
145
+ details << port['Vip']
146
+ details << port['PublicPort']
147
+ end
148
+ end
149
+ if role.udpports.length > 0
150
+ role.udpports.each do |port|
151
+ details << 'udp'
152
+ details << port['LocalPort']
153
+ details << port['Vip']
154
+ details << port['PublicPort']
155
+ end
156
+ end
157
+ puts ui.list(details, :columns_across, 4)
158
+ end
159
+ else
160
+ puts "No VM found"
161
+ end
162
+
163
+ rescue => error
164
+ puts "#{error.class} and #{error.message}"
165
+ end
166
+
167
+ end
168
+
169
+ def list_internal_lb
170
+ lbs = connection.lbs.all
171
+ cols = %w{Name Service Subnet VIP}
172
+ rows = []
173
+ lbs.each do |lb|
174
+ cols.each { |col| rows << lb.send(col.downcase).to_s }
175
+ end
176
+ display_list(ui, cols, rows)
177
+ end
178
+
179
+ def create_internal_lb(params = {})
180
+ connection.lbs.create(params)
181
+ end
182
+
183
+ def list_vnets
184
+ vnets = connection.vnets.all
185
+ cols = ['Name', 'Affinity Group', 'State']
186
+ rows = []
187
+ vnets.each do |vnet|
188
+ %w(name affinity_group state).each{ |col| rows << vnet.send(col).to_s }
189
+ end
190
+ display_list(ui, cols, rows)
191
+ end
192
+
193
+ def create_vnet(params = {})
194
+ connection.vnets.create(params)
195
+ end
196
+
197
+ def list_affinity_groups
198
+ affinity_groups = connection.ags.all
199
+ cols = %w{Name Location Description}
200
+ rows = []
201
+ affinity_groups.each do |affinity_group|
202
+ cols.each { |col| rows << affinity_group.send(col.downcase).to_s }
203
+ end
204
+ display_list(ui, cols, rows)
205
+ end
206
+
207
+ def create_affinity_group(params = {})
208
+ connection.ags.create(params)
209
+ end
210
+
211
+ def create_server(params = {})
212
+ remove_hosted_service_on_failure = params[:azure_dns_name]
213
+ if connection.hosts.exists?(params[:azure_dns_name])
214
+ remove_hosted_service_on_failure = nil
215
+ end
216
+
217
+ #If Storage Account is not specified, check if the geographic location has one to re-use
218
+ if not params[:azure_storage_account]
219
+ storage_accts = connection.storageaccounts.all
220
+ storage = storage_accts.find { |storage_acct| storage_acct.location.to_s == params[:azure_service_location] }
221
+ unless storage
222
+ params[:azure_storage_account] = [strip_non_ascii(params[:azure_vm_name]), random_string].join.downcase
223
+ remove_storage_service_on_failure = params[:azure_storage_account]
224
+ else
225
+ remove_storage_service_on_failure = nil
226
+ params[:azure_storage_account] = storage.name.to_s
227
+ end
228
+ else
229
+ if connection.storageaccounts.exists?(params[:azure_storage_account])
230
+ remove_storage_service_on_failure = nil
231
+ else
232
+ remove_storage_service_on_failure = params[:azure_storage_account]
233
+ end
234
+ end
235
+
236
+ begin
237
+ connection.deploys.create(params)
238
+ rescue Exception => e
239
+ Chef::Log.error("Failed to create the server -- exception being rescued: #{e.to_s}")
240
+ backtrace_message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
241
+ Chef::Log.debug("#{backtrace_message}")
242
+ cleanup_and_exit(remove_hosted_service_on_failure, remove_storage_service_on_failure)
243
+ end
244
+
245
+ end
246
+
247
+ def cleanup_and_exit(remove_hosted_service_on_failure, remove_storage_service_on_failure)
248
+ Chef::Log.warn("Cleaning up resources...")
249
+
250
+ if remove_hosted_service_on_failure
251
+ ret_val = connection.hosts.delete(remove_hosted_service_on_failure)
252
+ ret_val.content.empty? ? Chef::Log.warn("Deleted created DNS: #{remove_hosted_service_on_failure}.") : Chef::Log.warn("Deletion failed for created DNS:#{remove_hosted_service_on_failure}. " + ret_val.text)
253
+ end
254
+
255
+ if remove_storage_service_on_failure
256
+ ret_val = connection.storageaccounts.delete(remove_storage_service_on_failure)
257
+ ret_val.content.empty? ? Chef::Log.warn("Deleted created Storage Account: #{remove_storage_service_on_failure}.") : Chef::Log.warn("Deletion failed for created Storage Account: #{remove_storage_service_on_failure}. " + ret_val.text)
258
+ end
259
+ exit 1
260
+ end
261
+
262
+ def get_role_server(dns_name, vm_name)
263
+ deploy = connection.deploys.queryDeploy(dns_name)
264
+ deploy.find_role(vm_name)
265
+ end
266
+
267
+ def get_extension(name, publisher)
268
+ connection.query_azure("resourceextensions/#{publisher}/#{name}")
269
+ end
270
+
271
+ def deployment_name(dns_name)
272
+ connection.deploys.get_deploy_name_for_hostedservice(dns_name)
273
+ end
274
+
275
+ def deployment(path)
276
+ connection.query_azure(path)
277
+ end
278
+
279
+ def valid_image?(name)
280
+ connection.images.exists?(name)
281
+ end
282
+
283
+ def vm_image?(name)
284
+ connection.images.is_vm_image(name)
285
+ end
286
+
287
+ def add_extension(name, params = {})
288
+ begin
289
+ ui.info "Started with Chef Extension deployment on the server #{name}..."
290
+ connection.roles.update(name, params)
291
+ ui.info "\nSuccessfully deployed Chef Extension on the server #{name}."
292
+ rescue Exception => e
293
+ Chef::Log.error("Failed to add extension to the server -- exception being rescued: #{e.to_s}")
294
+ backtrace_message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
295
+ Chef::Log.debug("#{backtrace_message}")
296
+ end
297
+ end
298
+ end
299
+ end
300
+ end
301
+
@@ -16,7 +16,7 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- class Azure
19
+ module Azure
20
20
  class AGs
21
21
  def initialize(connection)
22
22
  @connection = connection
@@ -58,7 +58,7 @@ class Azure
58
58
  end
59
59
  end
60
60
 
61
- class Azure
61
+ module Azure
62
62
  class AG
63
63
  attr_accessor :name, :label, :description, :location
64
64