1# Licensed to the Apache Software Foundation (ASF) under one or more 2# contributor license agreements. See the NOTICE file distributed with 3# this work for additional information regarding copyright ownership. 4# The ASF licenses this file to You under the Apache License, Version 2.0 5# (the "License"); you may not use this file except in compliance with 6# the License. You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16""" 17Driver for Microsoft Azure Virtual Machines service. 18 19http://azure.microsoft.com/en-us/services/virtual-machines/ 20""" 21 22import re 23import time 24import collections 25import random 26import sys 27import copy 28import base64 29 30from datetime import datetime 31from xml.dom import minidom 32from xml.sax.saxutils import escape as xml_escape 33 34from libcloud.utils.py3 import ET 35from libcloud.common.azure import AzureServiceManagementConnection 36from libcloud.common.azure import AzureRedirectException 37from libcloud.compute.providers import Provider 38from libcloud.compute.base import Node, NodeDriver, NodeLocation, NodeSize 39from libcloud.compute.base import NodeImage, StorageVolume 40from libcloud.compute.types import NodeState 41from libcloud.common.types import LibcloudError 42from libcloud.utils.py3 import _real_unicode 43from libcloud.utils.py3 import httplib 44from libcloud.utils.py3 import urlparse 45from libcloud.utils.py3 import ensure_string 46from libcloud.utils.py3 import urlquote as url_quote 47from libcloud.utils.misc import ReprMixin 48 49HTTPSConnection = httplib.HTTPSConnection 50 51if sys.version_info < (3,): 52 _unicode_type = unicode # NOQA pylint: disable=undefined-variable 53 54 def _str(value): 55 if isinstance(value, unicode): # NOQA pylint: disable=undefined-variable 56 return value.encode('utf-8') 57 58 return str(value) 59else: 60 _str = str 61 _unicode_type = str 62 63 64AZURE_SERVICE_MANAGEMENT_HOST = 'management.core.windows.net' 65X_MS_VERSION = '2013-08-01' 66 67WINDOWS_SERVER_REGEX = re.compile( 68 r'Win|SQL|SharePoint|Visual|Dynamics|DynGP|BizTalk' 69) 70 71""" 72Sizes must be hardcoded because Microsoft doesn't provide an API to fetch them 73From http://msdn.microsoft.com/en-us/library/windowsazure/dn197896.aspx 74 75Prices are for Linux instances in East US data center. To see what pricing will 76actually be, visit: 77http://azure.microsoft.com/en-gb/pricing/details/virtual-machines/ 78""" 79AZURE_COMPUTE_INSTANCE_TYPES = { 80 'A0': { 81 'id': 'ExtraSmall', 82 'name': 'Extra Small Instance', 83 'ram': 768, 84 'disk': 127, 85 'bandwidth': None, 86 'price': '0.0211', 87 'max_data_disks': 1, 88 'cores': 'Shared' 89 }, 90 'A1': { 91 'id': 'Small', 92 'name': 'Small Instance', 93 'ram': 1792, 94 'disk': 127, 95 'bandwidth': None, 96 'price': '0.0633', 97 'max_data_disks': 2, 98 'cores': 1 99 }, 100 'A2': { 101 'id': 'Medium', 102 'name': 'Medium Instance', 103 'ram': 3584, 104 'disk': 127, 105 'bandwidth': None, 106 'price': '0.1266', 107 'max_data_disks': 4, 108 'cores': 2 109 }, 110 'A3': { 111 'id': 'Large', 112 'name': 'Large Instance', 113 'ram': 7168, 114 'disk': 127, 115 'bandwidth': None, 116 'price': '0.2531', 117 'max_data_disks': 8, 118 'cores': 4 119 }, 120 'A4': { 121 'id': 'ExtraLarge', 122 'name': 'Extra Large Instance', 123 'ram': 14336, 124 'disk': 127, 125 'bandwidth': None, 126 'price': '0.5062', 127 'max_data_disks': 16, 128 'cores': 8 129 }, 130 'A5': { 131 'id': 'A5', 132 'name': 'Memory Intensive Instance', 133 'ram': 14336, 134 'disk': 127, 135 'bandwidth': None, 136 'price': '0.2637', 137 'max_data_disks': 4, 138 'cores': 2 139 }, 140 'A6': { 141 'id': 'A6', 142 'name': 'A6 Instance', 143 'ram': 28672, 144 'disk': 127, 145 'bandwidth': None, 146 'price': '0.5273', 147 'max_data_disks': 8, 148 'cores': 4 149 }, 150 'A7': { 151 'id': 'A7', 152 'name': 'A7 Instance', 153 'ram': 57344, 154 'disk': 127, 155 'bandwidth': None, 156 'price': '1.0545', 157 'max_data_disks': 16, 158 'cores': 8 159 }, 160 'A8': { 161 'id': 'A8', 162 'name': 'A8 Instance', 163 'ram': 57344, 164 'disk': 127, 165 'bandwidth': None, 166 'price': '2.0774', 167 'max_data_disks': 16, 168 'cores': 8 169 }, 170 'A9': { 171 'id': 'A9', 172 'name': 'A9 Instance', 173 'ram': 114688, 174 'disk': 127, 175 'bandwidth': None, 176 'price': '4.7137', 177 'max_data_disks': 16, 178 'cores': 16 179 }, 180 'A10': { 181 'id': 'A10', 182 'name': 'A10 Instance', 183 'ram': 57344, 184 'disk': 127, 185 'bandwidth': None, 186 'price': '1.2233', 187 'max_data_disks': 16, 188 'cores': 8 189 }, 190 'A11': { 191 'id': 'A11', 192 'name': 'A11 Instance', 193 'ram': 114688, 194 'disk': 127, 195 'bandwidth': None, 196 'price': '2.1934', 197 'max_data_disks': 16, 198 'cores': 16 199 }, 200 'D1': { 201 'id': 'Standard_D1', 202 'name': 'D1 Faster Compute Instance', 203 'ram': 3584, 204 'disk': 127, 205 'bandwidth': None, 206 'price': '0.0992', 207 'max_data_disks': 2, 208 'cores': 1 209 }, 210 'D2': { 211 'id': 'Standard_D2', 212 'name': 'D2 Faster Compute Instance', 213 'ram': 7168, 214 'disk': 127, 215 'bandwidth': None, 216 'price': '0.1983', 217 'max_data_disks': 4, 218 'cores': 2 219 }, 220 'D3': { 221 'id': 'Standard_D3', 222 'name': 'D3 Faster Compute Instance', 223 'ram': 14336, 224 'disk': 127, 225 'bandwidth': None, 226 'price': '0.3965', 227 'max_data_disks': 8, 228 'cores': 4 229 }, 230 'D4': { 231 'id': 'Standard_D4', 232 'name': 'D4 Faster Compute Instance', 233 'ram': 28672, 234 'disk': 127, 235 'bandwidth': None, 236 'price': '0.793', 237 'max_data_disks': 16, 238 'cores': 8 239 }, 240 'D11': { 241 'id': 'Standard_D11', 242 'name': 'D11 Faster Compute Instance', 243 'ram': 14336, 244 'disk': 127, 245 'bandwidth': None, 246 'price': '0.251', 247 'max_data_disks': 4, 248 'cores': 2 249 }, 250 'D12': { 251 'id': 'Standard_D12', 252 'name': 'D12 Faster Compute Instance', 253 'ram': 28672, 254 'disk': 127, 255 'bandwidth': None, 256 'price': '0.502', 257 'max_data_disks': 8, 258 'cores': 4 259 }, 260 'D13': { 261 'id': 'Standard_D13', 262 'name': 'D13 Faster Compute Instance', 263 'ram': 57344, 264 'disk': 127, 265 'bandwidth': None, 266 'price': '0.9038', 267 'max_data_disks': 16, 268 'cores': 8 269 }, 270 'D14': { 271 'id': 'Standard_D14', 272 'name': 'D14 Faster Compute Instance', 273 'ram': 114688, 274 'disk': 127, 275 'bandwidth': None, 276 'price': '1.6261', 277 'max_data_disks': 32, 278 'cores': 16 279 } 280} 281 282_KNOWN_SERIALIZATION_XFORMS = { 283 'include_apis': 'IncludeAPIs', 284 'message_id': 'MessageId', 285 'content_md5': 'Content-MD5', 286 'last_modified': 'Last-Modified', 287 'cache_control': 'Cache-Control', 288 'account_admin_live_email_id': 'AccountAdminLiveEmailId', 289 'service_admin_live_email_id': 'ServiceAdminLiveEmailId', 290 'subscription_id': 'SubscriptionID', 291 'fqdn': 'FQDN', 292 'private_id': 'PrivateID', 293 'os_virtual_hard_disk': 'OSVirtualHardDisk', 294 'logical_disk_size_in_gb': 'LogicalDiskSizeInGB', 295 'logical_size_in_gb': 'LogicalSizeInGB', 296 'os': 'OS', 297 'persistent_vm_downtime_info': 'PersistentVMDowntimeInfo', 298 'copy_id': 'CopyId', 299 'os_disk_configuration': 'OSDiskConfiguration', 300 'is_dns_programmed': 'IsDnsProgrammed' 301} 302 303 304class AzureNodeDriver(NodeDriver): 305 connectionCls = AzureServiceManagementConnection 306 name = 'Azure Virtual machines' 307 website = 'http://azure.microsoft.com/en-us/services/virtual-machines/' 308 type = Provider.AZURE 309 310 _instance_types = AZURE_COMPUTE_INSTANCE_TYPES 311 _blob_url = ".blob.core.windows.net" 312 features = {'create_node': ['password']} 313 service_location = collections.namedtuple( 314 'service_location', 315 ['is_affinity_group', 'service_location'] 316 ) 317 318 NODE_STATE_MAP = { 319 'RoleStateUnknown': NodeState.UNKNOWN, 320 'CreatingVM': NodeState.PENDING, 321 'StartingVM': NodeState.PENDING, 322 'Provisioning': NodeState.PENDING, 323 'CreatingRole': NodeState.PENDING, 324 'StartingRole': NodeState.PENDING, 325 'ReadyRole': NodeState.RUNNING, 326 'BusyRole': NodeState.PENDING, 327 'StoppingRole': NodeState.PENDING, 328 'StoppingVM': NodeState.PENDING, 329 'DeletingVM': NodeState.PENDING, 330 'StoppedVM': NodeState.STOPPED, 331 'RestartingRole': NodeState.REBOOTING, 332 'CyclingRole': NodeState.TERMINATED, 333 'FailedStartingRole': NodeState.TERMINATED, 334 'FailedStartingVM': NodeState.TERMINATED, 335 'UnresponsiveRole': NodeState.TERMINATED, 336 'StoppedDeallocated': NodeState.TERMINATED, 337 } 338 339 def __init__(self, subscription_id=None, key_file=None, **kwargs): 340 """ 341 subscription_id contains the Azure subscription id in the form of GUID 342 key_file contains the Azure X509 certificate in .pem form 343 """ 344 self.subscription_id = subscription_id 345 self.key_file = key_file 346 self.follow_redirects = kwargs.get('follow_redirects', True) 347 super(AzureNodeDriver, self).__init__( 348 self.subscription_id, 349 self.key_file, 350 secure=True, 351 **kwargs 352 ) 353 354 def list_sizes(self): 355 """ 356 Lists all sizes 357 358 :rtype: ``list`` of :class:`NodeSize` 359 """ 360 sizes = [] 361 362 for _, values in self._instance_types.items(): 363 node_size = self._to_node_size(copy.deepcopy(values)) 364 sizes.append(node_size) 365 366 return sizes 367 368 def list_images(self, location=None): 369 """ 370 Lists all images 371 372 :rtype: ``list`` of :class:`NodeImage` 373 """ 374 data = self._perform_get(self._get_image_path(), Images) 375 376 custom_image_data = self._perform_get( 377 self._get_vmimage_path(), 378 VMImages 379 ) 380 381 images = [self._to_image(i) for i in data] 382 images.extend(self._vm_to_image(j) for j in custom_image_data) 383 384 if location is not None: 385 images = [ 386 image 387 for image in images 388 if location in image.extra["location"] 389 ] 390 391 return images 392 393 def list_locations(self): 394 """ 395 Lists all locations 396 397 :rtype: ``list`` of :class:`NodeLocation` 398 """ 399 data = self._perform_get( 400 '/' + self.subscription_id + '/locations', 401 Locations 402 ) 403 404 return [self._to_location(location) for location in data] 405 406 def list_nodes(self, ex_cloud_service_name): 407 """ 408 List all nodes 409 410 ex_cloud_service_name parameter is used to scope the request 411 to a specific Cloud Service. This is a required parameter as 412 nodes cannot exist outside of a Cloud Service nor be shared 413 between a Cloud Service within Azure. 414 415 :param ex_cloud_service_name: Cloud Service name 416 :type ex_cloud_service_name: ``str`` 417 418 :rtype: ``list`` of :class:`Node` 419 """ 420 response = self._perform_get( 421 self._get_hosted_service_path(ex_cloud_service_name) + 422 '?embed-detail=True', 423 None 424 ) 425 self.raise_for_response(response, 200) 426 427 data = self._parse_response(response, HostedService) 428 429 vips = None 430 431 if (len(data.deployments) > 0 and 432 data.deployments[0].virtual_ips is not None): 433 vips = [vip.address for vip in data.deployments[0].virtual_ips] 434 435 try: 436 return [ 437 self._to_node(n, ex_cloud_service_name, vips) 438 for n in data.deployments[0].role_instance_list 439 ] 440 except IndexError: 441 return [] 442 443 def reboot_node(self, node, ex_cloud_service_name=None, 444 ex_deployment_slot=None): 445 """ 446 Reboots a node. 447 448 ex_cloud_service_name parameter is used to scope the request 449 to a specific Cloud Service. This is a required parameter as 450 nodes cannot exist outside of a Cloud Service nor be shared 451 between a Cloud Service within Azure. 452 453 :param ex_cloud_service_name: Cloud Service name 454 :type ex_cloud_service_name: ``str`` 455 456 :param ex_deployment_slot: Options are "production" (default) 457 or "Staging". (Optional) 458 :type ex_deployment_slot: ``str`` 459 460 :rtype: ``bool`` 461 """ 462 if ex_cloud_service_name is None: 463 if node.extra is not None: 464 ex_cloud_service_name = node.extra.get( 465 'ex_cloud_service_name' 466 ) 467 468 if not ex_cloud_service_name: 469 raise ValueError("ex_cloud_service_name is required.") 470 471 if not ex_deployment_slot: 472 ex_deployment_slot = "Production" 473 474 _deployment_name = self._get_deployment( 475 service_name=ex_cloud_service_name, 476 deployment_slot=ex_deployment_slot 477 ).name 478 479 try: 480 response = self._perform_post( 481 self._get_deployment_path_using_name( 482 ex_cloud_service_name, 483 _deployment_name 484 ) + '/roleinstances/' + _str(node.id) + '?comp=reboot', 485 '' 486 ) 487 488 self.raise_for_response(response, 202) 489 490 if self._parse_response_for_async_op(response): 491 return True 492 else: 493 return False 494 except Exception: 495 return False 496 497 def list_volumes(self, node=None): 498 """ 499 Lists volumes of the disks in the image repository that are 500 associated with the specified subscription. 501 502 Pass Node object to scope the list of volumes to a single 503 instance. 504 505 :rtype: ``list`` of :class:`StorageVolume` 506 """ 507 508 data = self._perform_get(self._get_disk_path(), Disks) 509 volumes = [self._to_volume(volume=v, node=node) for v in data] 510 return volumes 511 512 def create_node(self, name, size, image, ex_cloud_service_name, 513 ex_storage_service_name=None, ex_new_deployment=False, 514 ex_deployment_slot="Production", ex_deployment_name=None, 515 ex_admin_user_id="azureuser", ex_custom_data=None, 516 ex_virtual_network_name=None, ex_network_config=None, 517 auth=None, **kwargs): 518 """ 519 Create Azure Virtual Machine 520 521 Reference: http://bit.ly/1fIsCb7 522 [www.windowsazure.com/en-us/documentation/] 523 524 We default to: 525 526 + 3389/TCP - RDP - 1st Microsoft instance. 527 + RANDOM/TCP - RDP - All succeeding Microsoft instances. 528 529 + 22/TCP - SSH - 1st Linux instance 530 + RANDOM/TCP - SSH - All succeeding Linux instances. 531 532 The above replicates the standard behavior of the Azure UI. 533 You can retrieve the assigned ports to each instance by 534 using the following private function: 535 536 _get_endpoint_ports(service_name) 537 Returns public,private port key pair. 538 539 @inherits: :class:`NodeDriver.create_node` 540 541 :keyword image: The image to use when creating this node 542 :type image: `NodeImage` 543 544 :keyword size: The size of the instance to create 545 :type size: `NodeSize` 546 547 :keyword ex_cloud_service_name: Required. 548 Name of the Azure Cloud Service. 549 :type ex_cloud_service_name: ``str`` 550 551 :keyword ex_storage_service_name: Optional: 552 Name of the Azure Storage Service. 553 :type ex_storage_service_name: ``str`` 554 555 :keyword ex_new_deployment: Optional. Tells azure to create a 556 new deployment rather than add to an 557 existing one. 558 :type ex_new_deployment: ``boolean`` 559 560 :keyword ex_deployment_slot: Optional: Valid values: production| 561 staging. 562 Defaults to production. 563 :type ex_deployment_slot: ``str`` 564 565 :keyword ex_deployment_name: Optional. The name of the 566 deployment. 567 If this is not passed in we default 568 to using the Cloud Service name. 569 :type ex_deployment_name: ``str`` 570 571 :type ex_custom_data: ``str`` 572 :keyword ex_custom_data: Optional script or other data which is 573 injected into the VM when it's beginning 574 provisioned. 575 576 :keyword ex_admin_user_id: Optional. Defaults to 'azureuser'. 577 :type ex_admin_user_id: ``str`` 578 579 :keyword ex_virtual_network_name: Optional. If this is not passed 580 in no virtual network is used. 581 :type ex_virtual_network_name: ``str`` 582 583 :keyword ex_network_config: Optional. The ConfigurationSet to use 584 for network configuration 585 :type ex_network_config: `ConfigurationSet` 586 587 """ 588 # TODO: Refactor this method to make it more readable, split it into 589 # multiple smaller methods 590 auth = self._get_and_check_auth(auth) 591 password = auth.password 592 593 if not isinstance(size, NodeSize): 594 raise ValueError('Size must be an instance of NodeSize') 595 596 if not isinstance(image, NodeImage): 597 raise ValueError( 598 "Image must be an instance of NodeImage, " 599 "produced by list_images()" 600 ) 601 602 # Retrieve a list of currently available nodes for the provided cloud 603 # service 604 node_list = self.list_nodes( 605 ex_cloud_service_name=ex_cloud_service_name 606 ) 607 608 if ex_network_config is None: 609 network_config = ConfigurationSet() 610 else: 611 network_config = ex_network_config 612 network_config.configuration_set_type = 'NetworkConfiguration' 613 614 # Base64 encode custom data if provided 615 if ex_custom_data: 616 ex_custom_data = self._encode_base64(data=ex_custom_data) 617 618 # We do this because we need to pass a Configuration to the 619 # method. This will be either Linux or Windows. 620 if WINDOWS_SERVER_REGEX.search(image.id, re.I): 621 machine_config = WindowsConfigurationSet( 622 computer_name=name, 623 admin_password=password, 624 admin_user_name=ex_admin_user_id 625 ) 626 627 machine_config.domain_join = None 628 629 if not node_list or ex_new_deployment: 630 port = "3389" 631 else: 632 port = random.randint(41952, 65535) 633 endpoints = self._get_deployment( 634 service_name=ex_cloud_service_name, 635 deployment_slot=ex_deployment_slot 636 ) 637 638 for instances in endpoints.role_instance_list: 639 ports = [ep.public_port for ep in 640 instances.instance_endpoints] 641 642 while port in ports: 643 port = random.randint(41952, 65535) 644 645 endpoint = ConfigurationSetInputEndpoint( 646 name='Remote Desktop', 647 protocol='tcp', 648 port=port, 649 local_port='3389', 650 load_balanced_endpoint_set_name=None, 651 enable_direct_server_return=False 652 ) 653 else: 654 if not node_list or ex_new_deployment: 655 port = "22" 656 else: 657 port = random.randint(41952, 65535) 658 endpoints = self._get_deployment( 659 service_name=ex_cloud_service_name, 660 deployment_slot=ex_deployment_slot 661 ) 662 663 for instances in endpoints.role_instance_list: 664 ports = [] 665 if instances.instance_endpoints is not None: 666 for ep in instances.instance_endpoints: 667 ports += [ep.public_port] 668 669 while port in ports: 670 port = random.randint(41952, 65535) 671 672 endpoint = ConfigurationSetInputEndpoint( 673 name='SSH', 674 protocol='tcp', 675 port=port, 676 local_port='22', 677 load_balanced_endpoint_set_name=None, 678 enable_direct_server_return=False 679 ) 680 machine_config = LinuxConfigurationSet( 681 name, 682 ex_admin_user_id, 683 password, 684 False, 685 ex_custom_data 686 ) 687 688 network_config.input_endpoints.items.append(endpoint) 689 690 _storage_location = self._get_cloud_service_location( 691 service_name=ex_cloud_service_name 692 ) 693 694 if ex_storage_service_name is None: 695 ex_storage_service_name = ex_cloud_service_name 696 ex_storage_service_name = re.sub( 697 r'[\W_-]+', 698 '', 699 ex_storage_service_name.lower(), 700 flags=re.UNICODE 701 ) 702 703 if self._is_storage_service_unique( 704 service_name=ex_storage_service_name): 705 706 self._create_storage_account( 707 service_name=ex_storage_service_name, 708 location=_storage_location.service_location, 709 is_affinity_group=_storage_location.is_affinity_group 710 ) 711 712 # OK, bit annoying here. You must create a deployment before 713 # you can create an instance; however, the deployment function 714 # creates the first instance, but all subsequent instances 715 # must be created using the add_role function. 716 # 717 # So, yeah, annoying. 718 if not node_list or ex_new_deployment: 719 # This is the first node in this cloud service. 720 721 if not ex_deployment_name: 722 ex_deployment_name = ex_cloud_service_name 723 724 vm_image_id = None 725 disk_config = None 726 727 if image.extra.get('vm_image', False): 728 vm_image_id = image.id 729 # network_config = None 730 else: 731 blob_url = "http://%s.blob.core.windows.net" % ( 732 ex_storage_service_name) 733 734 # Azure's pattern in the UI. 735 disk_name = "%s-%s-%s.vhd" % ( 736 ex_cloud_service_name, 737 name, 738 time.strftime("%Y-%m-%d") 739 ) 740 741 media_link = "%s/vhds/%s" % (blob_url, disk_name) 742 743 disk_config = OSVirtualHardDisk(image.id, media_link) 744 745 response = self._perform_post( 746 self._get_deployment_path_using_name(ex_cloud_service_name), 747 AzureXmlSerializer.virtual_machine_deployment_to_xml( 748 ex_deployment_name, 749 ex_deployment_slot, 750 name, 751 name, 752 machine_config, 753 disk_config, 754 'PersistentVMRole', 755 network_config, 756 None, 757 None, 758 size.id, 759 ex_virtual_network_name, 760 vm_image_id 761 ) 762 ) 763 self.raise_for_response(response, 202) 764 self._ex_complete_async_azure_operation(response) 765 else: 766 _deployment_name = self._get_deployment( 767 service_name=ex_cloud_service_name, 768 deployment_slot=ex_deployment_slot 769 ).name 770 771 vm_image_id = None 772 disk_config = None 773 774 if image.extra.get('vm_image', False): 775 vm_image_id = image.id 776 # network_config = None 777 else: 778 blob_url = "http://%s.blob.core.windows.net" % ( 779 ex_storage_service_name 780 ) 781 disk_name = "%s-%s-%s.vhd" % ( 782 ex_cloud_service_name, 783 name, 784 time.strftime("%Y-%m-%d") 785 ) 786 media_link = "%s/vhds/%s" % (blob_url, disk_name) 787 disk_config = OSVirtualHardDisk(image.id, media_link) 788 789 path = self._get_role_path(ex_cloud_service_name, _deployment_name) 790 body = AzureXmlSerializer.add_role_to_xml( 791 name, # role_name 792 machine_config, # system_config 793 disk_config, # os_virtual_hard_disk 794 'PersistentVMRole', # role_type 795 network_config, # network_config 796 None, # availability_set_name 797 None, # data_virtual_hard_disks 798 vm_image_id, # vm_image 799 size.id # role_size 800 ) 801 802 response = self._perform_post(path, body) 803 self.raise_for_response(response, 202) 804 self._ex_complete_async_azure_operation(response) 805 806 return Node( 807 id=name, 808 name=name, 809 state=NodeState.PENDING, 810 public_ips=[], 811 private_ips=[], 812 driver=self.connection.driver, 813 extra={ 814 'ex_cloud_service_name': ex_cloud_service_name 815 } 816 ) 817 818 def destroy_node(self, node, ex_cloud_service_name=None, 819 ex_deployment_slot="Production"): 820 """ 821 Remove Azure Virtual Machine 822 823 This removes the instance, but does not 824 remove the disk. You will need to use destroy_volume. 825 Azure sometimes has an issue where it will hold onto 826 a blob lease for an extended amount of time. 827 828 :keyword ex_cloud_service_name: Required. 829 Name of the Azure Cloud Service. 830 :type ex_cloud_service_name: ``str`` 831 832 :keyword ex_deployment_slot: Optional: The name of the deployment 833 slot. If this is not passed in we 834 default to production. 835 :type ex_deployment_slot: ``str`` 836 """ 837 838 if not isinstance(node, Node): 839 raise ValueError("A libcloud Node object is required.") 840 841 if ex_cloud_service_name is None and node.extra is not None: 842 ex_cloud_service_name = node.extra.get('ex_cloud_service_name') 843 844 if not ex_cloud_service_name: 845 raise ValueError("Unable to get ex_cloud_service_name from Node.") 846 847 _deployment = self._get_deployment( 848 service_name=ex_cloud_service_name, 849 deployment_slot=ex_deployment_slot 850 ) 851 852 _deployment_name = _deployment.name 853 854 _server_deployment_count = len(_deployment.role_instance_list) 855 856 if _server_deployment_count > 1: 857 path = self._get_role_path( 858 ex_cloud_service_name, 859 _deployment_name, 860 node.id 861 ) 862 else: 863 path = self._get_deployment_path_using_name( 864 ex_cloud_service_name, 865 _deployment_name 866 ) 867 868 path += '?comp=media' 869 870 self._perform_delete(path) 871 872 return True 873 874 def ex_list_cloud_services(self): 875 return self._perform_get( 876 self._get_hosted_service_path(), 877 HostedServices 878 ) 879 880 def ex_create_cloud_service(self, name, location, description=None, 881 extended_properties=None): 882 """ 883 Create an azure cloud service. 884 885 :param name: Name of the service to create 886 :type name: ``str`` 887 888 :param location: Standard azure location string 889 :type location: ``str`` 890 891 :param description: Optional description 892 :type description: ``str`` 893 894 :param extended_properties: Optional extended_properties 895 :type extended_properties: ``dict`` 896 897 :rtype: ``bool`` 898 """ 899 900 response = self._perform_cloud_service_create( 901 self._get_hosted_service_path(), 902 AzureXmlSerializer.create_hosted_service_to_xml( 903 name, 904 self._encode_base64(name), 905 description, 906 location, 907 None, 908 extended_properties 909 ) 910 ) 911 912 self.raise_for_response(response, 201) 913 914 return True 915 916 def ex_destroy_cloud_service(self, name): 917 """ 918 Delete an azure cloud service. 919 920 :param name: Name of the cloud service to destroy. 921 :type name: ``str`` 922 923 :rtype: ``bool`` 924 """ 925 response = self._perform_cloud_service_delete( 926 self._get_hosted_service_path(name) 927 ) 928 929 self.raise_for_response(response, 200) 930 931 return True 932 933 def ex_add_instance_endpoints(self, node, endpoints, 934 ex_deployment_slot="Production"): 935 all_endpoints = [ 936 { 937 "name": endpoint.name, 938 "protocol": endpoint.protocol, 939 "port": endpoint.public_port, 940 "local_port": endpoint.local_port, 941 942 } 943 for endpoint in node.extra['instance_endpoints'] 944 ] 945 946 all_endpoints.extend(endpoints) 947 # pylint: disable=assignment-from-no-return 948 result = self.ex_set_instance_endpoints(node, all_endpoints, 949 ex_deployment_slot) 950 return result 951 952 def ex_set_instance_endpoints(self, node, endpoints, 953 ex_deployment_slot="Production"): 954 955 """ 956 For example:: 957 958 endpoint = ConfigurationSetInputEndpoint( 959 name='SSH', 960 protocol='tcp', 961 port=port, 962 local_port='22', 963 load_balanced_endpoint_set_name=None, 964 enable_direct_server_return=False 965 ) 966 { 967 'name': 'SSH', 968 'protocol': 'tcp', 969 'port': port, 970 'local_port': '22' 971 } 972 """ 973 ex_cloud_service_name = node.extra['ex_cloud_service_name'] 974 vm_role_name = node.name 975 976 network_config = ConfigurationSet() 977 network_config.configuration_set_type = 'NetworkConfiguration' 978 979 for endpoint in endpoints: 980 new_endpoint = ConfigurationSetInputEndpoint(**endpoint) 981 network_config.input_endpoints.items.append(new_endpoint) 982 983 _deployment_name = self._get_deployment( 984 service_name=ex_cloud_service_name, 985 deployment_slot=ex_deployment_slot 986 ).name 987 988 response = self._perform_put( 989 self._get_role_path( 990 ex_cloud_service_name, 991 _deployment_name, 992 vm_role_name 993 ), 994 AzureXmlSerializer.add_role_to_xml( 995 None, # role_name 996 None, # system_config 997 None, # os_virtual_hard_disk 998 'PersistentVMRole', # role_type 999 network_config, # network_config 1000 None, # availability_set_name 1001 None, # data_virtual_hard_disks 1002 None, # vm_image 1003 None # role_size 1004 ) 1005 ) 1006 1007 self.raise_for_response(response, 202) 1008 1009 def ex_create_storage_service(self, name, location, 1010 description=None, affinity_group=None, 1011 extended_properties=None): 1012 """ 1013 Create an azure storage service. 1014 1015 :param name: Name of the service to create 1016 :type name: ``str`` 1017 1018 :param location: Standard azure location string 1019 :type location: ``str`` 1020 1021 :param description: (Optional) Description of storage service. 1022 :type description: ``str`` 1023 1024 :param affinity_group: (Optional) Azure affinity group. 1025 :type affinity_group: ``str`` 1026 1027 :param extended_properties: (Optional) Additional configuration 1028 options support by Azure. 1029 :type extended_properties: ``dict`` 1030 1031 :rtype: ``bool`` 1032 """ 1033 1034 response = self._perform_storage_service_create( 1035 self._get_storage_service_path(), 1036 AzureXmlSerializer.create_storage_service_to_xml( 1037 service_name=name, 1038 label=self._encode_base64(name), 1039 description=description, 1040 location=location, 1041 affinity_group=affinity_group, 1042 extended_properties=extended_properties 1043 ) 1044 ) 1045 1046 self.raise_for_response(response, 202) 1047 1048 return True 1049 1050 def ex_destroy_storage_service(self, name): 1051 """ 1052 Destroy storage service. Storage service must not have any active 1053 blobs. Sometimes Azure likes to hold onto volumes after they are 1054 deleted for an inordinate amount of time, so sleep before calling 1055 this method after volume deletion. 1056 1057 :param name: Name of storage service. 1058 :type name: ``str`` 1059 1060 :rtype: ``bool`` 1061 """ 1062 1063 response = self._perform_storage_service_delete( 1064 self._get_storage_service_path(name) 1065 ) 1066 self.raise_for_response(response, 200) 1067 1068 return True 1069 1070 """ 1071 Functions not implemented 1072 """ 1073 1074 def create_volume_snapshot(self): 1075 raise NotImplementedError( 1076 'You cannot create snapshots of ' 1077 'Azure VMs at this time.' 1078 ) 1079 1080 def attach_volume(self): 1081 raise NotImplementedError( 1082 'attach_volume is not supported ' 1083 'at this time.' 1084 ) 1085 1086 def create_volume(self): 1087 raise NotImplementedError( 1088 'create_volume is not supported ' 1089 'at this time.' 1090 ) 1091 1092 def detach_volume(self): 1093 raise NotImplementedError( 1094 'detach_volume is not supported ' 1095 'at this time.' 1096 ) 1097 1098 def destroy_volume(self): 1099 raise NotImplementedError( 1100 'destroy_volume is not supported ' 1101 'at this time.' 1102 ) 1103 1104 """ 1105 Private Functions 1106 """ 1107 1108 def _perform_cloud_service_create(self, path, data): 1109 request = AzureHTTPRequest() 1110 request.method = 'POST' 1111 request.host = AZURE_SERVICE_MANAGEMENT_HOST 1112 request.path = path 1113 request.body = data 1114 request.path, request.query = self._update_request_uri_query(request) 1115 request.headers = self._update_management_header(request) 1116 response = self._perform_request(request) 1117 1118 return response 1119 1120 def _perform_cloud_service_delete(self, path): 1121 request = AzureHTTPRequest() 1122 request.method = 'DELETE' 1123 request.host = AZURE_SERVICE_MANAGEMENT_HOST 1124 request.path = path 1125 request.path, request.query = self._update_request_uri_query(request) 1126 request.headers = self._update_management_header(request) 1127 response = self._perform_request(request) 1128 1129 return response 1130 1131 def _perform_storage_service_create(self, path, data): 1132 request = AzureHTTPRequest() 1133 request.method = 'POST' 1134 request.host = AZURE_SERVICE_MANAGEMENT_HOST 1135 request.path = path 1136 request.body = data 1137 request.path, request.query = self._update_request_uri_query(request) 1138 request.headers = self._update_management_header(request) 1139 response = self._perform_request(request) 1140 1141 return response 1142 1143 def _perform_storage_service_delete(self, path): 1144 request = AzureHTTPRequest() 1145 request.method = 'DELETE' 1146 request.host = AZURE_SERVICE_MANAGEMENT_HOST 1147 request.path = path 1148 request.path, request.query = self._update_request_uri_query(request) 1149 request.headers = self._update_management_header(request) 1150 response = self._perform_request(request) 1151 1152 return response 1153 1154 def _to_node(self, data, ex_cloud_service_name=None, virtual_ips=None): 1155 """ 1156 Convert the data from a Azure response object into a Node 1157 """ 1158 1159 remote_desktop_port = '' 1160 ssh_port = '' 1161 public_ips = virtual_ips or [] 1162 1163 if data.instance_endpoints is not None: 1164 if len(data.instance_endpoints) >= 1: 1165 public_ips = [data.instance_endpoints[0].vip] 1166 1167 for port in data.instance_endpoints: 1168 if port.name == 'Remote Desktop': 1169 remote_desktop_port = port.public_port 1170 1171 if port.name == "SSH": 1172 ssh_port = port.public_port 1173 1174 return Node( 1175 id=data.role_name, 1176 name=data.role_name, 1177 state=self.NODE_STATE_MAP.get( 1178 data.instance_status, 1179 NodeState.UNKNOWN 1180 ), 1181 public_ips=public_ips, 1182 private_ips=[data.ip_address], 1183 driver=self.connection.driver, 1184 extra={ 1185 'instance_endpoints': data.instance_endpoints, 1186 'remote_desktop_port': remote_desktop_port, 1187 'ssh_port': ssh_port, 1188 'power_state': data.power_state, 1189 'instance_size': data.instance_size, 1190 'ex_cloud_service_name': ex_cloud_service_name 1191 } 1192 ) 1193 1194 def _to_location(self, data): 1195 """ 1196 Convert the data from a Azure response object into a location 1197 """ 1198 country = data.display_name 1199 1200 if "Asia" in data.display_name: 1201 country = "Asia" 1202 1203 if "Europe" in data.display_name: 1204 country = "Europe" 1205 1206 if "US" in data.display_name: 1207 country = "US" 1208 1209 if "Japan" in data.display_name: 1210 country = "Japan" 1211 1212 if "Brazil" in data.display_name: 1213 country = "Brazil" 1214 1215 vm_role_sizes = data.compute_capabilities.virtual_machines_role_sizes 1216 1217 return AzureNodeLocation( 1218 id=data.name, 1219 name=data.display_name, 1220 country=country, 1221 driver=self.connection.driver, 1222 available_services=data.available_services, 1223 virtual_machine_role_sizes=vm_role_sizes 1224 ) 1225 1226 def _to_node_size(self, data): 1227 """ 1228 Convert the AZURE_COMPUTE_INSTANCE_TYPES into NodeSize 1229 """ 1230 return NodeSize( 1231 id=data["id"], 1232 name=data["name"], 1233 ram=data["ram"], 1234 disk=data["disk"], 1235 bandwidth=data["bandwidth"], 1236 price=data["price"], 1237 driver=self.connection.driver, 1238 extra={ 1239 'max_data_disks': data["max_data_disks"], 1240 'cores': data["cores"] 1241 } 1242 ) 1243 1244 def _to_image(self, data): 1245 return NodeImage( 1246 id=data.name, 1247 name=data.label, 1248 driver=self.connection.driver, 1249 extra={ 1250 'os': data.os, 1251 'category': data.category, 1252 'description': data.description, 1253 'location': data.location, 1254 'affinity_group': data.affinity_group, 1255 'media_link': data.media_link, 1256 'vm_image': False 1257 } 1258 ) 1259 1260 def _vm_to_image(self, data): 1261 return NodeImage( 1262 id=data.name, 1263 name=data.label, 1264 driver=self.connection.driver, 1265 extra={ 1266 'os': data.os_disk_configuration.os, 1267 'category': data.category, 1268 'location': data.location, 1269 'media_link': data.os_disk_configuration.media_link, 1270 'affinity_group': data.affinity_group, 1271 'deployment_name': data.deployment_name, 1272 'vm_image': True 1273 } 1274 ) 1275 1276 def _to_volume(self, volume, node): 1277 extra = { 1278 'affinity_group': volume.affinity_group, 1279 'os': volume.os, 1280 'location': volume.location, 1281 'media_link': volume.media_link, 1282 'source_image_name': volume.source_image_name 1283 } 1284 1285 role_name = getattr(volume.attached_to, 'role_name', None) 1286 hosted_service_name = getattr( 1287 volume.attached_to, 1288 'hosted_service_name', 1289 None 1290 ) 1291 1292 deployment_name = getattr( 1293 volume.attached_to, 1294 'deployment_name', 1295 None 1296 ) 1297 1298 if role_name is not None: 1299 extra['role_name'] = role_name 1300 1301 if hosted_service_name is not None: 1302 extra['hosted_service_name'] = hosted_service_name 1303 1304 if deployment_name is not None: 1305 extra['deployment_name'] = deployment_name 1306 1307 if node: 1308 if role_name is not None and role_name == node.id: 1309 return StorageVolume( 1310 id=volume.name, 1311 name=volume.name, 1312 size=int(volume.logical_disk_size_in_gb), 1313 driver=self.connection.driver, 1314 extra=extra 1315 ) 1316 else: 1317 return StorageVolume( 1318 id=volume.name, 1319 name=volume.name, 1320 size=int(volume.logical_disk_size_in_gb), 1321 driver=self.connection.driver, 1322 extra=extra 1323 ) 1324 1325 def _get_deployment(self, **kwargs): 1326 _service_name = kwargs['service_name'] 1327 _deployment_slot = kwargs['deployment_slot'] 1328 1329 response = self._perform_get( 1330 self._get_deployment_path_using_slot( 1331 _service_name, 1332 _deployment_slot 1333 ), 1334 None 1335 ) 1336 1337 self.raise_for_response(response, 200) 1338 1339 return self._parse_response(response, Deployment) 1340 1341 def _get_cloud_service_location(self, service_name=None): 1342 if not service_name: 1343 raise ValueError("service_name is required.") 1344 1345 res = self._perform_get( 1346 '%s?embed-detail=False' % ( 1347 self._get_hosted_service_path(service_name) 1348 ), 1349 HostedService 1350 ) 1351 1352 _affinity_group = res.hosted_service_properties.affinity_group 1353 _cloud_service_location = res.hosted_service_properties.location 1354 1355 if _affinity_group is not None and _affinity_group != '': 1356 return self.service_location(True, _affinity_group) 1357 elif _cloud_service_location is not None: 1358 return self.service_location(False, _cloud_service_location) 1359 else: 1360 return None 1361 1362 def _is_storage_service_unique(self, service_name=None): 1363 if not service_name: 1364 raise ValueError("service_name is required.") 1365 1366 _check_availability = self._perform_get( 1367 '%s/operations/isavailable/%s%s' % ( 1368 self._get_storage_service_path(), 1369 _str(service_name), 1370 '' 1371 ), 1372 AvailabilityResponse 1373 ) 1374 1375 self.raise_for_response(_check_availability, 200) 1376 1377 return _check_availability.result 1378 1379 def _create_storage_account(self, **kwargs): 1380 if kwargs['is_affinity_group'] is True: 1381 response = self._perform_post( 1382 self._get_storage_service_path(), 1383 AzureXmlSerializer.create_storage_service_input_to_xml( 1384 kwargs['service_name'], 1385 kwargs['service_name'], 1386 self._encode_base64(kwargs['service_name']), 1387 kwargs['location'], 1388 None, # Location 1389 True, # geo_replication_enabled 1390 None # extended_properties 1391 ) 1392 ) 1393 1394 self.raise_for_response(response, 202) 1395 1396 else: 1397 response = self._perform_post( 1398 self._get_storage_service_path(), 1399 AzureXmlSerializer.create_storage_service_input_to_xml( 1400 kwargs['service_name'], 1401 kwargs['service_name'], 1402 self._encode_base64(kwargs['service_name']), 1403 None, # Affinity Group 1404 kwargs['location'], # Location 1405 True, # geo_replication_enabled 1406 None # extended_properties 1407 ) 1408 ) 1409 1410 self.raise_for_response(response, 202) 1411 1412 # We need to wait for this to be created before we can 1413 # create the storage container and the instance. 1414 self._ex_complete_async_azure_operation( 1415 response, 1416 "create_storage_account" 1417 ) 1418 1419 def _get_operation_status(self, request_id): 1420 return self._perform_get( 1421 '/' + self.subscription_id + '/operations/' + _str(request_id), 1422 Operation 1423 ) 1424 1425 def _perform_get(self, path, response_type): 1426 request = AzureHTTPRequest() 1427 request.method = 'GET' 1428 request.host = AZURE_SERVICE_MANAGEMENT_HOST 1429 request.path = path 1430 request.path, request.query = self._update_request_uri_query(request) 1431 request.headers = self._update_management_header(request) 1432 response = self._perform_request(request) 1433 1434 if response_type is not None: 1435 return self._parse_response(response, response_type) 1436 1437 return response 1438 1439 def _perform_post(self, path, body, response_type=None): 1440 request = AzureHTTPRequest() 1441 request.method = 'POST' 1442 request.host = AZURE_SERVICE_MANAGEMENT_HOST 1443 request.path = path 1444 request.body = ensure_string(self._get_request_body(body)) 1445 request.path, request.query = self._update_request_uri_query(request) 1446 request.headers = self._update_management_header(request) 1447 response = self._perform_request(request) 1448 1449 return response 1450 1451 def _perform_put(self, path, body, response_type=None): 1452 request = AzureHTTPRequest() 1453 request.method = 'PUT' 1454 request.host = AZURE_SERVICE_MANAGEMENT_HOST 1455 request.path = path 1456 request.body = ensure_string(self._get_request_body(body)) 1457 request.path, request.query = self._update_request_uri_query(request) 1458 request.headers = self._update_management_header(request) 1459 response = self._perform_request(request) 1460 1461 return response 1462 1463 def _perform_delete(self, path): 1464 request = AzureHTTPRequest() 1465 request.method = 'DELETE' 1466 request.host = AZURE_SERVICE_MANAGEMENT_HOST 1467 request.path = path 1468 request.path, request.query = self._update_request_uri_query(request) 1469 request.headers = self._update_management_header(request) 1470 response = self._perform_request(request) 1471 1472 self.raise_for_response(response, 202) 1473 1474 def _perform_request(self, request): 1475 try: 1476 return self.connection.request( 1477 action=request.path, 1478 data=request.body, 1479 headers=request.headers, 1480 method=request.method 1481 ) 1482 except AzureRedirectException as e: 1483 parsed_url = urlparse.urlparse(e.location) 1484 request.host = parsed_url.netloc 1485 return self._perform_request(request) 1486 except Exception as e: 1487 raise e 1488 1489 def _update_request_uri_query(self, request): 1490 """ 1491 pulls the query string out of the URI and moves it into 1492 the query portion of the request object. If there are already 1493 query parameters on the request the parameters in the URI will 1494 appear after the existing parameters 1495 """ 1496 if '?' in request.path: 1497 request.path, _, query_string = request.path.partition('?') 1498 if query_string: 1499 query_params = query_string.split('&') 1500 for query in query_params: 1501 if '=' in query: 1502 name, _, value = query.partition('=') 1503 request.query.append((name, value)) 1504 1505 request.path = url_quote(request.path, '/()$=\',') 1506 1507 # add encoded queries to request.path. 1508 if request.query: 1509 request.path += '?' 1510 for name, value in request.query: 1511 if value is not None: 1512 request.path += '%s=%s%s' % ( 1513 name, 1514 url_quote(value, '/()$=\','), 1515 '&' 1516 ) 1517 request.path = request.path[:-1] 1518 1519 return request.path, request.query 1520 1521 def _update_management_header(self, request): 1522 """ 1523 Add additional headers for management. 1524 """ 1525 1526 if request.method in ['PUT', 'POST', 'MERGE', 'DELETE']: 1527 request.headers['Content-Length'] = str(len(request.body)) 1528 1529 # append additional headers base on the service 1530 # request.headers.append(('x-ms-version', X_MS_VERSION)) 1531 1532 # if it is not GET or HEAD request, must set content-type. 1533 if request.method not in ['GET', 'HEAD']: 1534 for key in request.headers: 1535 if 'content-type' == key.lower(): 1536 break 1537 else: 1538 request.headers['Content-Type'] = 'application/xml' 1539 1540 return request.headers 1541 1542 def _parse_response(self, response, return_type): 1543 """ 1544 Parse the HTTPResponse's body and fill all the data into a class of 1545 return_type. 1546 """ 1547 1548 return self._parse_response_body_from_xml_text( 1549 response=response, 1550 return_type=return_type 1551 ) 1552 1553 def _parse_response_body_from_xml_text(self, response, return_type): 1554 """ 1555 parse the xml and fill all the data into a class of return_type 1556 """ 1557 respbody = response.body 1558 1559 doc = minidom.parseString(respbody) 1560 return_obj = return_type() 1561 for node in self._get_child_nodes(doc, return_type.__name__): 1562 self._fill_data_to_return_object(node, return_obj) 1563 1564 # Note: We always explicitly assign status code to the custom return 1565 # type object 1566 return_obj.status = response.status 1567 1568 return return_obj 1569 1570 def _get_child_nodes(self, node, tag_name): 1571 return [childNode for childNode in node.getElementsByTagName(tag_name) 1572 if childNode.parentNode == node] 1573 1574 def _fill_data_to_return_object(self, node, return_obj): 1575 members = dict(vars(return_obj)) 1576 for name, value in members.items(): 1577 if isinstance(value, _ListOf): 1578 setattr( 1579 return_obj, 1580 name, 1581 self._fill_list_of( 1582 node, 1583 value.list_type, 1584 value.xml_element_name 1585 ) 1586 ) 1587 elif isinstance(value, ScalarListOf): 1588 setattr( 1589 return_obj, 1590 name, 1591 self._fill_scalar_list_of( 1592 node, 1593 value.list_type, 1594 self._get_serialization_name(name), 1595 value.xml_element_name 1596 ) 1597 ) 1598 elif isinstance(value, _DictOf): 1599 setattr( 1600 return_obj, 1601 name, 1602 self._fill_dict_of( 1603 node, 1604 self._get_serialization_name(name), 1605 value.pair_xml_element_name, 1606 value.key_xml_element_name, 1607 value.value_xml_element_name 1608 ) 1609 ) 1610 elif isinstance(value, WindowsAzureData): 1611 setattr( 1612 return_obj, 1613 name, 1614 self._fill_instance_child(node, name, value.__class__) 1615 ) 1616 elif isinstance(value, dict): 1617 setattr( 1618 return_obj, 1619 name, 1620 self._fill_dict( 1621 node, 1622 self._get_serialization_name(name) 1623 ) 1624 ) 1625 elif isinstance(value, _Base64String): 1626 value = self._fill_data_minidom(node, name, '') 1627 if value is not None: 1628 value = self._decode_base64_to_text(value) 1629 # always set the attribute, 1630 # so we don't end up returning an object 1631 # with type _Base64String 1632 setattr(return_obj, name, value) 1633 else: 1634 value = self._fill_data_minidom(node, name, value) 1635 if value is not None: 1636 setattr(return_obj, name, value) 1637 1638 def _fill_list_of(self, xmldoc, element_type, xml_element_name): 1639 xmlelements = self._get_child_nodes(xmldoc, xml_element_name) 1640 return [ 1641 self._parse_response_body_from_xml_node(xmlelement, element_type) 1642 for xmlelement in xmlelements 1643 ] 1644 1645 def _parse_response_body_from_xml_node(self, node, return_type): 1646 """ 1647 parse the xml and fill all the data into a class of return_type 1648 """ 1649 return_obj = return_type() 1650 self._fill_data_to_return_object(node, return_obj) 1651 1652 return return_obj 1653 1654 def _fill_scalar_list_of(self, 1655 xmldoc, 1656 element_type, 1657 parent_xml_element_name, 1658 xml_element_name): 1659 xmlelements = self._get_child_nodes(xmldoc, parent_xml_element_name) 1660 1661 if xmlelements: 1662 xmlelements = self._get_child_nodes( 1663 xmlelements[0], 1664 xml_element_name 1665 ) 1666 return [ 1667 self._get_node_value(xmlelement, element_type) 1668 for xmlelement in xmlelements 1669 ] 1670 1671 def _get_node_value(self, xmlelement, data_type): 1672 value = xmlelement.firstChild.nodeValue 1673 if data_type is datetime: 1674 return self._to_datetime(value) 1675 elif data_type is bool: 1676 return value.lower() != 'false' 1677 else: 1678 return data_type(value) 1679 1680 def _get_serialization_name(self, element_name): 1681 """ 1682 Converts a Python name into a serializable name. 1683 """ 1684 1685 known = _KNOWN_SERIALIZATION_XFORMS.get(element_name) 1686 if known is not None: 1687 return known 1688 1689 if element_name.startswith('x_ms_'): 1690 return element_name.replace('_', '-') 1691 1692 if element_name.endswith('_id'): 1693 element_name = element_name.replace('_id', 'ID') 1694 1695 for name in ['content_', 'last_modified', 'if_', 'cache_control']: 1696 if element_name.startswith(name): 1697 element_name = element_name.replace('_', '-_') 1698 1699 return ''.join(name.capitalize() for name in element_name.split('_')) 1700 1701 def _fill_dict_of(self, xmldoc, parent_xml_element_name, 1702 pair_xml_element_name, key_xml_element_name, 1703 value_xml_element_name): 1704 return_obj = {} 1705 1706 xmlelements = self._get_child_nodes(xmldoc, parent_xml_element_name) 1707 1708 if xmlelements: 1709 xmlelements = self._get_child_nodes( 1710 xmlelements[0], 1711 pair_xml_element_name 1712 ) 1713 for pair in xmlelements: 1714 keys = self._get_child_nodes(pair, key_xml_element_name) 1715 values = self._get_child_nodes(pair, value_xml_element_name) 1716 if keys and values: 1717 key = keys[0].firstChild.nodeValue 1718 value = values[0].firstChild.nodeValue 1719 return_obj[key] = value 1720 1721 return return_obj 1722 1723 def _fill_instance_child(self, xmldoc, element_name, return_type): 1724 """ 1725 Converts a child of the current dom element to the specified type. 1726 """ 1727 xmlelements = self._get_child_nodes( 1728 xmldoc, 1729 self._get_serialization_name(element_name) 1730 ) 1731 1732 if not xmlelements: 1733 return None 1734 1735 return_obj = return_type() 1736 self._fill_data_to_return_object(xmlelements[0], return_obj) 1737 1738 return return_obj 1739 1740 def _fill_dict(self, xmldoc, element_name): 1741 xmlelements = self._get_child_nodes(xmldoc, element_name) 1742 1743 if xmlelements: 1744 return_obj = {} 1745 for child in xmlelements[0].childNodes: 1746 if child.firstChild: 1747 return_obj[child.nodeName] = child.firstChild.nodeValue 1748 return return_obj 1749 1750 def _encode_base64(self, data): 1751 if isinstance(data, _unicode_type): 1752 data = data.encode('utf-8') 1753 encoded = base64.b64encode(data) 1754 return encoded.decode('utf-8') 1755 1756 def _decode_base64_to_bytes(self, data): 1757 if isinstance(data, _unicode_type): 1758 data = data.encode('utf-8') 1759 return base64.b64decode(data) 1760 1761 def _decode_base64_to_text(self, data): 1762 decoded_bytes = self._decode_base64_to_bytes(data) 1763 return decoded_bytes.decode('utf-8') 1764 1765 def _fill_data_minidom(self, xmldoc, element_name, data_member): 1766 xmlelements = self._get_child_nodes( 1767 xmldoc, 1768 self._get_serialization_name(element_name) 1769 ) 1770 1771 if not xmlelements or not xmlelements[0].childNodes: 1772 return None 1773 1774 value = xmlelements[0].firstChild.nodeValue 1775 1776 if data_member is None: 1777 return value 1778 elif isinstance(data_member, datetime): 1779 return self._to_datetime(value) 1780 elif type(data_member) is bool: 1781 return value.lower() != 'false' 1782 elif type(data_member) is str: 1783 return _real_unicode(value) 1784 else: 1785 return type(data_member)(value) 1786 1787 def _to_datetime(self, strtime): 1788 return datetime.strptime(strtime, "%Y-%m-%dT%H:%M:%S.%f") 1789 1790 def _get_request_body(self, request_body): 1791 if request_body is None: 1792 return b'' 1793 1794 if isinstance(request_body, WindowsAzureData): 1795 request_body = self._convert_class_to_xml(request_body) 1796 1797 if isinstance(request_body, bytes): 1798 return request_body 1799 1800 if isinstance(request_body, _unicode_type): 1801 return request_body.encode('utf-8') 1802 1803 request_body = str(request_body) 1804 if isinstance(request_body, _unicode_type): 1805 return request_body.encode('utf-8') 1806 1807 return request_body 1808 1809 def _convert_class_to_xml(self, source, xml_prefix=True): 1810 root = ET.Element() 1811 doc = self._construct_element_tree(source, root) 1812 1813 result = ensure_string(ET.tostring(doc, encoding='utf-8', 1814 method='xml')) 1815 return result 1816 1817 def _construct_element_tree(self, source, etree): 1818 if source is None: 1819 return ET.Element() 1820 1821 if isinstance(source, list): 1822 for value in source: 1823 etree.append(self._construct_element_tree(value, etree)) 1824 1825 elif isinstance(source, WindowsAzureData): 1826 class_name = source.__class__.__name__ 1827 etree.append(ET.Element(class_name)) 1828 1829 for name, value in vars(source).items(): 1830 if value is not None: 1831 if (isinstance(value, list) or 1832 isinstance(value, WindowsAzureData)): 1833 etree.append( 1834 self._construct_element_tree(value, etree) 1835 ) 1836 else: 1837 ele = ET.Element(self._get_serialization_name(name)) 1838 ele.text = xml_escape(str(value)) 1839 etree.append(ele) 1840 1841 etree.append(ET.Element(class_name)) 1842 return etree 1843 1844 def _parse_response_for_async_op(self, response): 1845 if response is None: 1846 return None 1847 1848 result = AsynchronousOperationResult() 1849 if response.headers: 1850 for name, value in response.headers.items(): 1851 if name.lower() == 'x-ms-request-id': 1852 result.request_id = value 1853 1854 return result 1855 1856 def _get_deployment_path_using_name(self, service_name, 1857 deployment_name=None): 1858 components = [ 1859 'services/hostedservices/', 1860 _str(service_name), 1861 '/deployments' 1862 ] 1863 resource = ''.join(components) 1864 return self._get_path(resource, deployment_name) 1865 1866 def _get_path(self, resource, name): 1867 path = '/' + self.subscription_id + '/' + resource 1868 if name is not None: 1869 path += '/' + _str(name) 1870 return path 1871 1872 def _get_image_path(self, image_name=None): 1873 return self._get_path('services/images', image_name) 1874 1875 def _get_vmimage_path(self, image_name=None): 1876 return self._get_path('services/vmimages', image_name) 1877 1878 def _get_hosted_service_path(self, service_name=None): 1879 return self._get_path('services/hostedservices', service_name) 1880 1881 def _get_deployment_path_using_slot(self, service_name, slot=None): 1882 return self._get_path( 1883 'services/hostedservices/%s/deploymentslots' % ( 1884 _str(service_name) 1885 ), 1886 slot 1887 ) 1888 1889 def _get_disk_path(self, disk_name=None): 1890 return self._get_path('services/disks', disk_name) 1891 1892 def _get_role_path(self, service_name, deployment_name, role_name=None): 1893 components = [ 1894 'services/hostedservices/', 1895 _str(service_name), 1896 '/deployments/', 1897 deployment_name, 1898 '/roles' 1899 ] 1900 resource = ''.join(components) 1901 return self._get_path(resource, role_name) 1902 1903 def _get_storage_service_path(self, service_name=None): 1904 return self._get_path('services/storageservices', service_name) 1905 1906 def _ex_complete_async_azure_operation(self, response=None, 1907 operation_type='create_node'): 1908 request_id = self._parse_response_for_async_op(response) 1909 operation_status = self._get_operation_status(request_id.request_id) 1910 1911 timeout = 60 * 5 1912 waittime = 0 1913 interval = 5 1914 1915 while operation_status.status == "InProgress" and waittime < timeout: 1916 operation_status = self._get_operation_status(request_id) 1917 if operation_status.status == "Succeeded": 1918 break 1919 1920 waittime += interval 1921 time.sleep(interval) 1922 1923 if operation_status.status == 'Failed': 1924 raise LibcloudError( 1925 'Message: Async request for operation %s has failed' % 1926 operation_type, 1927 driver=self.connection.driver 1928 ) 1929 1930 def raise_for_response(self, response, valid_response): 1931 if response.status != valid_response: 1932 values = (response.error, response.body, response.status) 1933 message = 'Message: %s, Body: %s, Status code: %s' % (values) 1934 raise LibcloudError(message, driver=self) 1935 1936 1937""" 1938XML Serializer 1939 1940Borrowed from the Azure SDK for Python which is licensed under Apache 2.0. 1941 1942https://github.com/Azure/azure-sdk-for-python 1943""" 1944 1945 1946def _lower(text): 1947 return text.lower() 1948 1949 1950class AzureXmlSerializer(object): 1951 1952 @staticmethod 1953 def create_storage_service_input_to_xml(service_name, 1954 description, 1955 label, 1956 affinity_group, 1957 location, 1958 geo_replication_enabled, 1959 extended_properties): 1960 return AzureXmlSerializer.doc_from_data( 1961 'CreateStorageServiceInput', 1962 [ 1963 ('ServiceName', service_name), 1964 ('Description', description), 1965 ('Label', label), 1966 ('AffinityGroup', affinity_group), 1967 ('Location', location), 1968 ('GeoReplicationEnabled', geo_replication_enabled, _lower) 1969 ], 1970 extended_properties 1971 ) 1972 1973 @staticmethod 1974 def update_storage_service_input_to_xml(description, 1975 label, 1976 geo_replication_enabled, 1977 extended_properties): 1978 return AzureXmlSerializer.doc_from_data( 1979 'UpdateStorageServiceInput', 1980 [ 1981 ('Description', description), 1982 ('Label', label, AzureNodeDriver._encode_base64), 1983 ('GeoReplicationEnabled', geo_replication_enabled, _lower) 1984 ], 1985 extended_properties 1986 ) 1987 1988 @staticmethod 1989 def regenerate_keys_to_xml(key_type): 1990 return AzureXmlSerializer.doc_from_data( 1991 'RegenerateKeys', 1992 [('KeyType', key_type)] 1993 ) 1994 1995 @staticmethod 1996 def update_hosted_service_to_xml(label, description, extended_properties): 1997 return AzureXmlSerializer.doc_from_data( 1998 'UpdateHostedService', 1999 [ 2000 ('Label', label, AzureNodeDriver._encode_base64), 2001 ('Description', description) 2002 ], 2003 extended_properties 2004 ) 2005 2006 @staticmethod 2007 def create_hosted_service_to_xml(service_name, 2008 label, 2009 description, 2010 location, 2011 affinity_group=None, 2012 extended_properties=None): 2013 if affinity_group: 2014 return AzureXmlSerializer.doc_from_data( 2015 'CreateHostedService', 2016 [ 2017 ('ServiceName', service_name), 2018 ('Label', label), 2019 ('Description', description), 2020 ('AffinityGroup', affinity_group), 2021 ], 2022 extended_properties 2023 ) 2024 2025 return AzureXmlSerializer.doc_from_data( 2026 'CreateHostedService', 2027 [ 2028 ('ServiceName', service_name), 2029 ('Label', label), 2030 ('Description', description), 2031 ('Location', location), 2032 ], 2033 extended_properties 2034 ) 2035 2036 @staticmethod 2037 def create_storage_service_to_xml(service_name, 2038 label, 2039 description, 2040 location, 2041 affinity_group, 2042 extended_properties=None): 2043 2044 return AzureXmlSerializer.doc_from_data( 2045 'CreateStorageServiceInput', 2046 [ 2047 ('ServiceName', service_name), 2048 ('Label', label), 2049 ('Description', description), 2050 ('Location', location), 2051 ('AffinityGroup', affinity_group) 2052 ], 2053 extended_properties 2054 ) 2055 2056 @staticmethod 2057 def create_deployment_to_xml(name, 2058 package_url, 2059 label, 2060 configuration, 2061 start_deployment, 2062 treat_warnings_as_error, 2063 extended_properties): 2064 return AzureXmlSerializer.doc_from_data( 2065 'CreateDeployment', 2066 [ 2067 ('Name', name), 2068 ('PackageUrl', package_url), 2069 ('Label', label, AzureNodeDriver._encode_base64), 2070 ('Configuration', configuration), 2071 ('StartDeployment', start_deployment, _lower), 2072 ('TreatWarningsAsError', treat_warnings_as_error, _lower) 2073 ], 2074 extended_properties 2075 ) 2076 2077 @staticmethod 2078 def swap_deployment_to_xml(production, source_deployment): 2079 return AzureXmlSerializer.doc_from_data( 2080 'Swap', 2081 [ 2082 ('Production', production), 2083 ('SourceDeployment', source_deployment) 2084 ] 2085 ) 2086 2087 @staticmethod 2088 def update_deployment_status_to_xml(status): 2089 return AzureXmlSerializer.doc_from_data( 2090 'UpdateDeploymentStatus', 2091 [('Status', status)] 2092 ) 2093 2094 @staticmethod 2095 def change_deployment_to_xml(configuration, 2096 treat_warnings_as_error, 2097 mode, 2098 extended_properties): 2099 return AzureXmlSerializer.doc_from_data( 2100 'ChangeConfiguration', 2101 [ 2102 ('Configuration', configuration), 2103 ('TreatWarningsAsError', treat_warnings_as_error, _lower), 2104 ('Mode', mode) 2105 ], 2106 extended_properties 2107 ) 2108 2109 @staticmethod 2110 def upgrade_deployment_to_xml(mode, 2111 package_url, 2112 configuration, 2113 label, 2114 role_to_upgrade, 2115 force, 2116 extended_properties): 2117 return AzureXmlSerializer.doc_from_data( 2118 'UpgradeDeployment', 2119 [ 2120 ('Mode', mode), 2121 ('PackageUrl', package_url), 2122 ('Configuration', configuration), 2123 ('Label', label, AzureNodeDriver._encode_base64), 2124 ('RoleToUpgrade', role_to_upgrade), 2125 ('Force', force, _lower) 2126 ], 2127 extended_properties 2128 ) 2129 2130 @staticmethod 2131 def rollback_upgrade_to_xml(mode, force): 2132 return AzureXmlSerializer.doc_from_data( 2133 'RollbackUpdateOrUpgrade', 2134 [ 2135 ('Mode', mode), 2136 ('Force', force, _lower) 2137 ] 2138 ) 2139 2140 @staticmethod 2141 def walk_upgrade_domain_to_xml(upgrade_domain): 2142 return AzureXmlSerializer.doc_from_data( 2143 'WalkUpgradeDomain', 2144 [('UpgradeDomain', upgrade_domain)] 2145 ) 2146 2147 @staticmethod 2148 def certificate_file_to_xml(data, certificate_format, password): 2149 return AzureXmlSerializer.doc_from_data( 2150 'CertificateFile', 2151 [ 2152 ('Data', data), 2153 ('CertificateFormat', certificate_format), 2154 ('Password', password) 2155 ] 2156 ) 2157 2158 @staticmethod 2159 def create_affinity_group_to_xml(name, label, description, location): 2160 return AzureXmlSerializer.doc_from_data( 2161 'CreateAffinityGroup', 2162 [ 2163 ('Name', name), 2164 ('Label', label, AzureNodeDriver._encode_base64), 2165 ('Description', description), 2166 ('Location', location) 2167 ] 2168 ) 2169 2170 @staticmethod 2171 def update_affinity_group_to_xml(label, description): 2172 return AzureXmlSerializer.doc_from_data( 2173 'UpdateAffinityGroup', 2174 [ 2175 ('Label', label, AzureNodeDriver._encode_base64), 2176 ('Description', description) 2177 ] 2178 ) 2179 2180 @staticmethod 2181 def subscription_certificate_to_xml(public_key, thumbprint, data): 2182 return AzureXmlSerializer.doc_from_data( 2183 'SubscriptionCertificate', 2184 [ 2185 ('SubscriptionCertificatePublicKey', public_key), 2186 ('SubscriptionCertificateThumbprint', thumbprint), 2187 ('SubscriptionCertificateData', data) 2188 ] 2189 ) 2190 2191 @staticmethod 2192 def os_image_to_xml(label, media_link, name, os): 2193 return AzureXmlSerializer.doc_from_data( 2194 'OSImage', 2195 [ 2196 ('Label', label), 2197 ('MediaLink', media_link), 2198 ('Name', name), 2199 ('OS', os) 2200 ] 2201 ) 2202 2203 @staticmethod 2204 def data_virtual_hard_disk_to_xml(host_caching, 2205 disk_label, 2206 disk_name, 2207 lun, 2208 logical_disk_size_in_gb, 2209 media_link, 2210 source_media_link): 2211 return AzureXmlSerializer.doc_from_data( 2212 'DataVirtualHardDisk', 2213 [ 2214 ('HostCaching', host_caching), 2215 ('DiskLabel', disk_label), 2216 ('DiskName', disk_name), 2217 ('Lun', lun), 2218 ('LogicalDiskSizeInGB', logical_disk_size_in_gb), 2219 ('MediaLink', media_link), 2220 ('SourceMediaLink', source_media_link) 2221 ] 2222 ) 2223 2224 @staticmethod 2225 def disk_to_xml(has_operating_system, label, media_link, name, os): 2226 return AzureXmlSerializer.doc_from_data( 2227 'Disk', 2228 [ 2229 ('HasOperatingSystem', has_operating_system, _lower), 2230 ('Label', label), 2231 ('MediaLink', media_link), 2232 ('Name', name), 2233 ('OS', os) 2234 ] 2235 ) 2236 2237 @staticmethod 2238 def restart_role_operation_to_xml(): 2239 xml = ET.Element("OperationType") 2240 xml.text = "RestartRoleOperation" 2241 doc = AzureXmlSerializer.doc_from_xml( 2242 'RestartRoleOperation', 2243 xml 2244 ) 2245 result = ensure_string(ET.tostring(doc, encoding='utf-8')) 2246 return result 2247 2248 @staticmethod 2249 def shutdown_role_operation_to_xml(): 2250 xml = ET.Element("OperationType") 2251 xml.text = "ShutdownRoleOperation" 2252 doc = AzureXmlSerializer.doc_from_xml( 2253 'ShutdownRoleOperation', 2254 xml 2255 ) 2256 result = ensure_string(ET.tostring(doc, encoding='utf-8')) 2257 return result 2258 2259 @staticmethod 2260 def start_role_operation_to_xml(): 2261 xml = ET.Element("OperationType") 2262 xml.text = "StartRoleOperation" 2263 doc = AzureXmlSerializer.doc_from_xml( 2264 'StartRoleOperation', 2265 xml 2266 ) 2267 result = ensure_string(ET.tostring(doc, encoding='utf-8')) 2268 return result 2269 2270 @staticmethod 2271 def windows_configuration_to_xml(configuration, xml): 2272 AzureXmlSerializer.data_to_xml( 2273 [('ConfigurationSetType', configuration.configuration_set_type)], 2274 xml 2275 ) 2276 AzureXmlSerializer.data_to_xml( 2277 [('ComputerName', configuration.computer_name)], 2278 xml 2279 ) 2280 AzureXmlSerializer.data_to_xml( 2281 [('AdminPassword', configuration.admin_password)], 2282 xml 2283 ) 2284 AzureXmlSerializer.data_to_xml( 2285 [ 2286 ( 2287 'ResetPasswordOnFirstLogon', 2288 configuration.reset_password_on_first_logon, 2289 _lower 2290 ) 2291 ], 2292 xml 2293 ) 2294 2295 AzureXmlSerializer.data_to_xml( 2296 [ 2297 ( 2298 'EnableAutomaticUpdates', 2299 configuration.enable_automatic_updates, 2300 _lower 2301 ) 2302 ], 2303 xml 2304 ) 2305 2306 AzureXmlSerializer.data_to_xml( 2307 [('TimeZone', configuration.time_zone)], 2308 xml 2309 ) 2310 2311 if configuration.domain_join is not None: 2312 domain = ET.xml("DomainJoin") # pylint: disable=no-member 2313 creds = ET.xml("Credentials") # pylint: disable=no-member 2314 domain.appemnd(creds) 2315 xml.append(domain) 2316 2317 AzureXmlSerializer.data_to_xml( 2318 [('Domain', configuration.domain_join.credentials.domain)], 2319 creds 2320 ) 2321 2322 AzureXmlSerializer.data_to_xml( 2323 [ 2324 ( 2325 'Username', 2326 configuration.domain_join.credentials.username 2327 ) 2328 ], 2329 creds 2330 ) 2331 AzureXmlSerializer.data_to_xml( 2332 [ 2333 ( 2334 'Password', 2335 configuration.domain_join.credentials.password 2336 ) 2337 ], 2338 creds 2339 ) 2340 2341 AzureXmlSerializer.data_to_xml( 2342 [('JoinDomain', configuration.domain_join.join_domain)], 2343 domain 2344 ) 2345 2346 AzureXmlSerializer.data_to_xml( 2347 [ 2348 ( 2349 'MachineObjectOU', 2350 configuration.domain_join.machine_object_ou 2351 ) 2352 ], 2353 domain 2354 ) 2355 2356 if configuration.stored_certificate_settings is not None: 2357 cert_settings = ET.Element("StoredCertificateSettings") 2358 xml.append(cert_settings) 2359 for cert in configuration.stored_certificate_settings: 2360 cert_setting = ET.Element("CertificateSetting") 2361 cert_settings.append(cert_setting) 2362 2363 cert_setting.append(AzureXmlSerializer.data_to_xml( 2364 [('StoreLocation', cert.store_location)]) 2365 ) 2366 AzureXmlSerializer.data_to_xml( 2367 [('StoreName', cert.store_name)], 2368 cert_setting 2369 ) 2370 AzureXmlSerializer.data_to_xml( 2371 [('Thumbprint', cert.thumbprint)], 2372 cert_setting 2373 ) 2374 2375 AzureXmlSerializer.data_to_xml( 2376 [('AdminUsername', configuration.admin_user_name)], 2377 xml 2378 ) 2379 return xml 2380 2381 @staticmethod 2382 def linux_configuration_to_xml(configuration, xml): 2383 AzureXmlSerializer.data_to_xml( 2384 [('ConfigurationSetType', configuration.configuration_set_type)], 2385 xml 2386 ) 2387 AzureXmlSerializer.data_to_xml( 2388 [('HostName', configuration.host_name)], 2389 xml 2390 ) 2391 AzureXmlSerializer.data_to_xml( 2392 [('UserName', configuration.user_name)], 2393 xml 2394 ) 2395 AzureXmlSerializer.data_to_xml( 2396 [('UserPassword', configuration.user_password)], 2397 xml 2398 ) 2399 AzureXmlSerializer.data_to_xml( 2400 [ 2401 ( 2402 'DisableSshPasswordAuthentication', 2403 configuration.disable_ssh_password_authentication, 2404 _lower 2405 ) 2406 ], 2407 xml 2408 ) 2409 2410 if configuration.ssh is not None: 2411 ssh = ET.Element("SSH") 2412 pkeys = ET.Element("PublicKeys") 2413 kpairs = ET.Element("KeyPairs") 2414 ssh.append(pkeys) 2415 ssh.append(kpairs) 2416 xml.append(ssh) 2417 2418 for key in configuration.ssh.public_keys: 2419 pkey = ET.Element("PublicKey") 2420 pkeys.append(pkey) 2421 AzureXmlSerializer.data_to_xml( 2422 [('Fingerprint', key.fingerprint)], 2423 pkey 2424 ) 2425 AzureXmlSerializer.data_to_xml([('Path', key.path)], pkey) 2426 2427 for key in configuration.ssh.key_pairs: 2428 kpair = ET.Element("KeyPair") 2429 kpairs.append(kpair) 2430 AzureXmlSerializer.data_to_xml( 2431 [('Fingerprint', key.fingerprint)], 2432 kpair 2433 ) 2434 AzureXmlSerializer.data_to_xml([('Path', key.path)], kpair) 2435 2436 if configuration.custom_data is not None: 2437 AzureXmlSerializer.data_to_xml( 2438 [('CustomData', configuration.custom_data)], 2439 xml 2440 ) 2441 2442 return xml 2443 2444 @staticmethod 2445 def network_configuration_to_xml(configuration, xml): 2446 AzureXmlSerializer.data_to_xml( 2447 [('ConfigurationSetType', configuration.configuration_set_type)], 2448 xml 2449 ) 2450 2451 input_endpoints = ET.Element("InputEndpoints") 2452 xml.append(input_endpoints) 2453 2454 for endpoint in configuration.input_endpoints: 2455 input_endpoint = ET.Element("InputEndpoint") 2456 input_endpoints.append(input_endpoint) 2457 2458 AzureXmlSerializer.data_to_xml( 2459 [ 2460 ( 2461 'LoadBalancedEndpointSetName', 2462 endpoint.load_balanced_endpoint_set_name 2463 ) 2464 ], 2465 input_endpoint 2466 ) 2467 2468 AzureXmlSerializer.data_to_xml( 2469 [('LocalPort', endpoint.local_port)], 2470 input_endpoint 2471 ) 2472 AzureXmlSerializer.data_to_xml( 2473 [('Name', endpoint.name)], 2474 input_endpoint 2475 ) 2476 AzureXmlSerializer.data_to_xml( 2477 [('Port', endpoint.port)], 2478 input_endpoint 2479 ) 2480 2481 if (endpoint.load_balancer_probe.path or 2482 endpoint.load_balancer_probe.port or 2483 endpoint.load_balancer_probe.protocol): 2484 2485 load_balancer_probe = ET.Element("LoadBalancerProbe") 2486 input_endpoint.append(load_balancer_probe) 2487 AzureXmlSerializer.data_to_xml( 2488 [('Path', endpoint.load_balancer_probe.path)], 2489 load_balancer_probe 2490 ) 2491 AzureXmlSerializer.data_to_xml( 2492 [('Port', endpoint.load_balancer_probe.port)], 2493 load_balancer_probe 2494 ) 2495 AzureXmlSerializer.data_to_xml( 2496 [('Protocol', endpoint.load_balancer_probe.protocol)], 2497 load_balancer_probe 2498 ) 2499 2500 AzureXmlSerializer.data_to_xml( 2501 [('Protocol', endpoint.protocol)], 2502 input_endpoint 2503 ) 2504 AzureXmlSerializer.data_to_xml( 2505 [ 2506 ( 2507 'EnableDirectServerReturn', 2508 endpoint.enable_direct_server_return, 2509 _lower 2510 ) 2511 ], 2512 input_endpoint 2513 ) 2514 2515 subnet_names = ET.Element("SubnetNames") 2516 xml.append(subnet_names) 2517 for name in configuration.subnet_names: 2518 AzureXmlSerializer.data_to_xml( 2519 [('SubnetName', name)], 2520 subnet_names 2521 ) 2522 2523 return xml 2524 2525 @staticmethod 2526 def role_to_xml(availability_set_name, 2527 data_virtual_hard_disks, 2528 network_configuration_set, 2529 os_virtual_hard_disk, 2530 vm_image_name, 2531 role_name, 2532 role_size, 2533 role_type, 2534 system_configuration_set, 2535 xml): 2536 2537 AzureXmlSerializer.data_to_xml([('RoleName', role_name)], xml) 2538 AzureXmlSerializer.data_to_xml([('RoleType', role_type)], xml) 2539 2540 config_sets = ET.Element("ConfigurationSets") 2541 xml.append(config_sets) 2542 2543 if system_configuration_set is not None: 2544 config_set = ET.Element("ConfigurationSet") 2545 config_sets.append(config_set) 2546 2547 if isinstance(system_configuration_set, WindowsConfigurationSet): 2548 AzureXmlSerializer.windows_configuration_to_xml( 2549 system_configuration_set, 2550 config_set 2551 ) 2552 elif isinstance(system_configuration_set, LinuxConfigurationSet): 2553 AzureXmlSerializer.linux_configuration_to_xml( 2554 system_configuration_set, 2555 config_set 2556 ) 2557 2558 if network_configuration_set is not None: 2559 config_set = ET.Element("ConfigurationSet") 2560 config_sets.append(config_set) 2561 2562 AzureXmlSerializer.network_configuration_to_xml( 2563 network_configuration_set, 2564 config_set 2565 ) 2566 2567 if availability_set_name is not None: 2568 AzureXmlSerializer.data_to_xml( 2569 [('AvailabilitySetName', availability_set_name)], 2570 xml 2571 ) 2572 2573 if data_virtual_hard_disks is not None: 2574 vhds = ET.Element("DataVirtualHardDisks") 2575 xml.append(vhds) 2576 2577 for hd in data_virtual_hard_disks: 2578 vhd = ET.Element("DataVirtualHardDisk") 2579 vhds.append(vhd) 2580 AzureXmlSerializer.data_to_xml( 2581 [('HostCaching', hd.host_caching)], 2582 vhd 2583 ) 2584 AzureXmlSerializer.data_to_xml( 2585 [('DiskLabel', hd.disk_label)], 2586 vhd 2587 ) 2588 AzureXmlSerializer.data_to_xml( 2589 [('DiskName', hd.disk_name)], 2590 vhd 2591 ) 2592 AzureXmlSerializer.data_to_xml( 2593 [('Lun', hd.lun)], 2594 vhd 2595 ) 2596 AzureXmlSerializer.data_to_xml( 2597 [('LogicalDiskSizeInGB', hd.logical_disk_size_in_gb)], 2598 vhd 2599 ) 2600 AzureXmlSerializer.data_to_xml( 2601 [('MediaLink', hd.media_link)], 2602 vhd 2603 ) 2604 2605 if os_virtual_hard_disk is not None: 2606 hd = ET.Element("OSVirtualHardDisk") 2607 xml.append(hd) 2608 AzureXmlSerializer.data_to_xml( 2609 [('HostCaching', os_virtual_hard_disk.host_caching)], 2610 hd 2611 ) 2612 AzureXmlSerializer.data_to_xml( 2613 [('DiskLabel', os_virtual_hard_disk.disk_label)], 2614 hd 2615 ) 2616 AzureXmlSerializer.data_to_xml( 2617 [('DiskName', os_virtual_hard_disk.disk_name)], 2618 hd 2619 ) 2620 AzureXmlSerializer.data_to_xml( 2621 [('MediaLink', os_virtual_hard_disk.media_link)], 2622 hd 2623 ) 2624 AzureXmlSerializer.data_to_xml( 2625 [('SourceImageName', os_virtual_hard_disk.source_image_name)], 2626 hd 2627 ) 2628 2629 if vm_image_name is not None: 2630 AzureXmlSerializer.data_to_xml( 2631 [('VMImageName', vm_image_name)], 2632 xml 2633 ) 2634 2635 if role_size is not None: 2636 AzureXmlSerializer.data_to_xml([('RoleSize', role_size)], xml) 2637 2638 return xml 2639 2640 @staticmethod 2641 def add_role_to_xml(role_name, 2642 system_configuration_set, 2643 os_virtual_hard_disk, 2644 role_type, 2645 network_configuration_set, 2646 availability_set_name, 2647 data_virtual_hard_disks, 2648 vm_image_name, 2649 role_size): 2650 doc = AzureXmlSerializer.doc_from_xml('PersistentVMRole') 2651 xml = AzureXmlSerializer.role_to_xml( 2652 availability_set_name, 2653 data_virtual_hard_disks, 2654 network_configuration_set, 2655 os_virtual_hard_disk, 2656 vm_image_name, 2657 role_name, 2658 role_size, 2659 role_type, 2660 system_configuration_set, 2661 doc 2662 ) 2663 result = ensure_string(ET.tostring(xml, encoding='utf-8')) 2664 return result 2665 2666 @staticmethod 2667 def update_role_to_xml(role_name, 2668 os_virtual_hard_disk, 2669 role_type, 2670 network_configuration_set, 2671 availability_set_name, 2672 data_virtual_hard_disks, 2673 vm_image_name, 2674 role_size): 2675 2676 doc = AzureXmlSerializer.doc_from_xml('PersistentVMRole') 2677 AzureXmlSerializer.role_to_xml( 2678 availability_set_name, 2679 data_virtual_hard_disks, 2680 network_configuration_set, 2681 os_virtual_hard_disk, 2682 vm_image_name, 2683 role_name, 2684 role_size, 2685 role_type, 2686 None, 2687 doc 2688 ) 2689 2690 result = ensure_string(ET.tostring(doc, encoding='utf-8')) 2691 return result 2692 2693 @staticmethod 2694 def capture_role_to_xml(post_capture_action, 2695 target_image_name, 2696 target_image_label, 2697 provisioning_configuration): 2698 xml = AzureXmlSerializer.data_to_xml( 2699 [('OperationType', 'CaptureRoleOperation')] 2700 ) 2701 AzureXmlSerializer.data_to_xml( 2702 [('PostCaptureAction', post_capture_action)], 2703 xml 2704 ) 2705 2706 if provisioning_configuration is not None: 2707 provisioning_config = ET.Element("ProvisioningConfiguration") 2708 xml.append(provisioning_config) 2709 2710 if isinstance(provisioning_configuration, WindowsConfigurationSet): 2711 AzureXmlSerializer.windows_configuration_to_xml( 2712 provisioning_configuration, 2713 provisioning_config 2714 ) 2715 elif isinstance(provisioning_configuration, LinuxConfigurationSet): 2716 AzureXmlSerializer.linux_configuration_to_xml( 2717 provisioning_configuration, 2718 provisioning_config 2719 ) 2720 2721 AzureXmlSerializer.data_to_xml( 2722 [('TargetImageLabel', target_image_label)], 2723 xml 2724 ) 2725 AzureXmlSerializer.data_to_xml( 2726 [('TargetImageName', target_image_name)], 2727 xml 2728 ) 2729 doc = AzureXmlSerializer.doc_from_xml('CaptureRoleOperation', xml) 2730 result = ensure_string(ET.tostring(doc, encoding='utf-8')) 2731 return result 2732 2733 @staticmethod 2734 def virtual_machine_deployment_to_xml(deployment_name, 2735 deployment_slot, 2736 label, 2737 role_name, 2738 system_configuration_set, 2739 os_virtual_hard_disk, 2740 role_type, 2741 network_configuration_set, 2742 availability_set_name, 2743 data_virtual_hard_disks, 2744 role_size, 2745 virtual_network_name, 2746 vm_image_name): 2747 2748 doc = AzureXmlSerializer.doc_from_xml('Deployment') 2749 AzureXmlSerializer.data_to_xml([('Name', deployment_name)], doc) 2750 AzureXmlSerializer.data_to_xml( 2751 [('DeploymentSlot', deployment_slot)], 2752 doc 2753 ) 2754 AzureXmlSerializer.data_to_xml([('Label', label)], doc) 2755 2756 role_list = ET.Element("RoleList") 2757 role = ET.Element("Role") 2758 role_list.append(role) 2759 doc.append(role_list) 2760 2761 AzureXmlSerializer.role_to_xml( 2762 availability_set_name, 2763 data_virtual_hard_disks, 2764 network_configuration_set, 2765 os_virtual_hard_disk, 2766 vm_image_name, 2767 role_name, 2768 role_size, 2769 role_type, 2770 system_configuration_set, 2771 role 2772 ) 2773 2774 if virtual_network_name is not None: 2775 doc.append( 2776 AzureXmlSerializer.data_to_xml( 2777 [('VirtualNetworkName', virtual_network_name)] 2778 ) 2779 ) 2780 2781 result = ensure_string(ET.tostring(doc, encoding='utf-8')) 2782 return result 2783 2784 @staticmethod 2785 def data_to_xml(data, xml=None): 2786 """ 2787 Creates an xml fragment from the specified data. 2788 data: Array of tuples, where first: xml element name 2789 second: xml element text 2790 third: conversion function 2791 """ 2792 2793 for element in data: 2794 name = element[0] 2795 val = element[1] 2796 if len(element) > 2: 2797 converter = element[2] 2798 else: 2799 converter = None 2800 2801 if val is not None: 2802 if converter is not None: 2803 text = _str(converter(_str(val))) 2804 else: 2805 text = _str(val) 2806 2807 entry = ET.Element(name) 2808 entry.text = text 2809 if xml is not None: 2810 xml.append(entry) 2811 else: 2812 return entry 2813 return xml 2814 2815 @staticmethod 2816 def doc_from_xml(document_element_name, inner_xml=None): 2817 """ 2818 Wraps the specified xml in an xml root element with default azure 2819 namespaces 2820 """ 2821 # Note: Namespaces don't work consistency in Python 2 and 3. 2822 """ 2823 nsmap = { 2824 None: "http://www.w3.org/2001/XMLSchema-instance", 2825 "i": "http://www.w3.org/2001/XMLSchema-instance" 2826 } 2827 2828 xml.attrib["xmlns:i"] = "http://www.w3.org/2001/XMLSchema-instance" 2829 xml.attrib["xmlns"] = "http://schemas.microsoft.com/windowsazure" 2830 """ 2831 xml = ET.Element(document_element_name) 2832 xml.set("xmlns", "http://schemas.microsoft.com/windowsazure") 2833 2834 if inner_xml is not None: 2835 xml.append(inner_xml) 2836 2837 return xml 2838 2839 @staticmethod 2840 def doc_from_data(document_element_name, data, extended_properties=None): 2841 doc = AzureXmlSerializer.doc_from_xml(document_element_name) 2842 AzureXmlSerializer.data_to_xml(data, doc) 2843 if extended_properties is not None: 2844 doc.append( 2845 AzureXmlSerializer.extended_properties_dict_to_xml_fragment( 2846 extended_properties 2847 ) 2848 ) 2849 2850 result = ensure_string(ET.tostring(doc, encoding='utf-8')) 2851 return result 2852 2853 @staticmethod 2854 def extended_properties_dict_to_xml_fragment(extended_properties): 2855 2856 if extended_properties is not None and len(extended_properties) > 0: 2857 xml = ET.Element("ExtendedProperties") 2858 for key, val in extended_properties.items(): 2859 extended_property = ET.Element("ExtendedProperty") 2860 name = ET.Element("Name") 2861 name.text = _str(key) 2862 value = ET.Element("Value") 2863 value.text = _str(val) 2864 2865 extended_property.append(name) 2866 extended_property.append(value) 2867 xml.append(extended_property) 2868 2869 return xml 2870 2871 2872""" 2873Data Classes 2874 2875Borrowed from the Azure SDK for Python. 2876""" 2877 2878 2879class WindowsAzureData(object): 2880 2881 """ 2882 This is the base of data class. 2883 It is only used to check whether it is instance or not. 2884 """ 2885 pass 2886 2887 2888class WindowsAzureDataTypedList(WindowsAzureData): 2889 2890 list_type = None 2891 xml_element_name = None 2892 2893 def __init__(self): 2894 self.items = _ListOf(self.list_type, self.xml_element_name) 2895 2896 def __iter__(self): 2897 return iter(self.items) 2898 2899 def __len__(self): 2900 return len(self.items) 2901 2902 def __getitem__(self, index): 2903 return self.items[index] 2904 2905 2906class OSVirtualHardDisk(WindowsAzureData): 2907 2908 def __init__(self, source_image_name=None, media_link=None, 2909 host_caching=None, disk_label=None, disk_name=None): 2910 self.source_image_name = source_image_name 2911 self.media_link = media_link 2912 self.host_caching = host_caching 2913 self.disk_label = disk_label 2914 self.disk_name = disk_name 2915 self.os = '' # undocumented, not used when adding a role 2916 2917 2918class LinuxConfigurationSet(WindowsAzureData): 2919 2920 def __init__(self, 2921 host_name=None, 2922 user_name=None, 2923 user_password=None, 2924 disable_ssh_password_authentication=None, 2925 custom_data=None): 2926 self.configuration_set_type = 'LinuxProvisioningConfiguration' 2927 self.host_name = host_name 2928 self.user_name = user_name 2929 self.user_password = user_password 2930 self.disable_ssh_password_authentication = \ 2931 disable_ssh_password_authentication 2932 self.ssh = SSH() 2933 self.custom_data = custom_data 2934 2935 2936class WindowsConfigurationSet(WindowsAzureData): 2937 2938 def __init__(self, 2939 computer_name=None, 2940 admin_password=None, 2941 reset_password_on_first_logon=None, 2942 enable_automatic_updates=None, 2943 time_zone=None, 2944 admin_user_name=None): 2945 self.configuration_set_type = 'WindowsProvisioningConfiguration' 2946 self.computer_name = computer_name 2947 self.admin_password = admin_password 2948 self.reset_password_on_first_logon = reset_password_on_first_logon 2949 self.enable_automatic_updates = enable_automatic_updates 2950 self.time_zone = time_zone 2951 self.admin_user_name = admin_user_name 2952 self.domain_join = DomainJoin() 2953 self.stored_certificate_settings = StoredCertificateSettings() 2954 2955 2956class DomainJoin(WindowsAzureData): 2957 2958 def __init__(self): 2959 self.credentials = Credentials() 2960 self.join_domain = '' 2961 self.machine_object_ou = '' 2962 2963 2964class Credentials(WindowsAzureData): 2965 2966 def __init__(self): 2967 self.domain = '' 2968 self.username = '' 2969 self.password = '' 2970 2971 2972class CertificateSetting(WindowsAzureData): 2973 2974 """ 2975 Initializes a certificate setting. 2976 2977 thumbprint: 2978 Specifies the thumbprint of the certificate to be provisioned. The 2979 thumbprint must specify an existing service certificate. 2980 store_name: 2981 Specifies the name of the certificate store from which retrieve 2982 certificate. 2983 store_location: 2984 Specifies the target certificate store location on the virtual machine 2985 The only supported value is LocalMachine. 2986 """ 2987 2988 def __init__(self, thumbprint='', store_name='', store_location=''): 2989 self.thumbprint = thumbprint 2990 self.store_name = store_name 2991 self.store_location = store_location 2992 2993 2994class StoredCertificateSettings(WindowsAzureDataTypedList): 2995 list_type = CertificateSetting 2996 2997 _repr_attributes = [ 2998 'items' 2999 ] 3000 3001 3002class SSH(WindowsAzureData): 3003 3004 def __init__(self): 3005 self.public_keys = PublicKeys() 3006 self.key_pairs = KeyPairs() 3007 3008 3009class PublicKey(WindowsAzureData): 3010 3011 def __init__(self, fingerprint='', path=''): 3012 self.fingerprint = fingerprint 3013 self.path = path 3014 3015 3016class PublicKeys(WindowsAzureDataTypedList): 3017 list_type = PublicKey 3018 3019 _repr_attributes = [ 3020 'items' 3021 ] 3022 3023 3024class AzureKeyPair(WindowsAzureData): 3025 3026 def __init__(self, fingerprint='', path=''): 3027 self.fingerprint = fingerprint 3028 self.path = path 3029 3030 3031class KeyPairs(WindowsAzureDataTypedList): 3032 list_type = AzureKeyPair 3033 3034 _repr_attributes = [ 3035 'items' 3036 ] 3037 3038 3039class LoadBalancerProbe(WindowsAzureData): 3040 3041 def __init__(self): 3042 self.path = '' 3043 self.port = '' 3044 self.protocol = '' 3045 3046 3047class ConfigurationSet(WindowsAzureData): 3048 3049 def __init__(self): 3050 self.configuration_set_type = '' 3051 self.role_type = '' 3052 self.input_endpoints = ConfigurationSetInputEndpoints() 3053 self.subnet_names = ScalarListOf(str, 'SubnetName') 3054 3055 3056class ConfigurationSets(WindowsAzureDataTypedList): 3057 list_type = ConfigurationSet 3058 3059 _repr_attributes = [ 3060 'items' 3061 ] 3062 3063 3064class ConfigurationSetInputEndpoint(WindowsAzureData): 3065 3066 def __init__(self, 3067 name='', 3068 protocol='', 3069 port='', 3070 local_port='', 3071 load_balanced_endpoint_set_name='', 3072 enable_direct_server_return=False): 3073 self.enable_direct_server_return = enable_direct_server_return 3074 self.load_balanced_endpoint_set_name = load_balanced_endpoint_set_name 3075 self.local_port = local_port 3076 self.name = name 3077 self.port = port 3078 self.load_balancer_probe = LoadBalancerProbe() 3079 self.protocol = protocol 3080 3081 3082class ConfigurationSetInputEndpoints(WindowsAzureDataTypedList): 3083 list_type = ConfigurationSetInputEndpoint 3084 xml_element_name = 'InputEndpoint' 3085 3086 _repr_attributes = [ 3087 'items' 3088 ] 3089 3090 3091class Location(WindowsAzureData): 3092 3093 def __init__(self): 3094 self.name = '' 3095 self.display_name = '' 3096 self.available_services = ScalarListOf(str, 'AvailableService') 3097 self.compute_capabilities = ComputeCapability() 3098 3099 3100class Locations(WindowsAzureDataTypedList): 3101 list_type = Location 3102 3103 _repr_attributes = [ 3104 'items' 3105 ] 3106 3107 3108class ComputeCapability(WindowsAzureData): 3109 3110 def __init__(self): 3111 self.virtual_machines_role_sizes = ScalarListOf(str, 'RoleSize') 3112 3113 3114class VirtualMachinesRoleSizes(WindowsAzureData): 3115 3116 def __init__(self): 3117 self.role_size = ScalarListOf(str, 'RoleSize') 3118 3119 3120class OSImage(WindowsAzureData): 3121 3122 def __init__(self): 3123 self.affinity_group = '' 3124 self.category = '' 3125 self.location = '' 3126 self.logical_size_in_gb = 0 3127 self.label = '' 3128 self.media_link = '' 3129 self.name = '' 3130 self.os = '' 3131 self.eula = '' 3132 self.description = '' 3133 3134 3135class Images(WindowsAzureDataTypedList): 3136 list_type = OSImage 3137 3138 _repr_attributes = [ 3139 'items' 3140 ] 3141 3142 3143class VMImage(WindowsAzureData): 3144 3145 def __init__(self): 3146 self.name = '' 3147 self.label = '' 3148 self.category = '' 3149 self.os_disk_configuration = OSDiskConfiguration() 3150 self.service_name = '' 3151 self.deployment_name = '' 3152 self.role_name = '' 3153 self.location = '' 3154 self.affinity_group = '' 3155 3156 3157class VMImages(WindowsAzureDataTypedList): 3158 list_type = VMImage 3159 3160 _repr_attributes = [ 3161 'items' 3162 ] 3163 3164 3165class VirtualIP(WindowsAzureData): 3166 3167 def __init__(self): 3168 self.address = '' 3169 self.is_dns_programmed = '' 3170 self.name = '' 3171 3172 3173class VirtualIPs(WindowsAzureDataTypedList): 3174 list_type = VirtualIP 3175 3176 _repr_attributes = [ 3177 'items' 3178 ] 3179 3180 3181class HostedService(WindowsAzureData, ReprMixin): 3182 _repr_attributes = [ 3183 'service_name', 3184 'url' 3185 ] 3186 3187 def __init__(self): 3188 self.url = '' 3189 self.service_name = '' 3190 self.hosted_service_properties = HostedServiceProperties() 3191 self.deployments = Deployments() 3192 3193 3194class HostedServices(WindowsAzureDataTypedList, ReprMixin): 3195 list_type = HostedService 3196 3197 _repr_attributes = [ 3198 'items' 3199 ] 3200 3201 3202class HostedServiceProperties(WindowsAzureData): 3203 3204 def __init__(self): 3205 self.description = '' 3206 self.location = '' 3207 self.affinity_group = '' 3208 self.label = _Base64String() 3209 self.status = '' 3210 self.date_created = '' 3211 self.date_last_modified = '' 3212 self.extended_properties = _DictOf( 3213 'ExtendedProperty', 3214 'Name', 3215 'Value' 3216 ) 3217 3218 3219class Deployment(WindowsAzureData): 3220 3221 def __init__(self): 3222 self.name = '' 3223 self.deployment_slot = '' 3224 self.private_id = '' 3225 self.status = '' 3226 self.label = _Base64String() 3227 self.url = '' 3228 self.configuration = _Base64String() 3229 self.role_instance_list = RoleInstanceList() 3230 self.upgrade_status = UpgradeStatus() 3231 self.upgrade_domain_count = '' 3232 self.role_list = RoleList() 3233 self.sdk_version = '' 3234 self.input_endpoint_list = InputEndpoints() 3235 self.locked = False 3236 self.rollback_allowed = False 3237 self.persistent_vm_downtime_info = PersistentVMDowntimeInfo() 3238 self.created_time = '' 3239 self.last_modified_time = '' 3240 self.extended_properties = _DictOf( 3241 'ExtendedProperty', 3242 'Name', 3243 'Value' 3244 ) 3245 self.virtual_ips = VirtualIPs() 3246 3247 3248class Deployments(WindowsAzureDataTypedList): 3249 list_type = Deployment 3250 3251 _repr_attributes = [ 3252 'items' 3253 ] 3254 3255 3256class UpgradeStatus(WindowsAzureData): 3257 3258 def __init__(self): 3259 self.upgrade_type = '' 3260 self.current_upgrade_domain_state = '' 3261 self.current_upgrade_domain = '' 3262 3263 3264class RoleInstance(WindowsAzureData): 3265 3266 def __init__(self): 3267 self.role_name = '' 3268 self.instance_name = '' 3269 self.instance_status = '' 3270 self.instance_upgrade_domain = 0 3271 self.instance_fault_domain = 0 3272 self.instance_size = '' 3273 self.instance_state_details = '' 3274 self.instance_error_code = '' 3275 self.ip_address = '' 3276 self.instance_endpoints = InstanceEndpoints() 3277 self.power_state = '' 3278 self.fqdn = '' 3279 self.host_name = '' 3280 3281 3282class RoleInstanceList(WindowsAzureDataTypedList): 3283 list_type = RoleInstance 3284 3285 _repr_attributes = [ 3286 'items' 3287 ] 3288 3289 3290class InstanceEndpoint(WindowsAzureData): 3291 3292 def __init__(self): 3293 self.name = '' 3294 self.vip = '' 3295 self.public_port = '' 3296 self.local_port = '' 3297 self.protocol = '' 3298 3299 3300class InstanceEndpoints(WindowsAzureDataTypedList): 3301 list_type = InstanceEndpoint 3302 3303 _repr_attributes = [ 3304 'items' 3305 ] 3306 3307 3308class InputEndpoint(WindowsAzureData): 3309 3310 def __init__(self): 3311 self.role_name = '' 3312 self.vip = '' 3313 self.port = '' 3314 3315 3316class InputEndpoints(WindowsAzureDataTypedList): 3317 list_type = InputEndpoint 3318 3319 _repr_attributes = [ 3320 'items' 3321 ] 3322 3323 3324class Role(WindowsAzureData): 3325 3326 def __init__(self): 3327 self.role_name = '' 3328 self.os_version = '' 3329 3330 3331class RoleList(WindowsAzureDataTypedList): 3332 list_type = Role 3333 3334 _repr_attributes = [ 3335 'items' 3336 ] 3337 3338 3339class PersistentVMDowntimeInfo(WindowsAzureData): 3340 3341 def __init__(self): 3342 self.start_time = '' 3343 self.end_time = '' 3344 self.status = '' 3345 3346 3347class AsynchronousOperationResult(WindowsAzureData): 3348 3349 def __init__(self, request_id=None): 3350 self.request_id = request_id 3351 3352 3353class Disk(WindowsAzureData): 3354 3355 def __init__(self): 3356 self.affinity_group = '' 3357 self.attached_to = AttachedTo() 3358 self.has_operating_system = '' 3359 self.is_corrupted = '' 3360 self.location = '' 3361 self.logical_disk_size_in_gb = 0 3362 self.label = '' 3363 self.media_link = '' 3364 self.name = '' 3365 self.os = '' 3366 self.source_image_name = '' 3367 3368 3369class Disks(WindowsAzureDataTypedList): 3370 list_type = Disk 3371 3372 _repr_attributes = [ 3373 'items' 3374 ] 3375 3376 3377class AttachedTo(WindowsAzureData): 3378 3379 def __init__(self): 3380 self.hosted_service_name = '' 3381 self.deployment_name = '' 3382 self.role_name = '' 3383 3384 3385class OperationError(WindowsAzureData): 3386 3387 def __init__(self): 3388 self.code = '' 3389 self.message = '' 3390 3391 3392class Operation(WindowsAzureData): 3393 3394 def __init__(self): 3395 self.id = '' 3396 self.status = '' 3397 self.http_status_code = '' 3398 self.error = OperationError() 3399 3400 3401class OperatingSystem(WindowsAzureData): 3402 3403 def __init__(self): 3404 self.version = '' 3405 self.label = _Base64String() 3406 self.is_default = True 3407 self.is_active = True 3408 self.family = 0 3409 self.family_label = _Base64String() 3410 3411 3412class OSDiskConfiguration(WindowsAzureData): 3413 3414 def __init__(self): 3415 self.name = '' 3416 self.host_caching = '' 3417 self.os_state = '' 3418 self.os = '' 3419 self.media_link = '' 3420 self.logical_disk_size_in_gb = 0 3421 3422 3423class OperatingSystems(WindowsAzureDataTypedList): 3424 list_type = OperatingSystem 3425 3426 _repr_attributes = [ 3427 'items' 3428 ] 3429 3430 3431class OperatingSystemFamily(WindowsAzureData): 3432 3433 def __init__(self): 3434 self.name = '' 3435 self.label = _Base64String() 3436 self.operating_systems = OperatingSystems() 3437 3438 3439class OperatingSystemFamilies(WindowsAzureDataTypedList): 3440 list_type = OperatingSystemFamily 3441 3442 _repr_attributes = [ 3443 'items' 3444 ] 3445 3446 3447class Subscription(WindowsAzureData): 3448 3449 def __init__(self): 3450 self.subscription_id = '' 3451 self.subscription_name = '' 3452 self.subscription_status = '' 3453 self.account_admin_live_email_id = '' 3454 self.service_admin_live_email_id = '' 3455 self.max_core_count = 0 3456 self.max_storage_accounts = 0 3457 self.max_hosted_services = 0 3458 self.current_core_count = 0 3459 self.current_hosted_services = 0 3460 self.current_storage_accounts = 0 3461 self.max_virtual_network_sites = 0 3462 self.max_local_network_sites = 0 3463 self.max_dns_servers = 0 3464 3465 3466class AvailabilityResponse(WindowsAzureData): 3467 3468 def __init__(self): 3469 self.result = False 3470 3471 3472class SubscriptionCertificate(WindowsAzureData): 3473 3474 def __init__(self): 3475 self.subscription_certificate_public_key = '' 3476 self.subscription_certificate_thumbprint = '' 3477 self.subscription_certificate_data = '' 3478 self.created = '' 3479 3480 3481class SubscriptionCertificates(WindowsAzureDataTypedList): 3482 list_type = SubscriptionCertificate 3483 3484 _repr_attributes = [ 3485 'items' 3486 ] 3487 3488 3489class AzureHTTPRequest(object): 3490 def __init__(self): 3491 self.host = '' 3492 self.method = '' 3493 self.path = '' 3494 self.query = [] # list of (name, value) 3495 self.headers = {} # list of (header name, header value) 3496 self.body = '' 3497 self.protocol_override = None 3498 3499 3500class AzureHTTPResponse(object): 3501 def __init__(self, status, message, headers, body): 3502 self.status = status 3503 self.message = message 3504 self.headers = headers 3505 self.body = body 3506 3507 3508""" 3509Helper classes and functions. 3510""" 3511 3512 3513class _Base64String(str): 3514 pass 3515 3516 3517class _ListOf(list): 3518 3519 """ 3520 A list which carries with it the type that's expected to go in it. 3521 Used for deserializaion and construction of the lists 3522 """ 3523 3524 def __init__(self, list_type, xml_element_name=None): 3525 self.list_type = list_type 3526 if xml_element_name is None: 3527 self.xml_element_name = list_type.__name__ 3528 else: 3529 self.xml_element_name = xml_element_name 3530 super(_ListOf, self).__init__() 3531 3532 3533class ScalarListOf(list): 3534 3535 """ 3536 A list of scalar types which carries with it the type that's 3537 expected to go in it along with its xml element name. 3538 Used for deserializaion and construction of the lists 3539 """ 3540 3541 def __init__(self, list_type, xml_element_name): 3542 self.list_type = list_type 3543 self.xml_element_name = xml_element_name 3544 super(ScalarListOf, self).__init__() 3545 3546 3547class _DictOf(dict): 3548 3549 """ 3550 A dict which carries with it the xml element names for key,val. 3551 Used for deserializaion and construction of the lists 3552 """ 3553 3554 def __init__(self, 3555 pair_xml_element_name, 3556 key_xml_element_name, 3557 value_xml_element_name): 3558 self.pair_xml_element_name = pair_xml_element_name 3559 self.key_xml_element_name = key_xml_element_name 3560 self.value_xml_element_name = value_xml_element_name 3561 super(_DictOf, self).__init__() 3562 3563 3564class AzureNodeLocation(NodeLocation): 3565 # we can also have something in here for available services which is an 3566 # extra to the API with Azure 3567 3568 def __init__(self, id, name, country, driver, available_services, 3569 virtual_machine_role_sizes): 3570 super(AzureNodeLocation, self).__init__(id, name, country, driver) 3571 self.available_services = available_services 3572 self.virtual_machine_role_sizes = virtual_machine_role_sizes 3573 3574 def __repr__(self): 3575 return ( 3576 ( 3577 '<AzureNodeLocation: id=%s, name=%s, country=%s, ' 3578 'driver=%s services=%s virtualMachineRoleSizes=%s >' 3579 ) % ( 3580 self.id, 3581 self.name, 3582 self.country, 3583 self.driver.name, 3584 ','.join(self.available_services), 3585 ','.join(self.virtual_machine_role_sizes) 3586 ) 3587 ) 3588