knife-azure 3.0.0 → 4.0.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/azure/custom_errors.rb +1 -1
  3. data/lib/azure/resource_management/ARM_deployment_template.rb +5 -5
  4. data/lib/azure/resource_management/ARM_interface.rb +8 -12
  5. data/lib/azure/resource_management/windows_credentials.rb +7 -8
  6. data/lib/chef/knife/azurerm_server_create.rb +2 -2
  7. data/lib/chef/knife/azurerm_server_delete.rb +1 -1
  8. data/lib/chef/knife/bootstrap/bootstrapper.rb +10 -11
  9. data/lib/chef/knife/bootstrap_azurerm.rb +1 -1
  10. data/lib/chef/knife/helpers/azurerm_base.rb +17 -19
  11. data/lib/knife-azure/version.rb +1 -1
  12. metadata +30 -43
  13. data/lib/azure/service_management/ASM_interface.rb +0 -310
  14. data/lib/azure/service_management/ag.rb +0 -99
  15. data/lib/azure/service_management/certificate.rb +0 -235
  16. data/lib/azure/service_management/connection.rb +0 -102
  17. data/lib/azure/service_management/deploy.rb +0 -221
  18. data/lib/azure/service_management/disk.rb +0 -68
  19. data/lib/azure/service_management/host.rb +0 -184
  20. data/lib/azure/service_management/image.rb +0 -94
  21. data/lib/azure/service_management/loadbalancer.rb +0 -78
  22. data/lib/azure/service_management/rest.rb +0 -125
  23. data/lib/azure/service_management/role.rb +0 -717
  24. data/lib/azure/service_management/storageaccount.rb +0 -127
  25. data/lib/azure/service_management/utility.rb +0 -40
  26. data/lib/azure/service_management/vnet.rb +0 -134
  27. data/lib/chef/knife/azure_ag_create.rb +0 -73
  28. data/lib/chef/knife/azure_ag_list.rb +0 -35
  29. data/lib/chef/knife/azure_image_list.rb +0 -56
  30. data/lib/chef/knife/azure_internal-lb_create.rb +0 -74
  31. data/lib/chef/knife/azure_internal-lb_list.rb +0 -35
  32. data/lib/chef/knife/azure_server_create.rb +0 -531
  33. data/lib/chef/knife/azure_server_delete.rb +0 -136
  34. data/lib/chef/knife/azure_server_list.rb +0 -38
  35. data/lib/chef/knife/azure_server_show.rb +0 -41
  36. data/lib/chef/knife/azure_vnet_create.rb +0 -74
  37. data/lib/chef/knife/azure_vnet_list.rb +0 -35
  38. data/lib/chef/knife/bootstrap_azure.rb +0 -191
  39. data/lib/chef/knife/helpers/azure_base.rb +0 -394
@@ -1,94 +0,0 @@
1
- #
2
- # Author:: Barry Davis (barryd@jetstreamsoftware.com)
3
- # Copyright:: Copyright (c) Chef Software 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
- module Azure
20
- class Images
21
- def initialize(connection)
22
- @connection = connection
23
- end
24
-
25
- def load
26
- @images ||= begin
27
- osimages = get_images("OSImage") # get OSImages
28
- vmimages = get_images("VMImage") # get VMImages
29
-
30
- all_images = osimages.merge(vmimages)
31
- end
32
- end
33
-
34
- def all
35
- load.values
36
- end
37
-
38
- # img_type = OSImages or VMImage
39
- def get_images(img_type)
40
- images = {}
41
-
42
- if img_type == "OSImage"
43
- response = @connection.query_azure("images")
44
- elsif img_type == "VMImage"
45
- response = @connection.query_azure("vmimages")
46
- end
47
-
48
- unless response.to_s.empty?
49
- osimages = response.css(img_type)
50
-
51
- osimages.each do |image|
52
- item = Image.new(image)
53
- images[item.name] = item
54
- end
55
- end
56
-
57
- images
58
- end
59
-
60
- def is_os_image(image_name)
61
- os_images = get_images("OSImage").values
62
- os_images.detect { |img| img.name == image_name } ? true : false
63
- end
64
-
65
- def is_vm_image(image_name)
66
- vm_images = get_images("VMImage").values
67
- vm_images.detect { |img| img.name == image_name } ? true : false
68
- end
69
-
70
- def exists?(name)
71
- all.detect { |img| img.name == name } ? true : false
72
- end
73
-
74
- def find(name)
75
- load[name]
76
- end
77
- end
78
- end
79
-
80
- module Azure
81
- class Image
82
- attr_accessor :category, :label
83
- attr_accessor :name, :os, :eula, :description, :location
84
- def initialize(image)
85
- @category = image.at_css("Category").content
86
- @label = image.at_css("Label").content
87
- @name = image.at_css("Name").content
88
- @os = image.at_css("OS").content
89
- @location = image.at_css("Location").content.gsub(";", ", ") if image.at_css("Location")
90
- @eula = image.at_css("Eula").content if image.at_css("Eula")
91
- @description = image.at_css("Description").content if image.at_css("Description")
92
- end
93
- end
94
- end
@@ -1,78 +0,0 @@
1
- #
2
- # Author:: Aiman Alsari (aiman.alsari@gmail.com)
3
- # Copyright:: Copyright (c) Chef Software 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
- module Azure
20
- class Loadbalancer
21
- include AzureUtility
22
- attr_accessor :name, :service, :subnet, :vip
23
-
24
- def initialize(connection)
25
- @connection = connection
26
- end
27
-
28
- def load
29
- @lbs ||= begin
30
- @lbs = {}
31
- @connection.deploys.all.each do |deploy|
32
- @lbs.merge!(deploy.loadbalancers)
33
- end
34
- @lbs
35
- end
36
- end
37
-
38
- def all
39
- load.values
40
- end
41
-
42
- def exists?(name)
43
- load.key?(name)
44
- end
45
-
46
- def find(name)
47
- load[name]
48
- end
49
-
50
- def parse(lbXML, hostedservicename)
51
- @name = xml_content(lbXML, "Name")
52
- ip_configXML = lbXML.css("FrontendIpConfiguration")
53
- @subnet = xml_content(ip_configXML, "SubnetName")
54
- @vip = xml_content(ip_configXML, "StaticVirtualNetworkIPAddress")
55
- @service = hostedservicename
56
- self
57
- end
58
-
59
- def create(params)
60
- if params[:azure_lb_static_vip] && !params[:azure_subnet_name]
61
- Chef::Log.fatal "Unable to create Loadbalancer, :azure_subnet_name needs to be set if :azure_lb_static_vip is set"
62
- end
63
- builder = Nokogiri::XML::Builder.new(encoding: "utf-8") do |xml|
64
- xml.LoadBalancer(xmlns: "http://schemas.microsoft.com/windowsazure") do
65
- xml.Name params[:azure_load_balancer]
66
- xml.FrontendIpConfiguration do
67
- xml.Type "Private"
68
- xml.SubnetName params[:azure_subnet_name] if params[:azure_subnet_name]
69
- xml.StaticVirtualNetworkIPAddress params[:azure_lb_static_vip] if params[:azure_lb_static_vip]
70
- end
71
- end
72
- end
73
- deploy_name = @connection.deploys.get_deploy_name_for_hostedservice(params[:azure_dns_name])
74
- servicecall = "hostedservices/#{params[:azure_dns_name]}/deployments/#{deploy_name}/loadbalancers"
75
- @connection.query_azure(servicecall, "post", builder.doc.to_xml)
76
- end
77
- end
78
- end
@@ -1,125 +0,0 @@
1
- #
2
- # Author:: Barry Davis (barryd@jetstreamsoftware.com)
3
- # Copyright:: Copyright (c) Chef Software 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 "net/https"
20
- require "uri"
21
- require "nokogiri"
22
-
23
- module AzureAPI
24
-
25
- class Rest
26
- def initialize(params)
27
- @subscription_id = params[:azure_subscription_id]
28
- @pem_file = params[:azure_mgmt_cert]
29
- @host_name = params[:azure_api_host_name]
30
- @verify_ssl = params[:verify_ssl_cert]
31
- end
32
-
33
- def query_azure(service_name,
34
- verb = "get",
35
- body = "",
36
- params = "",
37
- services = true,
38
- content_type = nil)
39
- svc_str = services ? "/services" : ""
40
- uri = URI.parse("#{@host_name}/#{@subscription_id}#{svc_str}/#{service_name}")
41
- scheme = !uri.scheme ? "https://" : ""
42
- request_url = "#{scheme}#{@host_name}/#{@subscription_id}#{svc_str}/#{service_name}"
43
- print "."
44
- response = http_query(request_url, verb, body, params, content_type)
45
- if response.code.to_i == 307
46
- Chef::Log.debug "Redirect to #{response["Location"]}"
47
- response = http_query(response["Location"], verb, body, params, content_type)
48
- end
49
- @last_request_id = response["x-ms-request-id"]
50
- response
51
- end
52
-
53
- def http_query(request_url, verb, body, params, content_type = nil)
54
- uri = URI.parse(request_url)
55
- uri.query = params
56
- http = http_setup(uri)
57
- request = request_setup(uri, verb, body, content_type)
58
- response = http.request(request)
59
- @last_request_id = response["x-ms-request-id"]
60
- response
61
- end
62
-
63
- def query_for_completion
64
- uri = URI.parse("#{@host_name}/#{@subscription_id}/operations/#{@last_request_id}")
65
- scheme = !uri.scheme ? "https://" : ""
66
- request_url = "#{scheme}#{@host_name}/#{@subscription_id}/operations/#{@last_request_id}"
67
- response = http_query(request_url, "get", "", "")
68
- if response.code.to_i == 307
69
- Chef::Log.debug "Redirect to #{response["Location"]}"
70
- response = http_query(response["Location"], "get", "", "")
71
- end
72
- response
73
- end
74
-
75
- def http_setup(uri)
76
- http = Net::HTTP.new(uri.host, uri.port)
77
- store = OpenSSL::X509::Store.new
78
- store.set_default_paths
79
- http.cert_store = store
80
- if @verify_ssl
81
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
82
- else
83
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
84
- end
85
- http.use_ssl = true
86
- begin
87
- http.cert = OpenSSL::X509::Certificate.new(@pem_file)
88
- rescue OpenSSL::X509::CertificateError => err
89
- raise "Invalid Azure Certificate pem file. Error: #{err}"
90
- end
91
- http.key = OpenSSL::PKey::RSA.new(@pem_file)
92
- http
93
- end
94
-
95
- def request_setup(uri, verb, body, content_type)
96
- if verb == "get"
97
- request = Net::HTTP::Get.new(uri.request_uri)
98
- elsif verb == "post"
99
- request = Net::HTTP::Post.new(uri.request_uri)
100
- elsif verb == "delete"
101
- request = Net::HTTP::Delete.new(uri.request_uri)
102
- elsif verb == "put"
103
- request = Net::HTTP::Put.new(uri.request_uri)
104
- end
105
- text = verb == "put" && content_type.nil?
106
- request["x-ms-version"] = "2014-05-01"
107
- request["content-type"] = text ? "text/plain" : "application/xml"
108
- request["accept"] = "application/xml"
109
- request["accept-charset"] = "utf-8"
110
- request.body = body
111
- request
112
- end
113
-
114
- def showResponse(response)
115
- puts "=== response body ==="
116
- puts response.body
117
- puts "=== response.code ==="
118
- puts response.code
119
- puts "=== response.inspect ==="
120
- puts response.inspect
121
- puts "=== all of the headers ==="
122
- puts response.each_header { |h, j| puts h.inspect + " : " + j.inspect }
123
- end
124
- end
125
- end
@@ -1,717 +0,0 @@
1
- #
2
- # Author:: Barry Davis (barryd@jetstreamsoftware.com)
3
- # Copyright:: Copyright (c) Chef Software 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 "securerandom"
20
- require_relative "utility"
21
-
22
- module Azure
23
- class Roles
24
- include AzureUtility
25
- attr_accessor :connection, :roles
26
- def initialize(connection)
27
- @connection = connection
28
- @roles = nil
29
- end
30
-
31
- # do not use this unless you want a list of all roles(vms) in your subscription
32
- def all
33
- @roles = []
34
- @connection.deploys.all.each do |deploy|
35
- deploy.roles.each do |role|
36
- @roles << role
37
- end
38
- end
39
- @roles
40
- end
41
-
42
- def find_roles_within_hostedservice(hostedservicename)
43
- host = @connection.hosts.find(hostedservicename)
44
- host ? host.roles : nil # nil says invalid hosted service
45
- end
46
-
47
- def find_in_hosted_service(role_name, hostedservicename)
48
- host = @connection.hosts.find(hostedservicename)
49
- return nil if host.nil?
50
-
51
- host.find_role(role_name)
52
- end
53
-
54
- def find(role_name, params = nil)
55
- if params && params[:azure_dns_name]
56
- return find_in_hosted_service(role_name, params[:azure_dns_name])
57
- end
58
-
59
- all if @roles.nil?
60
-
61
- # TODO: - optimize this lookup
62
- @roles.each do |role|
63
- return role if role.name == role_name
64
- end
65
- nil
66
- end
67
-
68
- def alone_on_hostedservice(found_role)
69
- roles = find_roles_within_hostedservice(found_role.hostedservicename)
70
- return false if roles && roles.length > 1
71
-
72
- true
73
- end
74
-
75
- def exists?(name)
76
- find(name) != nil
77
- end
78
-
79
- def delete(params)
80
- role = find(params[:name])
81
- unless role.nil?
82
- roleXML = nil
83
- roleXML = @connection.query_azure("hostedservices/#{role.hostedservicename}", "get", "", "embed-detail=true")
84
- osdisk = roleXML.css(roleXML, "OSVirtualHardDisk")
85
- disk_name = xml_content(osdisk, "DiskName")
86
- storage_account_name = xml_content(osdisk, "MediaLink").gsub("http://", "").gsub(/.blob(.*)$/, "")
87
-
88
- if !params[:preserve_azure_os_disk] && !params[:preserve_azure_vhd] && !params[:wait]
89
- # default compmedia = true. So, it deletes role and associated resources
90
- check_and_delete_role_and_resources(params, role)
91
- else
92
- # compmedia = false. So, it deletes only role and not associated resources
93
- check_and_delete_role_and_resources(params, role, compmedia = false)
94
- check_and_delete_disks(params, disk_name)
95
- check_and_delete_service(params)
96
- end
97
- check_and_delete_storage(params, disk_name, storage_account_name)
98
- end
99
- end
100
-
101
- def check_and_delete_role_and_resources(params, role, compmedia = true)
102
- if alone_on_hostedservice(role)
103
- if !params[:preserve_azure_dns_name] && compmedia
104
- servicecall = "hostedservices/#{role.hostedservicename}"
105
- else
106
- servicecall = "hostedservices/#{role.hostedservicename}/deployments/#{role.deployname}"
107
- end
108
- else
109
- servicecall = "hostedservices/#{role.hostedservicename}/deployments" \
110
- "/#{role.deployname}/roles/#{role.name}"
111
- end
112
- if compmedia
113
- @connection.query_azure(servicecall, "delete", "", "comp=media", wait = params[:wait])
114
- else
115
- @connection.query_azure(servicecall, "delete")
116
- end
117
-
118
- # delete role from local cache as well.
119
- @connection.hosts.find(role.hostedservicename).delete_role(role)
120
- @roles.delete(role) if @roles
121
- end
122
-
123
- def check_and_delete_disks(params, disk_name)
124
- servicecall = "disks/#{disk_name}"
125
- unless params[:preserve_azure_os_disk]
126
- # OS Disk can only be deleted if it is detached from the VM.
127
- # So Iteratively check for disk detachment from the VM while waiting for 5 minutes ,
128
- # exit otherwise after 12 attempts.
129
- for attempt in 0..12
130
- break if @connection.query_azure(servicecall, "get").search("AttachedTo").text == ""
131
-
132
- attempt == 12 ? (puts "The associated disk could not be deleted due to time out.") : (sleep 25)
133
- end
134
- if params[:preserve_azure_vhd]
135
- @connection.query_azure(servicecall, "delete")
136
- else
137
- @connection.query_azure(servicecall, "delete", "", "comp=media", wait = params[:wait])
138
- end
139
- end
140
- end
141
-
142
- def check_and_delete_service(params)
143
- unless params[:preserve_azure_dns_name]
144
- unless params[:azure_dns_name].nil?
145
- roles_using_same_service = find_roles_within_hostedservice(params[:azure_dns_name])
146
- if roles_using_same_service.size <= 1
147
- servicecall = "hostedservices/" + params[:azure_dns_name]
148
- @connection.query_azure(servicecall, "delete")
149
- end
150
- end
151
- end
152
- end
153
-
154
- def check_and_delete_storage(params, disk_name, storage_account_name)
155
- if params[:delete_azure_storage_account]
156
- # Iteratively check for disk deletion
157
- for attempt in 0..12
158
- break unless @connection.query_azure("disks").search("Name").text.include?(disk_name)
159
-
160
- attempt == 12 ? (puts "The associated disk could not be deleted due to time out.") : (sleep 25)
161
- end
162
- begin
163
- @connection.query_azure("storageservices/#{storage_account_name}", "delete")
164
- rescue Exception => ex
165
- ui.warn(ex.message.to_s)
166
- ui.warn(ex.backtrace.join("\n").to_s)
167
- end
168
- end
169
- end
170
-
171
- def update(name, params)
172
- role = Role.new(@connection)
173
- roleExtensionXml = role.setup_extension(params)
174
- role.update(name, params, roleExtensionXml)
175
- end
176
-
177
- private :check_and_delete_role_and_resources, :check_and_delete_disks, :check_and_delete_service, :check_and_delete_storage
178
- end
179
-
180
- class Role
181
- include AzureUtility
182
- attr_accessor :connection, :name, :status, :size, :ipaddress, :publicipaddress
183
- attr_accessor :sshport, :hostedservicename, :deployname, :thumbprint
184
- attr_accessor :winrmport
185
- attr_accessor :hostname, :tcpports, :udpports
186
- attr_accessor :role_xml, :os_type, :os_version
187
-
188
- TCP_ENDPOINTS_MAPPING = { "3389" => "Remote Desktop",
189
- "5986" => "PowerShell",
190
- "22" => "SSH",
191
- "21" => "FTP",
192
- "25" => "SMTP",
193
- "53" => "DNS",
194
- "80" => "HTTP",
195
- "110" => "POP3",
196
- "143" => "IMAP",
197
- "389" => "LDAP",
198
- "443" => "HTTPs",
199
- "587" => "SMTPS",
200
- "995" => "POP3S",
201
- "993" => "IMAPS",
202
- "1433" => "MSSQL",
203
- "3306" => "MySQL" }.freeze
204
-
205
- def initialize(connection)
206
- @connection = connection
207
- end
208
-
209
- def parse(roleXML, hostedservicename, deployname)
210
- @name = xml_content(roleXML, "RoleName")
211
- @status = xml_content(roleXML, "InstanceStatus")
212
- @size = xml_content(roleXML, "InstanceSize")
213
- @ipaddress = xml_content(roleXML, "IpAddress")
214
- @hostname = xml_content(roleXML, "HostName")
215
- @hostedservicename = hostedservicename
216
- @deployname = deployname
217
- @thumbprint = fetch_thumbprint
218
- @tcpports = []
219
- @udpports = []
220
-
221
- endpoints = roleXML.css("InstanceEndpoint")
222
- @publicipaddress = xml_content(endpoints[0], "Vip") unless endpoints.empty?
223
- endpoints.each do |endpoint|
224
- if xml_content(endpoint, "Name").casecmp("ssh").zero?
225
- @sshport = xml_content(endpoint, "PublicPort")
226
- elsif xml_content(endpoint, "Name").casecmp("winrm").zero?
227
- @winrmport = xml_content(endpoint, "PublicPort")
228
- else
229
- hash = {}
230
- hash["Name"] = xml_content(endpoint, "Name")
231
- hash["Vip"] = xml_content(endpoint, "Vip")
232
- hash["PublicPort"] = xml_content(endpoint, "PublicPort")
233
- hash["LocalPort"] = xml_content(endpoint, "LocalPort")
234
-
235
- if xml_content(endpoint, "Protocol") == "tcp"
236
- @tcpports << hash
237
- else # == 'udp'
238
- @udpports << hash
239
- end
240
- end
241
- end
242
- end
243
-
244
- def parse_role_list_xml(roleListXML)
245
- @role_xml = roleListXML
246
- os_disk_xml = roleListXML.css("OSVirtualHardDisk")
247
- @os_type = xml_content(os_disk_xml, "OS")
248
- @os_version = xml_content(os_disk_xml, "SourceImageName")
249
- end
250
-
251
- # Expects endpoint_param_string to be in the form {localport}:{publicport}:{lb_set_name}:{lb_probe_path}
252
- # Only localport is mandatory.
253
- def parse_endpoint_from_params(protocol, _azure_vm_name, endpoint_param_string)
254
- fields = endpoint_param_string.split(":").map(&:strip)
255
- hash = {}
256
- hash["LocalPort"] = fields[0]
257
- hash["Port"] = fields[1] || fields[0]
258
- hash["LoadBalancerName"] = fields[2] if fields[2] != "EXTERNAL" # TODO: hackity hack.. Shouldn't use magic words.
259
- hash["LoadBalancedEndpointSetName"] = fields[3]
260
- hash["Protocol"] = protocol
261
- if TCP_ENDPOINTS_MAPPING.include?(hash["Port"]) && protocol == "TCP"
262
- hash["Name"] = TCP_ENDPOINTS_MAPPING[hash["Port"]]
263
- else
264
- hash["Name"] = "#{protocol}Endpoint_chef_#{fields[0]}"
265
- end
266
- if fields[2]
267
- hash["LoadBalancerProbe"] = {}
268
- hash["LoadBalancerProbe"]["Path"] = fields[4]
269
- hash["LoadBalancerProbe"]["Port"] = fields[0]
270
- hash["LoadBalancerProbe"]["Protocol"] = fields[4] ? "HTTP" : protocol
271
- end
272
- hash
273
- end
274
-
275
- def find_deploy(params)
276
- @connection.hosts.find(params[:azure_dns_name]).deploys[0] # TODO: this relies on the 'production only' bug.
277
- end
278
-
279
- def add_endpoints_to_xml(xml, endpoints, params)
280
- existing_endpoints = find_deploy(params).input_endpoints
281
-
282
- endpoints.each do |ep|
283
- if existing_endpoints
284
- existing_endpoints.each do |eep|
285
- ep = eep if eep["LoadBalancedEndpointSetName"] && ep["LoadBalancedEndpointSetName"] && (eep["LoadBalancedEndpointSetName"] == ep["LoadBalancedEndpointSetName"])
286
- end
287
- end
288
-
289
- if ep["Port"] == params[:port] && ep["Protocol"].casecmp("tcp").zero?
290
- puts("Skipping tcp-endpoints: #{ep["LocalPort"]} because this port is already in use by ssh/winrm endpoint in current VM.")
291
- next
292
- end
293
-
294
- xml.InputEndpoint do
295
- xml.LoadBalancedEndpointSetName ep["LoadBalancedEndpointSetName"] if ep["LoadBalancedEndpointSetName"]
296
- xml.LocalPort ep["LocalPort"]
297
- xml.Name ep["Name"]
298
- xml.Port ep["Port"]
299
- if ep["LoadBalancerProbe"]
300
- xml.LoadBalancerProbe do
301
- xml.Path ep["LoadBalancerProbe"]["Path"] if ep["LoadBalancerProbe"]["Path"]
302
- xml.Port ep["LoadBalancerProbe"]["Port"]
303
- xml.Protocol ep["LoadBalancerProbe"]["Protocol"]
304
- xml.IntervalInSeconds ep["LoadBalancerProbe"]["IntervalInSeconds"] if ep["LoadBalancerProbe"]["IntervalInSeconds"]
305
- xml.TimeoutInSeconds ep["LoadBalancerProbe"]["TimeoutInSeconds"] if ep["LoadBalancerProbe"]["TimeoutInSeconds"]
306
- end
307
- end
308
- xml.Protocol ep["Protocol"]
309
- xml.EnableDirectServerReturn ep["EnableDirectServerReturn"] if ep["EnableDirectServerReturn"]
310
- xml.LoadBalancerName ep["LoadBalancerName"] if ep["LoadBalancerName"]
311
- xml.IdleTimeoutInMinutes ep["IdleTimeoutInMinutes"] if ep["IdleTimeoutInMinutes"]
312
- end
313
- end
314
- end
315
-
316
- def fetch_thumbprint
317
- query_result = connection.query_azure("hostedservices/#{@hostedservicename}/deployments/#{@hostedservicename}/roles/#{@name}")
318
- query_result.at_css("DefaultWinRmCertificateThumbprint").nil? ? "" : query_result.at_css("DefaultWinRmCertificateThumbprint").text
319
- end
320
-
321
- def setup(params)
322
- azure_user_domain_name = params[:azure_user_domain_name] || params[:azure_domain_name]
323
- builder = Nokogiri::XML::Builder.new do |xml|
324
- xml.PersistentVMRole(
325
- "xmlns" => "http://schemas.microsoft.com/windowsazure",
326
- "xmlns:i" => "http://www.w3.org/2001/XMLSchema-instance"
327
- ) do
328
- xml.RoleName { xml.text params[:azure_vm_name] }
329
- xml.OsVersion("i:nil" => "true")
330
- xml.RoleType "PersistentVMRole"
331
-
332
- xml.ConfigurationSets do
333
- if params[:os_type] == "Linux"
334
- xml.ConfigurationSet("i:type" => "LinuxProvisioningConfigurationSet") do
335
- xml.ConfigurationSetType "LinuxProvisioningConfiguration"
336
- xml.HostName params[:azure_vm_name]
337
- xml.UserName params[:connection_user]
338
- if params[:ssh_identity_file].nil?
339
- xml.UserPassword params[:connection_password]
340
- xml.DisableSshPasswordAuthentication "false"
341
- else
342
- xml.DisableSshPasswordAuthentication "true"
343
- xml.SSH do
344
- xml.PublicKeys do
345
- xml.PublicKey do
346
- xml.Fingerprint params[:fingerprint].to_s.upcase
347
- xml.Path "/home/" + params[:connection_user] + "/.ssh/authorized_keys"
348
- end
349
- end
350
- end
351
- end
352
- end
353
- elsif params[:os_type] == "Windows"
354
- xml.ConfigurationSet("i:type" => "WindowsProvisioningConfigurationSet") do
355
- xml.ConfigurationSetType "WindowsProvisioningConfiguration"
356
- xml.ComputerName params[:azure_vm_name]
357
- xml.AdminPassword params[:admin_password]
358
- xml.ResetPasswordOnFirstLogon "false"
359
- xml.EnableAutomaticUpdates "false"
360
- if params[:azure_domain_name]
361
- xml.DomainJoin do
362
- xml.Credentials do
363
- xml.Domain azure_user_domain_name
364
- xml.Username params[:azure_domain_user]
365
- xml.Password params[:azure_domain_passwd]
366
- end
367
- xml.JoinDomain params[:azure_domain_name]
368
- xml.MachineObjectOU params[:azure_domain_ou_dn] if params[:azure_domain_ou_dn]
369
- end
370
- end
371
- if params[:connection_protocol].casecmp("winrm").zero?
372
- if params[:ssl_cert_fingerprint]
373
- xml.StoredCertificateSettings do
374
- xml.CertificateSetting do
375
- xml.StoreLocation "LocalMachine"
376
- xml.StoreName "My"
377
- xml.Thumbprint params[:ssl_cert_fingerprint]
378
- end
379
- end
380
- end
381
- xml.WinRM do
382
- xml.Listeners do
383
- if params[:winrm_ssl] || params[:ssl_cert_fingerprint]
384
- xml.Listener do
385
- xml.CertificateThumbprint params[:ssl_cert_fingerprint] if params[:ssl_cert_fingerprint]
386
- xml.Protocol "Https"
387
- end
388
- else
389
- xml.Listener do
390
- xml.Protocol "Http"
391
- end
392
- end
393
- end
394
- end
395
- end
396
- xml.AdminUsername params[:connection_user]
397
- if params[:connection_protocol].casecmp("winrm").zero? && (params[:winrm_max_timeout] || params[:winrm_max_memory_per_shell])
398
- xml.AdditionalUnattendContent do
399
- xml.Passes do
400
- xml.UnattendPass do
401
- xml.PassName "oobeSystem"
402
- xml.Components do
403
- xml.UnattendComponent do
404
- xml.ComponentName "Microsoft-Windows-Shell-Setup"
405
- xml.ComponentSettings do
406
- xml.ComponentSetting do
407
- xml.SettingName "AutoLogon"
408
- xml.Content Base64.encode64(
409
- Nokogiri::XML::Builder.new do |auto_logon_xml|
410
- auto_logon_xml.AutoLogon do
411
- auto_logon_xml.Username params[:connection_user]
412
- auto_logon_xml.Password do
413
- auto_logon_xml.Value params[:admin_password]
414
- auto_logon_xml.PlainText true
415
- end
416
- auto_logon_xml.LogonCount 1
417
- auto_logon_xml.Enabled true
418
- end
419
- end.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::NO_DECLARATION)
420
- ).strip
421
- end
422
- xml.ComponentSetting do
423
- xml.SettingName "FirstLogonCommands"
424
- xml.Content Base64.encode64(
425
- Nokogiri::XML::Builder.new do |first_logon_xml|
426
- first_logon_xml.FirstLogonCommands do
427
- if params[:winrm_max_timeout]
428
- first_logon_xml.SynchronousCommand("wcm:action" => "add") do
429
- first_logon_xml.Order 1
430
- first_logon_xml.CommandLine "cmd.exe /c winrm set winrm/config @{MaxTimeoutms=\"#{params[:winrm_max_timeout]}\"}"
431
- first_logon_xml.Description "Bump WinRM max timeout to #{params[:winrm_max_timeout]} milliseconds"
432
- end
433
- end
434
-
435
- if params[:winrm_max_memory_per_shell]
436
- first_logon_xml.SynchronousCommand("wcm:action" => "add") do
437
- first_logon_xml.Order 2
438
- first_logon_xml.CommandLine "cmd.exe /c winrm set winrm/config/winrs @{MaxMemoryPerShellMB=\"#{params[:winrm_max_memory_per_shell]}\"}"
439
- first_logon_xml.Description "Bump WinRM max memory per shell to #{params[:winrm_max_memory_per_shell]} MB"
440
- end
441
- end
442
- end
443
- end.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::NO_DECLARATION)
444
- ).strip
445
- end
446
- end
447
- end
448
- end
449
- end
450
- end
451
- end
452
- end
453
- end
454
- end
455
-
456
- xml.ConfigurationSet("i:type" => "NetworkConfigurationSet") do
457
- xml.ConfigurationSetType "NetworkConfiguration"
458
- xml.InputEndpoints do
459
- # 1. connection_protocol = 'winrm' for windows => Set winrm port
460
- # 2. connection_protocol = 'ssh' for windows and linux => Set ssh port
461
- # 3. connection_protocol = 'cloud-api' for windows and linux => Set no port
462
- if (params[:os_type] == "Windows") && params[:connection_protocol].casecmp("winrm").zero?
463
- xml.InputEndpoint do
464
- if params[:winrm_ssl]
465
- xml.LocalPort "5986"
466
- else
467
- xml.LocalPort "5985"
468
- end
469
- xml.Name "WinRM"
470
- xml.Port params[:port]
471
- xml.Protocol "TCP"
472
- end
473
- elsif params[:connection_protocol].casecmp("ssh").zero?
474
- xml.InputEndpoint do
475
- xml.LocalPort "22"
476
- xml.Name "SSH"
477
- xml.Port params[:port]
478
- xml.Protocol "TCP"
479
- end
480
- end
481
- all_endpoints = []
482
-
483
- if params[:tcp_endpoints]
484
- params[:tcp_endpoints].split(",").map(&:strip).each do |endpoint|
485
- all_endpoints << parse_endpoint_from_params("TCP", params[:azure_vm_name], endpoint)
486
- end
487
- end
488
- if params[:udp_endpoints]
489
- params[:udp_endpoints].split(",").map(&:strip).each do |endpoint|
490
- all_endpoints << parse_endpoint_from_params("UDP", params[:azure_vm_name], endpoint)
491
- end
492
- end
493
- add_endpoints_to_xml(xml, all_endpoints, params) if all_endpoints.any?
494
- end
495
- if params[:azure_subnet_name]
496
- xml.SubnetNames do
497
- xml.SubnetName params[:azure_subnet_name]
498
- end
499
- end
500
- end
501
- end
502
-
503
- # Azure resource extension support
504
- if params[:connection_protocol] == "cloud-api"
505
- xml.ResourceExtensionReferences do
506
- xml.ResourceExtensionReference do
507
- xml.ReferenceName params[:chef_extension]
508
- xml.Publisher params[:chef_extension_publisher]
509
- xml.Name params[:chef_extension]
510
- xml.Version params[:chef_extension_version]
511
- xml.ResourceExtensionParameterValues do
512
- if params[:chef_extension_public_param]
513
- xml.ResourceExtensionParameterValue do
514
- xml.Key "PublicParams"
515
- xml.Value Base64.encode64(params[:chef_extension_public_param].to_json)
516
- xml.Type "Public"
517
- end
518
- end
519
- if params[:chef_extension_private_param]
520
- xml.ResourceExtensionParameterValue do
521
- xml.Key "PrivateParams"
522
- xml.Value Base64.encode64(params[:chef_extension_private_param].to_json)
523
- xml.Type "Private"
524
- end
525
- end
526
- end
527
- xml.State "Enable"
528
- end
529
- end
530
- end
531
-
532
- if params[:azure_availability_set]
533
- xml.AvailabilitySetName params[:azure_availability_set]
534
- end
535
-
536
- xml.VMImageName params[:azure_source_image] if params[:is_vm_image]
537
-
538
- xml.Label Base64.encode64(params[:azure_vm_name]).strip
539
-
540
- # OSVirtualHardDisk not required in case azure_source_image is a VMImage
541
- unless params[:is_vm_image]
542
- xml.OSVirtualHardDisk do
543
- disk_name = params[:azure_os_disk_name] || "disk_" + SecureRandom.uuid
544
- xml.DiskName disk_name
545
- domain_suffix = params[:azure_api_host_name] ? params[:azure_api_host_name].scan(/core.*/)[0] : ""
546
- xml.MediaLink "http://" + params[:azure_storage_account] + ".blob." + domain_suffix + "/vhds/" + disk_name + ".vhd"
547
- xml.SourceImageName params[:azure_source_image]
548
- end
549
- end
550
-
551
- xml.RoleSize params[:azure_vm_size]
552
- xml.ProvisionGuestAgent true if params[:connection_protocol] == "cloud-api"
553
- end
554
- end
555
- builder.doc
556
- end
557
-
558
- def create(params, roleXML)
559
- servicecall = "hostedservices/#{params[:azure_dns_name]}/deployments" \
560
- "/#{params["deploy_name"]}/roles"
561
- @connection.query_azure(servicecall, "post", roleXML.to_xml)
562
- end
563
-
564
- def setup_extension(params)
565
- ## add Chef Extension config in role_xml retrieved from the server
566
- puts "Adding Chef Extension config in server role..."
567
- role_xml = update_role_xml_for_extension(params[:role_xml], params)
568
-
569
- ## role_xml can't be used for update as it has additional tags like
570
- ## role_name, osversion etc. which update API does not support, also the
571
- ## xml is the child of parent node 'Deployment' in XML, so instead of
572
- ## modifying the role_xml to fit for our requirements, we create
573
- ## new XML (with Chef Extension config and other pre-existing VM config)
574
- ## using the required values of the updated role_xml
575
- builder = Nokogiri::XML::Builder.new do |xml|
576
- xml.PersistentVMRole(
577
- "xmlns" => "http://schemas.microsoft.com/windowsazure",
578
- "xmlns:i" => "http://www.w3.org/2001/XMLSchema-instance"
579
- ) do
580
- xml.ConfigurationSets role_xml.at_css("ConfigurationSets").children unless role_xml.at_css("ConfigurationSets").nil?
581
- xml.ResourceExtensionReferences role_xml.at_css("ResourceExtensionReferences").children unless role_xml.at_css("ResourceExtensionReferences").nil?
582
- xml.AvailabilitySetName role_xml.at_css("AvailabilitySetName").children unless role_xml.at_css("AvailabilitySetName").nil?
583
- xml.DataVirtualHardDisks role_xml.at_css("DataVirtualHardDisks").children unless role_xml.at_css("DataVirtualHardDisks").nil?
584
- xml.OSVirtualHardDisk role_xml.at_css("OSVirtualHardDisk").children unless role_xml.at_css("OSVirtualHardDisk").nil?
585
- xml.RoleSize role_xml.at_css("RoleSize").children unless role_xml.at_css("RoleSize").nil?
586
- xml.ProvisionGuestAgent role_xml.at_css("ProvisionGuestAgent").children unless role_xml.at_css("ProvisionGuestAgent").nil?
587
- end
588
- end
589
-
590
- builder.doc.to_xml.gsub("&lt\;", "<").gsub("&gt\;", ">")
591
- end
592
-
593
- def update_role_xml_for_extension(roleXML, params)
594
- ## check if 'ResourceExtensionReferences' node already exist in the XML,
595
- ## if no add it, else retrieve the object of the existing node
596
- add_resource_extension_references = roleXML.at_css("ResourceExtensionReferences").nil?
597
-
598
- if add_resource_extension_references
599
- resource_extension_references = Nokogiri::XML::Node.new("ResourceExtensionReferences", roleXML)
600
- else
601
- resource_extension_references = roleXML.css("ResourceExtensionReferences")
602
- end
603
-
604
- ## check if Azure Chef Extension is already installed on the given server,
605
- ## if no than install it, else raise error saying that the extension is
606
- ## already installed
607
- ext = nil
608
- unless add_resource_extension_references
609
- unless resource_extension_references.at_css("ReferenceName").nil?
610
- resource_extension_references.css("ReferenceName").each { |node| ext = node if node.content == params[:chef_extension] }
611
- end
612
- end
613
-
614
- add_resource_extension_reference = ext.nil?
615
-
616
- ## create Azure Chef Extension config and add it in the role_xml
617
- if add_resource_extension_reference
618
- resource_extension_reference = Nokogiri::XML::Node.new("ResourceExtensionReference", roleXML)
619
-
620
- reference_name = Nokogiri::XML::Node.new("ReferenceName", roleXML)
621
- reference_name.content = params[:chef_extension]
622
- resource_extension_reference.add_child(reference_name)
623
-
624
- publisher = Nokogiri::XML::Node.new("Publisher", roleXML)
625
- publisher.content = params[:chef_extension_publisher]
626
- resource_extension_reference.add_child(publisher)
627
-
628
- name = Nokogiri::XML::Node.new("Name", roleXML)
629
- name.content = params[:chef_extension]
630
- resource_extension_reference.add_child(name)
631
-
632
- version = Nokogiri::XML::Node.new("Version", roleXML)
633
- version.content = params[:chef_extension_version]
634
- resource_extension_reference.add_child(version)
635
-
636
- resource_extension_parameter_values = Nokogiri::XML::Node.new("ResourceExtensionParameterValues", roleXML)
637
- if params[:chef_extension_public_param]
638
- resource_extension_parameter_value = Nokogiri::XML::Node.new("ResourceExtensionParameterValue", roleXML)
639
-
640
- key = Nokogiri::XML::Node.new("Key", roleXML)
641
- key.content = "PublicParams"
642
- resource_extension_parameter_value.add_child(key)
643
-
644
- value = Nokogiri::XML::Node.new("Value", roleXML)
645
- value.content = Base64.encode64(params[:chef_extension_public_param].to_json)
646
- resource_extension_parameter_value.add_child(value)
647
-
648
- type = Nokogiri::XML::Node.new("Type", roleXML)
649
- type.content = "Public"
650
- resource_extension_parameter_value.add_child(type)
651
-
652
- resource_extension_parameter_values.add_child(resource_extension_parameter_value)
653
- end
654
-
655
- if params[:chef_extension_private_param]
656
- resource_extension_parameter_value = Nokogiri::XML::Node.new("ResourceExtensionParameterValue", roleXML)
657
-
658
- key = Nokogiri::XML::Node.new("Key", roleXML)
659
- key.content = "PrivateParams"
660
- resource_extension_parameter_value.add_child(key)
661
-
662
- value = Nokogiri::XML::Node.new("Value", roleXML)
663
- value.content = Base64.encode64(params[:chef_extension_private_param].to_json)
664
- resource_extension_parameter_value.add_child(value)
665
-
666
- type = Nokogiri::XML::Node.new("Type", roleXML)
667
- type.content = "Private"
668
- resource_extension_parameter_value.add_child(type)
669
-
670
- resource_extension_parameter_values.add_child(resource_extension_parameter_value)
671
- end
672
-
673
- resource_extension_reference.add_child(resource_extension_parameter_values)
674
-
675
- state = Nokogiri::XML::Node.new("State", roleXML)
676
- state.content = "enable"
677
- resource_extension_reference.add_child(state)
678
-
679
- if add_resource_extension_references
680
- resource_extension_references.add_child(resource_extension_reference)
681
- else
682
- resource_extension_references.last.add_child(resource_extension_reference)
683
- end
684
-
685
- roleXML.add_child(resource_extension_references) if add_resource_extension_references
686
-
687
- add_provision_guest_agent = roleXML.at_css("ProvisionGuestAgent").nil?
688
-
689
- if add_provision_guest_agent
690
- provision_guest_agent = Nokogiri::XML::Node.new("ProvisionGuestAgent", roleXML)
691
- provision_guest_agent.content = true
692
- else
693
- provision_guest_agent = roleXML.css("ProvisionGuestAgent")
694
- provision_guest_agent.first.content = true
695
- end
696
-
697
- roleXML.add_child(provision_guest_agent) if add_provision_guest_agent
698
- else ## raise error as Chef Extension is already installed on the server
699
- raise "Chef Extension is already installed on the server #{params[:azure_vm_name]}."
700
- end
701
-
702
- roleXML
703
- end
704
-
705
- def update(name, params, roleXML)
706
- puts "Updating server role..."
707
- servicecall = "hostedservices/#{params[:azure_dns_name]}" \
708
- "/deployments/#{params[:deploy_name]}/roles/#{name}"
709
- ret_val = @connection.query_azure(servicecall, "put", roleXML, "", true, true, "application/xml")
710
- error_code, error_message = error_from_response_xml(ret_val)
711
- unless error_code.empty?
712
- Chef::Log.debug(ret_val.to_s)
713
- raise "Unable to update role:" + error_code + " : " + error_message
714
- end
715
- end
716
- end
717
- end