knife-azure 3.0.0 → 4.0.0

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