azure-plus 0.6.5

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 (298) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.travis.yml +11 -0
  4. data/ChangeLog.txt +40 -0
  5. data/Gemfile +16 -0
  6. data/README.md +590 -0
  7. data/Rakefile +129 -0
  8. data/azure.gemspec +47 -0
  9. data/lib/azure.rb +52 -0
  10. data/lib/azure/base_management/affinity_group.rb +32 -0
  11. data/lib/azure/base_management/base_management_service.rb +238 -0
  12. data/lib/azure/base_management/location.rb +27 -0
  13. data/lib/azure/base_management/management_http_request.rb +244 -0
  14. data/lib/azure/base_management/serialization.rb +129 -0
  15. data/lib/azure/base_management/sql_management_http_request.rb +45 -0
  16. data/lib/azure/blob/blob.rb +32 -0
  17. data/lib/azure/blob/blob_service.rb +1424 -0
  18. data/lib/azure/blob/block.rb +31 -0
  19. data/lib/azure/blob/container.rb +32 -0
  20. data/lib/azure/blob/serialization.rb +285 -0
  21. data/lib/azure/cloud_service_management/cloud_service.rb +38 -0
  22. data/lib/azure/cloud_service_management/cloud_service_management_service.rb +361 -0
  23. data/lib/azure/cloud_service_management/configuration_set.rb +28 -0
  24. data/lib/azure/cloud_service_management/deployment.rb +64 -0
  25. data/lib/azure/cloud_service_management/input_endpoint.rb +29 -0
  26. data/lib/azure/cloud_service_management/instance_endpoint.rb +31 -0
  27. data/lib/azure/cloud_service_management/persistent_vm_downtime.rb +29 -0
  28. data/lib/azure/cloud_service_management/role.rb +30 -0
  29. data/lib/azure/cloud_service_management/role_instance.rb +40 -0
  30. data/lib/azure/cloud_service_management/serialization.rb +329 -0
  31. data/lib/azure/cloud_service_management/virtual_ip.rb +29 -0
  32. data/lib/azure/core.rb +39 -0
  33. data/lib/azure/core/auth/authorizer.rb +36 -0
  34. data/lib/azure/core/auth/shared_key.rb +110 -0
  35. data/lib/azure/core/auth/shared_key_lite.rb +48 -0
  36. data/lib/azure/core/auth/signer.rb +48 -0
  37. data/lib/azure/core/configuration.rb +211 -0
  38. data/lib/azure/core/error.rb +22 -0
  39. data/lib/azure/core/filtered_service.rb +44 -0
  40. data/lib/azure/core/http/debug_filter.rb +36 -0
  41. data/lib/azure/core/http/http_error.rb +88 -0
  42. data/lib/azure/core/http/http_filter.rb +53 -0
  43. data/lib/azure/core/http/http_request.rb +159 -0
  44. data/lib/azure/core/http/http_response.rb +140 -0
  45. data/lib/azure/core/http/retry_policy.rb +74 -0
  46. data/lib/azure/core/http/signer_filter.rb +34 -0
  47. data/lib/azure/core/service.rb +63 -0
  48. data/lib/azure/core/signed_service.rb +43 -0
  49. data/lib/azure/core/utility.rb +190 -0
  50. data/lib/azure/queue/message.rb +30 -0
  51. data/lib/azure/queue/queue.rb +29 -0
  52. data/lib/azure/queue/queue_service.rb +568 -0
  53. data/lib/azure/queue/serialization.rb +106 -0
  54. data/lib/azure/service/access_policy.rb +26 -0
  55. data/lib/azure/service/enumeration_results.rb +21 -0
  56. data/lib/azure/service/logging.rb +32 -0
  57. data/lib/azure/service/metrics.rb +31 -0
  58. data/lib/azure/service/retention_policy.rb +25 -0
  59. data/lib/azure/service/serialization.rb +240 -0
  60. data/lib/azure/service/signed_identifier.rb +30 -0
  61. data/lib/azure/service/storage_service.rb +79 -0
  62. data/lib/azure/service/storage_service_properties.rb +32 -0
  63. data/lib/azure/service_bus/action.rb +21 -0
  64. data/lib/azure/service_bus/auth/wrap_service.rb +89 -0
  65. data/lib/azure/service_bus/auth/wrap_signer.rb +69 -0
  66. data/lib/azure/service_bus/brokered_message.rb +124 -0
  67. data/lib/azure/service_bus/brokered_message_serializer.rb +159 -0
  68. data/lib/azure/service_bus/correlation_filter.rb +45 -0
  69. data/lib/azure/service_bus/empty_rule_action.rb +30 -0
  70. data/lib/azure/service_bus/false_filter.rb +38 -0
  71. data/lib/azure/service_bus/filter.rb +21 -0
  72. data/lib/azure/service_bus/interval.rb +104 -0
  73. data/lib/azure/service_bus/queue.rb +230 -0
  74. data/lib/azure/service_bus/resource.rb +109 -0
  75. data/lib/azure/service_bus/rule.rb +98 -0
  76. data/lib/azure/service_bus/rule_aspect.rb +34 -0
  77. data/lib/azure/service_bus/serialization.rb +160 -0
  78. data/lib/azure/service_bus/service_bus_service.rb +829 -0
  79. data/lib/azure/service_bus/sql_filter.rb +50 -0
  80. data/lib/azure/service_bus/sql_rule_action.rb +50 -0
  81. data/lib/azure/service_bus/subscription.rb +184 -0
  82. data/lib/azure/service_bus/topic.rb +187 -0
  83. data/lib/azure/service_bus/true_filter.rb +38 -0
  84. data/lib/azure/sql_database_management/serialization.rb +111 -0
  85. data/lib/azure/sql_database_management/sql_database.rb +31 -0
  86. data/lib/azure/sql_database_management/sql_database_management_service.rb +200 -0
  87. data/lib/azure/storage_management/serialization.rb +184 -0
  88. data/lib/azure/storage_management/storage_account.rb +40 -0
  89. data/lib/azure/storage_management/storage_management_service.rb +166 -0
  90. data/lib/azure/table/auth/shared_key.rb +92 -0
  91. data/lib/azure/table/auth/shared_key_lite.rb +44 -0
  92. data/lib/azure/table/batch.rb +330 -0
  93. data/lib/azure/table/batch_response.rb +118 -0
  94. data/lib/azure/table/edmtype.rb +127 -0
  95. data/lib/azure/table/entity.rb +31 -0
  96. data/lib/azure/table/guid.rb +24 -0
  97. data/lib/azure/table/query.rb +112 -0
  98. data/lib/azure/table/serialization.rb +108 -0
  99. data/lib/azure/table/table_service.rb +560 -0
  100. data/lib/azure/version.rb +31 -0
  101. data/lib/azure/virtual_machine_image_management/serialization.rb +51 -0
  102. data/lib/azure/virtual_machine_image_management/virtual_machine_disk.rb +25 -0
  103. data/lib/azure/virtual_machine_image_management/virtual_machine_image.rb +25 -0
  104. data/lib/azure/virtual_machine_image_management/virtual_machine_image_management_service.rb +66 -0
  105. data/lib/azure/virtual_machine_management/serialization.rb +436 -0
  106. data/lib/azure/virtual_machine_management/virtual_machine.rb +42 -0
  107. data/lib/azure/virtual_machine_management/virtual_machine_management_service.rb +549 -0
  108. data/lib/azure/virtual_network_management/serialization.rb +186 -0
  109. data/lib/azure/virtual_network_management/virtual_network.rb +36 -0
  110. data/lib/azure/virtual_network_management/virtual_network_management_service.rb +109 -0
  111. data/test/fixtures/32px-fulls-black.jpg +0 -0
  112. data/test/fixtures/affinity_group.xml +33 -0
  113. data/test/fixtures/all_containers.xml +20 -0
  114. data/test/fixtures/all_tables.xml +22 -0
  115. data/test/fixtures/certificate.pem +21 -0
  116. data/test/fixtures/container_acl.xml +11 -0
  117. data/test/fixtures/create_sql_database_server.xml +2 -0
  118. data/test/fixtures/create_storage_desc_error.xml +5 -0
  119. data/test/fixtures/create_storage_extendedprop_error.xml +8 -0
  120. data/test/fixtures/create_storage_extendedpropname_error.xml +6 -0
  121. data/test/fixtures/create_storage_full_error.xml +6 -0
  122. data/test/fixtures/create_storage_label_error.xml +5 -0
  123. data/test/fixtures/create_storage_location_error.xml +5 -0
  124. data/test/fixtures/create_storage_name_error.xml +6 -0
  125. data/test/fixtures/create_table_response_entry.xml +15 -0
  126. data/test/fixtures/delete_storage_container_error.xml +5 -0
  127. data/test/fixtures/delete_storage_error.xml +5 -0
  128. data/test/fixtures/deployment_error.xml +5 -0
  129. data/test/fixtures/empty_xml_file +0 -0
  130. data/test/fixtures/get_deployment.xml +94 -0
  131. data/test/fixtures/get_deployment_existing.xml +94 -0
  132. data/test/fixtures/get_deployment_missing.xml +5 -0
  133. data/test/fixtures/get_storage_account_error.xml +5 -0
  134. data/test/fixtures/get_storage_account_properties.xml +32 -0
  135. data/test/fixtures/get_storage_account_properties_new.xml +32 -0
  136. data/test/fixtures/http_error.xml +5 -0
  137. data/test/fixtures/insert_entity_response_entry.xml +25 -0
  138. data/test/fixtures/list_affinity_groups.xml +22 -0
  139. data/test/fixtures/list_blobs.xml +121 -0
  140. data/test/fixtures/list_block_all_with_none_committed.xml +22 -0
  141. data/test/fixtures/list_blocks_all.xml +23 -0
  142. data/test/fixtures/list_blocks_committed.xml +13 -0
  143. data/test/fixtures/list_cloud_services.xml +39 -0
  144. data/test/fixtures/list_containers.xml +38 -0
  145. data/test/fixtures/list_firewall_management_endpoint.xml +27 -0
  146. data/test/fixtures/list_images.xml +110 -0
  147. data/test/fixtures/list_locations.xml +62 -0
  148. data/test/fixtures/list_page_ranges.xml +11 -0
  149. data/test/fixtures/list_sql_database.xml +36 -0
  150. data/test/fixtures/list_sql_server_firewall.xml +23 -0
  151. data/test/fixtures/list_storage_account_single.xml +25 -0
  152. data/test/fixtures/list_storage_accounts.xml +46 -0
  153. data/test/fixtures/list_virtual_networks.xml +92 -0
  154. data/test/fixtures/logging.xml +11 -0
  155. data/test/fixtures/management_certificate.pem +55 -0
  156. data/test/fixtures/messages.xml +12 -0
  157. data/test/fixtures/metrics.xml +10 -0
  158. data/test/fixtures/privatekey.key +28 -0
  159. data/test/fixtures/query_entities_empty_response.xml +7 -0
  160. data/test/fixtures/query_entities_response.xml +45 -0
  161. data/test/fixtures/queue_service_properties.xml +22 -0
  162. data/test/fixtures/queue_service_properties_original.xml +19 -0
  163. data/test/fixtures/queues.xml +16 -0
  164. data/test/fixtures/retention_policy.xml +5 -0
  165. data/test/fixtures/sb_default_create_queue_response.xml +23 -0
  166. data/test/fixtures/sb_default_create_topic_response.xml +18 -0
  167. data/test/fixtures/sb_get_access_token_response.txt +1 -0
  168. data/test/fixtures/sb_queues_runtime_peek_message_response_headers.txt +9 -0
  169. data/test/fixtures/storage_service_properties.xml +23 -0
  170. data/test/fixtures/update_storage_account.xml +16 -0
  171. data/test/fixtures/update_storage_error.xml +5 -0
  172. data/test/fixtures/updated_storage_accounts.xml +53 -0
  173. data/test/fixtures/virtual_machine.xml +113 -0
  174. data/test/fixtures/windows_virtual_machine.xml +106 -0
  175. data/test/integration/affinity_group/Affinity_test.rb +55 -0
  176. data/test/integration/affinity_group/Create_Affinity_test.rb +63 -0
  177. data/test/integration/affinity_group/Delete_Affinity_test.rb +56 -0
  178. data/test/integration/affinity_group/List_Affinity_test.rb +41 -0
  179. data/test/integration/affinity_group/Update_Affinity_test.rb +82 -0
  180. data/test/integration/blob/blob_gb18030_test.rb +199 -0
  181. data/test/integration/blob/blob_metadata_test.rb +75 -0
  182. data/test/integration/blob/blob_pages_test.rb +119 -0
  183. data/test/integration/blob/blob_properties_test.rb +77 -0
  184. data/test/integration/blob/block_blob_test.rb +254 -0
  185. data/test/integration/blob/container/container_acl_test.rb +69 -0
  186. data/test/integration/blob/container/container_metadata_test.rb +50 -0
  187. data/test/integration/blob/container/create_container_test.rb +60 -0
  188. data/test/integration/blob/container/delete_container_test.rb +39 -0
  189. data/test/integration/blob/container/get_container_properties_test.rb +48 -0
  190. data/test/integration/blob/container/list_containers_test.rb +79 -0
  191. data/test/integration/blob/container/root_container_test.rb +54 -0
  192. data/test/integration/blob/copy_blob_test.rb +113 -0
  193. data/test/integration/blob/create_blob_snapshot_test.rb +80 -0
  194. data/test/integration/blob/create_page_blob_test.rb +83 -0
  195. data/test/integration/blob/delete_blob_test.rb +159 -0
  196. data/test/integration/blob/get_blob_test.rb +65 -0
  197. data/test/integration/blob/informative_errors_test.rb +39 -0
  198. data/test/integration/blob/lease/acquire_lease_test.rb +36 -0
  199. data/test/integration/blob/lease/break_lease_test.rb +40 -0
  200. data/test/integration/blob/lease/release_lease_test.rb +40 -0
  201. data/test/integration/blob/lease/renew_lease_test.rb +42 -0
  202. data/test/integration/blob/list_blobs_test.rb +113 -0
  203. data/test/integration/cloud_service/Cloud_Create_test.rb +44 -0
  204. data/test/integration/cloud_service/Cloud_Delete_test.rb +44 -0
  205. data/test/integration/database/create_sql_server_firewall_test.rb +86 -0
  206. data/test/integration/database/create_sql_server_test.rb +53 -0
  207. data/test/integration/database/delete_sql_server_firewall_test.rb +70 -0
  208. data/test/integration/database/delete_sql_server_test.rb +58 -0
  209. data/test/integration/database/list_sql_server_firewall_test.rb +45 -0
  210. data/test/integration/database/list_sql_servers_test.rb +44 -0
  211. data/test/integration/database/reset_password_sql_server_test.rb +55 -0
  212. data/test/integration/location/Location_List_test.rb +39 -0
  213. data/test/integration/queue/clear_messages_test.rb +42 -0
  214. data/test/integration/queue/create_message_test.rb +75 -0
  215. data/test/integration/queue/create_queue_test.rb +50 -0
  216. data/test/integration/queue/delete_message_test.rb +67 -0
  217. data/test/integration/queue/delete_queue_test.rb +45 -0
  218. data/test/integration/queue/informative_errors_test.rb +42 -0
  219. data/test/integration/queue/list_messages_encoded_test.rb +79 -0
  220. data/test/integration/queue/list_messages_test.rb +79 -0
  221. data/test/integration/queue/list_queues_test.rb +44 -0
  222. data/test/integration/queue/peek_messages_test.rb +59 -0
  223. data/test/integration/queue/queue_gb18030_test.rb +131 -0
  224. data/test/integration/queue/queue_metadata_test.rb +40 -0
  225. data/test/integration/queue/update_message_test.rb +74 -0
  226. data/test/integration/service_bus/informative_errors_test.rb +37 -0
  227. data/test/integration/service_bus/queues_scenario_test.rb +200 -0
  228. data/test/integration/service_bus/queues_test.rb +266 -0
  229. data/test/integration/service_bus/rules_test.rb +145 -0
  230. data/test/integration/service_bus/sb_queue_gb18030_test.rb +182 -0
  231. data/test/integration/service_bus/scenario_test.rb +101 -0
  232. data/test/integration/service_bus/subscriptions_test.rb +211 -0
  233. data/test/integration/service_bus/topics_scenario_test.rb +406 -0
  234. data/test/integration/service_bus/topics_test.rb +129 -0
  235. data/test/integration/storage_management/storage_management_test.rb +160 -0
  236. data/test/integration/table/create_table_test.rb +36 -0
  237. data/test/integration/table/delete_entity_batch_test.rb +107 -0
  238. data/test/integration/table/delete_entity_test.rb +94 -0
  239. data/test/integration/table/delete_table_test.rb +40 -0
  240. data/test/integration/table/get_table_test.rb +37 -0
  241. data/test/integration/table/informative_errors_test.rb +39 -0
  242. data/test/integration/table/insert_entity_batch_test.rb +100 -0
  243. data/test/integration/table/insert_entity_test.rb +88 -0
  244. data/test/integration/table/insert_or_merge_entity_batch_test.rb +159 -0
  245. data/test/integration/table/insert_or_merge_entity_test.rb +143 -0
  246. data/test/integration/table/insert_or_replace_entity_batch_test.rb +152 -0
  247. data/test/integration/table/insert_or_replace_entity_test.rb +137 -0
  248. data/test/integration/table/merge_entity_batch_test.rb +128 -0
  249. data/test/integration/table/merge_entity_test.rb +113 -0
  250. data/test/integration/table/query_entities_test.rb +195 -0
  251. data/test/integration/table/query_tables_test.rb +43 -0
  252. data/test/integration/table/query_test.rb +251 -0
  253. data/test/integration/table/table_acl_test.rb +52 -0
  254. data/test/integration/table/table_gb18030_test.rb +355 -0
  255. data/test/integration/table/update_entity_batch_test.rb +149 -0
  256. data/test/integration/table/update_entity_test.rb +131 -0
  257. data/test/integration/test_helper.rb +42 -0
  258. data/test/integration/vm/VM_Create_test.rb +260 -0
  259. data/test/integration/vm/VM_Delete_test.rb +55 -0
  260. data/test/integration/vm/VM_Operations_test.rb +173 -0
  261. data/test/integration/vm_image/virtual_machine_disk_test.rb +37 -0
  262. data/test/integration/vm_image/virtual_machine_image_test.rb +37 -0
  263. data/test/integration/vnet/Virtual_Network_Create_test.rb +122 -0
  264. data/test/integration/vnet/Virtual_Network_list_test.rb +53 -0
  265. data/test/support/env.rb +19 -0
  266. data/test/support/fixtures.rb +36 -0
  267. data/test/support/name_generator.rb +160 -0
  268. data/test/support/stubs.rb +42 -0
  269. data/test/support/virtual_machine_name_generator.rb +102 -0
  270. data/test/support/virtual_network_helper.rb +73 -0
  271. data/test/test_helper.rb +53 -0
  272. data/test/unit/affinity_group/affinity_group_test.rb +192 -0
  273. data/test/unit/affinity_group/serialization_test.rb +88 -0
  274. data/test/unit/base_management/location_test.rb +57 -0
  275. data/test/unit/blob/blob_service_test.rb +1947 -0
  276. data/test/unit/cloud_service_management/cloud_service_management_service_test.rb +276 -0
  277. data/test/unit/cloud_service_management/serialization_test.rb +169 -0
  278. data/test/unit/core/auth/shared_key_lite_test.rb +51 -0
  279. data/test/unit/core/auth/shared_key_test.rb +58 -0
  280. data/test/unit/core/auth/signer_test.rb +30 -0
  281. data/test/unit/core/http/http_error_test.rb +57 -0
  282. data/test/unit/core/http/http_request_test.rb +66 -0
  283. data/test/unit/core/http/http_response_test.rb +45 -0
  284. data/test/unit/core/http/retry_policy_test.rb +23 -0
  285. data/test/unit/database/serialization_test.rb +97 -0
  286. data/test/unit/database/sql_database_server_service_test.rb +288 -0
  287. data/test/unit/service/serialization_test.rb +502 -0
  288. data/test/unit/service/storage_service_test.rb +291 -0
  289. data/test/unit/storage_management/serialization_test.rb +232 -0
  290. data/test/unit/storage_management/storage_management_service_test.rb +261 -0
  291. data/test/unit/table/edmtype_test.rb +108 -0
  292. data/test/unit/virtual_machine_image_management/serialization_test.rb +35 -0
  293. data/test/unit/virtual_machine_image_management/virtual_machine_image_management_service_test.rb +65 -0
  294. data/test/unit/virtual_machine_management/serialization_test.rb +247 -0
  295. data/test/unit/virtual_machine_management/virtual_machine_management_service_test.rb +440 -0
  296. data/test/unit/vnet/serialization_test.rb +187 -0
  297. data/test/unit/vnet/virtual_network_management_service_test.rb +131 -0
  298. metadata +470 -0
@@ -0,0 +1,42 @@
1
+ #-------------------------------------------------------------------------
2
+ # Copyright 2013 Microsoft Open Technologies, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #--------------------------------------------------------------------------
15
+
16
+ module Azure
17
+ module VirtualMachineManagement
18
+ class VirtualMachine
19
+ def initialize
20
+ yield self if block_given?
21
+ end
22
+
23
+ attr_accessor :cloud_service_name
24
+ attr_accessor :status
25
+ attr_accessor :ipaddress
26
+ attr_accessor :vm_name
27
+ attr_accessor :udp_endpoints
28
+ attr_accessor :hostname
29
+ attr_accessor :deployment_name
30
+ attr_accessor :deployment_status
31
+ attr_accessor :tcp_endpoints
32
+ attr_accessor :role_size
33
+ attr_accessor :image
34
+ attr_accessor :os_type
35
+ attr_accessor :disk_name
36
+ attr_accessor :virtual_network_name
37
+ attr_accessor :availability_set_name
38
+ attr_accessor :media_link
39
+ attr_accessor :data_disks
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,549 @@
1
+ #-------------------------------------------------------------------------
2
+ # Copyright 2013 Microsoft Open Technologies, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #--------------------------------------------------------------------------
15
+ require 'azure/virtual_machine_management/serialization'
16
+ include Azure::VirtualMachineImageManagement
17
+
18
+ module Azure
19
+ module VirtualMachineManagement
20
+ class VirtualMachineManagementService < BaseManagementService
21
+ def initialize
22
+ super()
23
+ end
24
+
25
+ # Public: Get a lists of virtual machines available under the current subscription.
26
+ #
27
+ # Returns an list of Azure::VirtualMachineManagement::VirtualMachine instances.
28
+ def list_virtual_machines(*cloud_service_names)
29
+ roles = []
30
+ cloud_service_names.flatten!
31
+ if cloud_service_names.empty?
32
+ cloud_service = Azure::CloudServiceManagementService.new
33
+ cloud_service_names = cloud_service.list_cloud_services.map(&:name)
34
+ end
35
+ cloud_service_names.each do |cloud_service_name|
36
+ request_path = "/services/hostedservices/#{cloud_service_name}/deploymentslots/production"
37
+ request = ManagementHttpRequest.new(:get, request_path)
38
+ request.warn = true
39
+ response = request.call
40
+ roles << Serialization.virtual_machines_from_xml(response, cloud_service_name)
41
+ end
42
+ roles.flatten.compact
43
+ end
44
+
45
+ # Public: Gets a virtual machine based on the provided name and cloud service name.
46
+ #
47
+ # ==== Attributes
48
+ #
49
+ # * +name+ - String. Virtual machine name.
50
+ # * +cloud_service_name+ - String. Cloud service name.
51
+ #
52
+ # Returns an Azure::VirtualMachineManagement::VirtualMachine instance.
53
+ def get_virtual_machine(name, cloud_service_name)
54
+ server = list_virtual_machines(cloud_service_name).select { |x| x.vm_name == name.downcase }
55
+ server.first
56
+ end
57
+
58
+ # Public: Provisions a virtual machine based on the supplied configuration.
59
+ #
60
+ # ==== Attributes
61
+ #
62
+ # * +params+ - Hash. parameters.
63
+ # * +options+ - Hash. Optional parameters.
64
+ # * +add_role+ - true/false. Optional Parameter. Default is false
65
+ #
66
+ # ==== Params
67
+ #
68
+ # Accepted key/value pairs are:
69
+ # * +:vm_name+ - String. Name of virtual machine.
70
+ # * +:vm_user+ - String. User name for the virtual machine instance.
71
+ # * +:password+ - String. A description for the hosted service.
72
+ # * +:image+ - String. Name of the disk image to use to create the virtual machine.
73
+ # * +:location+ - String. The location where the virtual machine will be created.
74
+ # * +:affinity_group_name - String. The affinity group name to be used
75
+ # for the cloud service and the storage account if these do not exist.
76
+ #
77
+ # ==== Options
78
+ #
79
+ # Accepted key/value pairs are:
80
+ # * +:storage_account_name+ - String. Name of storage account.
81
+ # * +:cloud_service_name+ - String. Name of cloud service.
82
+ # * +:deployment_name+ - String. A name for the deployment.
83
+ # * +:tcp_endpoints+ - String. Specifies the internal port and external/public port separated by a colon.
84
+ # You can map multiple internal and external ports by separating them with a comma.
85
+ # * +:ssh_private_key_file+ - String. Path of private key file.
86
+ # * +:ssh_certificate_file+ - String. Path of certificate file.
87
+ # * +:ssh_port+ - Integer. Specifies the SSH port number.
88
+ # * +:winrm_http_port - Integer. Specifies the WinRM HTTP port number.
89
+ # * +:winrm_https_port - Integer. Specifies the WinRM HTTPS port number.
90
+ # * +:vm_size+ - String. Specifies the size of the virtual machine instance.
91
+ # * +:winrm_transport+ - Array. Specifies WINRM transport protocol.
92
+ # * +:availability_set_name+ - String. Specifies the availability set name.
93
+ #
94
+ # ==== add_role
95
+ #
96
+ # Accepted values are:
97
+ # * +false+ - Will add a new deployment in a cloud service.
98
+ # * +true+ - Will add a new role to a cloud service. Atleast one
99
+ # deployment should exist before you can add a role.
100
+ #
101
+ # Returns Azure::VirtualMachineManagement::VirtualMachine objects of newly created instance.
102
+ #
103
+ # See:
104
+ # http://msdn.microsoft.com/en-us/library/windowsazure/jj157194.aspx
105
+ # http://msdn.microsoft.com/en-us/library/windowsazure/jj157186.aspx
106
+ def create_virtual_machine(params, options = {})
107
+ options[:os_type] = get_os_type(params[:image])
108
+ validate_deployment_params(params, options)
109
+ options[:deployment_name] ||= options[:cloud_service_name]
110
+ Loggerx.info 'Creating deploymnent...'
111
+ options[:cloud_service_name] ||= generate_cloud_service_name(params[:vm_name])
112
+ options[:storage_account_name] ||= generate_storage_account_name(params[:vm_name])
113
+ optionals = {}
114
+ if options[:virtual_network_name]
115
+ virtual_network_service = Azure::VirtualNetworkManagementService.new
116
+ virtual_networks = virtual_network_service.list_virtual_networks.select { |x| x.name == options[:virtual_network_name] }
117
+ if virtual_networks.empty?
118
+ Loggerx.error_with_exit "Virtual network #{options[:virtual_network_name]} doesn't exists"
119
+ else
120
+ optionals[:affinity_group_name] = virtual_networks.first.affinity_group
121
+ end
122
+ elsif options[:affinity_group_name]
123
+ optionals[:affinity_group_name] = options[:affinity_group_name]
124
+ else
125
+ optionals[:location] = params[:location]
126
+ end
127
+ cloud_service = Azure::CloudServiceManagementService.new
128
+ cloud_service.create_cloud_service(options[:cloud_service_name], optionals)
129
+ cloud_service.upload_certificate(options[:cloud_service_name], params[:certificate]) unless params[:certificate].empty?
130
+ Azure::StorageManagementService.new.create_storage_account(options[:storage_account_name], optionals)
131
+ body = Serialization.deployment_to_xml(params, options)
132
+ path = "/services/hostedservices/#{options[:cloud_service_name]}/deployments"
133
+ Loggerx.info 'Deployment in progress...'
134
+ request = ManagementHttpRequest.new(:post, path, body)
135
+ request.call
136
+ get_virtual_machine(params[:vm_name], options[:cloud_service_name])
137
+ rescue Exception => e
138
+ e.message
139
+ end
140
+
141
+ # Public: Add a new role to a cloud service. Atleast one deployment should exist before you can add a role.
142
+ #
143
+ # ==== Attributes
144
+ #
145
+ # * +params+ - Hash. parameters.
146
+ # * +options+ - Hash. Optional parameters.
147
+ #
148
+ # ==== Params
149
+ #
150
+ # Accepted key/value pairs are:
151
+ # * +:vm_name+ - String. Name of virtual machine.
152
+ # * +:vm_user+ - String. User name for the virtual machine instance.
153
+ # * +:password+ - String. A description for the hosted service.
154
+ # * +:image+ - String. Name of the disk image to use to create the virtual machine.
155
+ # * +:cloud_service_name+ - String. Name of cloud service.
156
+ #
157
+ # ==== Options
158
+ #
159
+ # Accepted key/value pairs are:
160
+ # * +:storage_account_name+ - String. Name of storage account.
161
+ # * +:tcp_endpoints+ - String. Specifies the internal port and external/public port separated by a colon.
162
+ # You can map multiple internal and external ports by separating them with a comma.
163
+ # * +:ssh_private_key_file+ - String. Path of private key file.
164
+ # * +:ssh_certificate_file+ - String. Path of certificate file.
165
+ # * +:ssh_port+ - Integer. Specifies the SSH port number.
166
+ # * +:winrm_http_port - Integer. Specifies the WinRM HTTP port number.
167
+ # * +:winrm_https_port - Integer. Specifies the WinRM HTTPS port number.
168
+ # * +:vm_size+ - String. Specifies the size of the virtual machine instance.
169
+ # * +:winrm_transport+ - Array. Specifies WINRM transport protocol.
170
+ #
171
+ # Returns Azure::VirtualMachineManagement::VirtualMachine objects of newly created instance.
172
+ #
173
+ # See:
174
+ # http://msdn.microsoft.com/en-us/library/windowsazure/jj157186.aspx
175
+ def add_role(params, options = {})
176
+ options[:os_type] = get_os_type(params[:image])
177
+ validate_deployment_params(params, options, true)
178
+ cloud_service = Azure::CloudServiceManagementService.new
179
+ cloud_service = cloud_service.get_cloud_service_properties(params[:cloud_service_name])
180
+ deployment_name = cloud_service.deployment_name
181
+ Loggerx.error_with_exit "Deployment doesn't exists." if cloud_service && deployment_name.empty?
182
+ others = {}
183
+ if cloud_service.location
184
+ others[:location] = cloud_service.location
185
+ elsif cloud_service.affinity_group
186
+ others[:affinity_group_name] = cloud_service.affinity_group
187
+ end
188
+ options[:storage_account_name] ||= generate_storage_account_name(params[:vm_name])
189
+ Azure::StorageManagementService.new.create_storage_account(options[:storage_account_name], others)
190
+ Loggerx.info 'Deployment exists, adding role...'
191
+ existing_ports = []
192
+ cloud_service.virtual_machines[deployment_name.to_sym].each do |vm|
193
+ vm.tcp_endpoints.each do |endpoint|
194
+ existing_ports << endpoint[:public_port]
195
+ end
196
+ end
197
+ options[:existing_ports] = existing_ports
198
+ body = Serialization.role_to_xml(params, options).to_xml
199
+ path = "/services/hostedservices/#{cloud_service.name}/deployments/#{deployment_name}/roles"
200
+ Loggerx.info 'Deployment in progress...'
201
+ request = ManagementHttpRequest.new(:post, path, body)
202
+ request.call
203
+ get_virtual_machine(params[:vm_name], cloud_service.name)
204
+ end
205
+
206
+ # Public: Deletes the deployment, cloud service and disk.
207
+ #
208
+ # ==== Attributes
209
+ #
210
+ # * +vm_name+ - String. Virtual machine name.
211
+ # * +cloud_service_name+ - String. Cloud service name.
212
+ #
213
+ # See http://msdn.microsoft.com/en-us/library/windowsazure/gg441305.aspx
214
+ # See http://msdn.microsoft.com/en-us/library/windowsazure/jj157179.aspx
215
+ #
216
+ # Returns NONE
217
+ def delete_virtual_machine(vm_name, cloud_service_name)
218
+ virtual_machines = list_virtual_machines(cloud_service_name)
219
+ vm = virtual_machines.select { |x| x.vm_name == vm_name }.first
220
+ if vm
221
+ if virtual_machines.size == 1
222
+ cloud_service = Azure::CloudServiceManagementService.new
223
+ cloud_service.delete_cloud_service_deployment(cloud_service_name)
224
+ cloud_service.delete_cloud_service(cloud_service_name)
225
+ else
226
+ path = "/services/hostedservices/#{vm.cloud_service_name}/deployments/#{vm.deployment_name}/roles/#{vm.vm_name}"
227
+ Loggerx.info "Deleting virtual machine #{vm_name}. \n"
228
+ request = ManagementHttpRequest.new(:delete, path)
229
+ request.call
230
+ end
231
+ Loggerx.info "Waiting for disk to be released.\n"
232
+ disk_name = vm.disk_name
233
+ disk_management_service = VirtualMachineDiskManagementService.new
234
+ # Wait for 180s for disk to be released.
235
+ disk = nil
236
+ 18.times do
237
+ print '# '
238
+ disk = disk_management_service.get_virtual_machine_disk(disk_name)
239
+ unless disk.attached
240
+ print "Disk released.\n"
241
+ break
242
+ end
243
+ sleep 10
244
+ end
245
+ if disk.attached
246
+ Loggerx.error "\nCannot delete disk #{disk_name}."
247
+ else
248
+ disk_management_service.delete_virtual_machine_disk(disk_name)
249
+ end
250
+ else
251
+ Loggerx.error "Cannot find virtual machine #{vm_name} under cloud service #{cloud_service_name}"
252
+ end
253
+ rescue
254
+ end
255
+
256
+ # Public: Shuts down the specified virtual machine.
257
+ #
258
+ # ==== Attributes
259
+ #
260
+ # * +name+ - String. Virtual machine name.
261
+ # * +cloud_service_name+ - String. Cloud service name.
262
+ #
263
+ # See http://msdn.microsoft.com/en-us/library/windowsazure/jj157195.aspx
264
+ #
265
+ # Returns NONE
266
+ def shutdown_virtual_machine(vm_name, cloud_service_name)
267
+ vm = get_virtual_machine(vm_name, cloud_service_name)
268
+ if vm
269
+ if %w(StoppedVM StoppedDeallocated).include?(vm.status)
270
+ Loggerx.error 'Cannot perform the shutdown operation on a stopped virtual machine.'
271
+ elsif vm.deployment_status == 'Running'
272
+ path = "/services/hostedservices/#{vm.cloud_service_name}/deployments/#{vm.deployment_name}/roleinstances/#{vm.vm_name}/Operations"
273
+ body = Serialization.shutdown_virtual_machine_to_xml
274
+ Loggerx.info "Shutting down virtual machine \"#{vm.vm_name}\" ..."
275
+ request = ManagementHttpRequest.new(:post, path, body)
276
+ request.call
277
+ else
278
+ Loggerx.error 'Cannot perform the shutdown operation on a stopped deployment.'
279
+ end
280
+ else
281
+ Loggerx.error "Cannot find virtual machine \"#{vm_name}\" under cloud service \"#{cloud_service_name}\". "
282
+ end
283
+ end
284
+
285
+ # Public: Starts the specified virtual machine.
286
+ #
287
+ # ==== Attributes
288
+ #
289
+ # * +name+ - String. Virtual machine name.
290
+ # * +cloud_service_name+ - String. Cloud service name.
291
+ #
292
+ # See http://msdn.microsoft.com/en-us/library/windowsazure/jj157189.aspx
293
+ #
294
+ # Returns NONE
295
+ def start_virtual_machine(vm_name, cloud_service_name)
296
+ vm = get_virtual_machine(vm_name, cloud_service_name)
297
+ if vm
298
+ if vm.status == 'ReadyRole'
299
+ Loggerx.error 'Cannot perform the start operation on started virtual machine.'
300
+ else
301
+ path = "/services/hostedservices/#{vm.cloud_service_name}/deployments/#{vm.deployment_name}/roleinstances/#{vm.vm_name}/Operations"
302
+ body = Serialization.start_virtual_machine_to_xml
303
+ Loggerx.info "Starting virtual machine \"#{vm.vm_name}\" ..."
304
+ request = ManagementHttpRequest.new(:post, path, body)
305
+ request.call
306
+ end
307
+ else
308
+ Loggerx.error "Cannot find virtual machine \"#{vm_name}\" under cloud service \"#{cloud_service_name}\"."
309
+ end
310
+ end
311
+
312
+ # Public: Restarts the specified virtual machine.
313
+ #
314
+ # ==== Attributes
315
+ #
316
+ # * +name+ - String. Virtual machine name.
317
+ # * +cloud_service_name+ - String. Cloud service name.
318
+ #
319
+ # See http://msdn.microsoft.com/en-us/library/windowsazure/jj157197.aspx
320
+ #
321
+ # Returns NONE
322
+ def restart_virtual_machine(vm_name, cloud_service_name)
323
+ vm = get_virtual_machine(vm_name, cloud_service_name)
324
+ if vm
325
+ path = "/services/hostedservices/#{vm.cloud_service_name}/deployments/#{vm.deployment_name}/roleinstances/#{vm.vm_name}/Operations"
326
+ body = Serialization.restart_virtual_machine_to_xml
327
+ Loggerx.info "Restarting virtual machine \"#{vm.vm_name}\" ..."
328
+ request = ManagementHttpRequest.new(:post, path, body)
329
+ request.call
330
+ else
331
+ Loggerx.error "Cannot find virtual machine \"#{vm_name}\" under cloud service \"#{cloud_service_name}\"."
332
+ end
333
+ end
334
+
335
+ # Public: Add/Update endpoints of virtual machine.
336
+ #
337
+ # ==== Attributes
338
+ #
339
+ # * +name+ - String. Virtual machine name.
340
+ # * +cloud_service_name+ - String. Cloud service name.
341
+ # * +input_endpoints+ - Hash. A hash of the name/value pairs for the endpoint.
342
+ #
343
+ # ==== Endpoint
344
+ #
345
+ # Accepted key/value pairs are:
346
+ # * +:local_port+ - String. Specifies the internal port on which the
347
+ # Virtual Machine is listening.
348
+ # * +:public_port+ - String. Specifies the external port to use for
349
+ # the endpoint.
350
+ # * +:name+ - String. Specifies the name of the external endpoint.
351
+ # * +load_balancer_name+ - String. Specifies a name for a set of
352
+ # load-balanced endpoints.
353
+ # * +:protocol+ - String. Specifies the transport protocol
354
+ # for the endpoint. Possible values are: TCP, UDP
355
+ # * +:direct_server_return+ - String. Specifies whether the endpoint
356
+ # uses Direct Server Return. Possible values are: true, false (optional)
357
+ # * +:load_balancer - Hash. Contains properties that define the
358
+ # endpoint settings that the load balancer uses to monitor the
359
+ # availability of the Virtual Machine (optional)
360
+ #
361
+ # === Load balancer
362
+ #
363
+ # Accepted key/value pairs are:
364
+ # * +:port+ - String. Specifies the internal port on which the
365
+ # Virtual Machine is listening.
366
+ # * +:protocol+ - String. Specifies the protocol to use to inspect the
367
+ # availability status of the virtual machine.
368
+ # * +:interval+ - String. Specifies the interval for the load balancer
369
+ # probe in seconds. (optional)
370
+ # * +:timeout+ - String. Specifies the timeout for the load balancer
371
+ # probe in seconds. (optional)
372
+ # * +:path+ - String. Specifies the relative path to inspect to
373
+ # determine the availability status of the Virtual Machine. (optional)
374
+ #
375
+ # See http://msdn.microsoft.com/en-us/library/windowsazure/jj157187.aspx
376
+ #
377
+ # Returns NONE
378
+ def update_endpoints(vm_name, cloud_service_name, *input_endpoints)
379
+ input_endpoints.flatten!
380
+ vm = get_virtual_machine(vm_name, cloud_service_name)
381
+ if vm
382
+ path = "/services/hostedservices/#{vm.cloud_service_name}/deployments/#{vm.deployment_name}/roles/#{vm_name}"
383
+ endpoints = vm.tcp_endpoints + vm.udp_endpoints
384
+ input_endpoints.each do |iep|
385
+ endpoints.delete_if { |ep| iep[:name].downcase == ep[:name].downcase }
386
+ end
387
+ endpoints += input_endpoints
388
+ body = Serialization.update_role_to_xml(endpoints, vm)
389
+ request = ManagementHttpRequest.new(:put, path, body)
390
+ Loggerx.info "Updating endpoints of virtual machine #{vm.vm_name} ..."
391
+ request.call
392
+ else
393
+ Loggerx.error "Cannot find virtual machine \"#{vm_name}\" under cloud service \"#{cloud_service_name}\"."
394
+ end
395
+ end
396
+
397
+ # Public: Delete endpoint of virtual machine.
398
+ #
399
+ # ==== Attributes
400
+ #
401
+ # * +name+ - String. Virtual machine name.
402
+ # * +cloud_service_name+ - String. Cloud service name.
403
+ # * +endpoint_name+ - String. Name of endpoint.
404
+ #
405
+ # See http://msdn.microsoft.com/en-us/library/windowsazure/jj157187.aspx
406
+ #
407
+ # Returns NONE
408
+ def delete_endpoint(vm_name, cloud_service_name, endpoint_name)
409
+ vm = get_virtual_machine(vm_name, cloud_service_name)
410
+ if vm
411
+ path = "/services/hostedservices/#{vm.cloud_service_name}/deployments/#{vm.deployment_name}/roles/#{vm_name}"
412
+ endpoints = vm.tcp_endpoints + vm.udp_endpoints
413
+ endpoints.delete_if { |ep| endpoint_name.downcase == ep[:name].downcase }
414
+ body = Serialization.update_role_to_xml(endpoints, vm)
415
+ request = ManagementHttpRequest.new(:put, path, body)
416
+ Loggerx.info "Deleting virtual machine endpoint #{endpoint_name} ..."
417
+ request.call
418
+ else
419
+ Loggerx.error "Cannot find virtual machine \"#{vm_name}\" under cloud service \"#{cloud_service_name}\"."
420
+ end
421
+ end
422
+
423
+ # Public: adds a data disk to a virtual machine.
424
+ #
425
+ # ==== Attributes
426
+ #
427
+ # * +cloud_service_name+ - String. Cloud service name.
428
+ # * +vm_name+ - String. Virtual machine name.
429
+ # * +options+ - Hash. Optional parameters.
430
+ #
431
+ # ==== Options
432
+ #
433
+ # Accepted key/value pairs in options parameter are:
434
+ # * +:import+ - Boolean. if true, then allows to use an existing
435
+ # disk by disk name. if false, then create and attach new data disk.
436
+ # * +:disk_name+ - String. Specifies the name of the disk.
437
+ # Reqruied if using existing disk.
438
+ # * +:host_caching+ - String. Specifies the caching behavior of data disk
439
+ # The default is ReadOnly. Possible values are: None, ReadOnly, ReadWrite
440
+ # * +:disk_label+ - String. Specifies the description of the data disk.
441
+ # * +:disk_size+ - String. Specifies the size of disk in GB
442
+ #
443
+ # See http://msdn.microsoft.com/en-us/library/windowsazure/jj157199.aspx
444
+ #
445
+ # Returns None
446
+ def add_data_disk(vm_name, cloud_service_name, options = {})
447
+ options[:import] ||= false
448
+ vm = get_virtual_machine(vm_name, cloud_service_name)
449
+ if vm
450
+ path = "/services/hostedservices/#{cloud_service_name}/deployments/#{vm.deployment_name}/roles/#{vm_name}/DataDisks"
451
+ body = Serialization.add_data_disk_to_xml(vm, options)
452
+ Loggerx.info "Adding data disk to virtual machine #{vm_name} ..."
453
+ request = ManagementHttpRequest.new(:post, path, body)
454
+ request.call
455
+ else
456
+ Loggerx.error "Cannot find virtual machine \"#{vm_name}\" under cloud service \"#{cloud_service_name}\"."
457
+ end
458
+ end
459
+
460
+ private
461
+
462
+ # Private: Gets the operating system type of an image.
463
+ #
464
+ # Returns Linux or Windows
465
+ def get_os_type(image_name)
466
+ image_service = Azure::VirtualMachineImageManagementService.new
467
+ image = image_service.list_virtual_machine_images.select { |x| x.name == image_name }.first
468
+ Loggerx.error_with_exit 'The virtual machine image source is not valid.' unless image
469
+ image.os_type
470
+ end
471
+
472
+ def generate_cloud_service_name(vm_name)
473
+ random_string(vm_name + '-service-')
474
+ end
475
+
476
+ def generate_storage_account_name(vm_name)
477
+ random_string(vm_name + 'storage').gsub(/[^0-9a-z ]/i, '').downcase[0..23]
478
+ end
479
+
480
+ def validate_deployment_params(params, options, add_role = false)
481
+ errors = []
482
+ params_keys = %w(vm_name image vm_user)
483
+ params_keys += ['password'] if options[:os_type] == 'Windows'
484
+ options_keys = []
485
+ options_keys = %w(private_key_file certificate_file) if certificate_required?(params, options)
486
+ if add_role
487
+ params_keys += ['cloud_service_name']
488
+ else
489
+ params_keys += ['location']
490
+ end
491
+ params_keys.each do |key|
492
+ errors << key if params[key.to_sym].nil?
493
+ end
494
+
495
+ options_keys.each do |key|
496
+ errors << key if options[key.to_sym].nil?
497
+ end
498
+
499
+ if errors.empty?
500
+ validate_location(params[:location]) unless add_role
501
+ validate_role_size(options[:vm_size])
502
+ params[:certificate] = {}
503
+ if certificate_required?(params, options)
504
+ begin
505
+ params[:certificate][:key] = OpenSSL::PKey.read File.read(options[:private_key_file])
506
+ params[:certificate][:cert] = OpenSSL::X509::Certificate.new File.read(options[:certificate_file])
507
+ params[:certificate][:fingerprint] = export_fingerprint(params[:certificate][:cert])
508
+ rescue Exception => e
509
+ Loggerx.error_with_exit e.message
510
+ end
511
+ end
512
+ else
513
+ Loggerx.error_with_exit "You did not provide a valid '#{errors.uniq.join(", ")}' value."
514
+ end
515
+ end
516
+
517
+ def certificate_required?(params, options)
518
+ if options[:os_type] == 'Linux'
519
+ (params[:password].nil? or (!options[:certificate_file].nil? && !options[:private_key_file].nil?))
520
+ else
521
+ winrm_with_https(options)
522
+ end
523
+ end
524
+
525
+ def winrm_with_https(options)
526
+ if options[:os_type] == 'Windows'
527
+ options[:winrm_transport] && options[:winrm_transport].include?('https') && options[:certificate_file] && options[:private_key_file]
528
+ end
529
+ end
530
+
531
+ def validate_role_size(vm_size)
532
+ valid_role_sizes = %w(ExtraSmall Small Medium Large ExtraLarge A5 A6 A7 Basic_A0 Basic_A1 Basic_A2 Basic_A3 Basic_A4)
533
+ if vm_size && !valid_role_sizes.include?(vm_size)
534
+ Loggerx.error_with_exit "Value '#{vm_size}' specified for parameter 'vm_size' is invalid. Allowed values are 'ExtraSmall,Small,Medium,Large,ExtraLarge,A6,A7'"
535
+ end
536
+ end
537
+
538
+ def validate_location(location_name)
539
+ locations = Azure::BaseManagementService.new.list_locations
540
+ location = locations.select { |loc| loc.name.downcase == location_name.downcase }.first
541
+ if location.nil?
542
+ Loggerx.error_with_exit "Value '#{location_name}' specified for parameter 'location' is invalid. Allowed values are #{locations.map(&:name).join(',')}"
543
+ elsif !location.available_services.include?('PersistentVMRole')
544
+ Loggerx.error_with_exit "Persistentvmrole not enabled for \"#{location.name}\". Try different location"
545
+ end
546
+ end
547
+ end
548
+ end
549
+ end