1""" 2Azure Cloud Module 3================== 4 5The Azure cloud module is used to control access to Microsoft Azure 6 7:depends: 8 * `Microsoft Azure SDK for Python <https://pypi.python.org/pypi/azure/1.0.2>`_ >= 1.0.2 9 * python-requests, for Python < 2.7.9 10:configuration: 11 Required provider parameters: 12 13 * ``apikey`` 14 * ``certificate_path`` 15 * ``subscription_id`` 16 * ``backend`` 17 18 A Management Certificate (.pem and .crt files) must be created and the .pem 19 file placed on the same machine that salt-cloud is run from. Information on 20 creating the pem file to use, and uploading the associated cer file can be 21 found at: 22 23 http://www.windowsazure.com/en-us/develop/python/how-to-guides/service-management/ 24 25 For users with Python < 2.7.9, ``backend`` must currently be set to ``requests``. 26 27Example ``/etc/salt/cloud.providers`` or 28``/etc/salt/cloud.providers.d/azure.conf`` configuration: 29 30.. code-block:: yaml 31 32 my-azure-config: 33 driver: azure 34 subscription_id: 3287abc8-f98a-c678-3bde-326766fd3617 35 certificate_path: /etc/salt/azure.pem 36 management_host: management.core.windows.net 37""" 38# pylint: disable=function-redefined 39 40import copy 41import logging 42import pprint 43import time 44 45import salt.config as config 46import salt.utils.cloud 47import salt.utils.stringutils 48import salt.utils.yaml 49from salt.exceptions import SaltCloudSystemExit 50 51HAS_LIBS = False 52try: 53 import azure 54 import azure.storage 55 import azure.servicemanagement 56 from azure.common import ( 57 AzureConflictHttpError, 58 AzureMissingResourceHttpError, 59 AzureException, 60 ) 61 import salt.utils.msazure 62 from salt.utils.msazure import object_to_dict 63 64 HAS_LIBS = True 65except ImportError: 66 pass 67 68__virtualname__ = "azure" 69 70 71# Get logging started 72log = logging.getLogger(__name__) 73 74 75# Only load in this module if the AZURE configurations are in place 76def __virtual__(): 77 """ 78 Check for Azure configurations. 79 """ 80 if get_configured_provider() is False: 81 return False 82 83 if get_dependencies() is False: 84 return False 85 86 return __virtualname__ 87 88 89def _get_active_provider_name(): 90 try: 91 return __active_provider_name__.value() 92 except AttributeError: 93 return __active_provider_name__ 94 95 96def get_configured_provider(): 97 """ 98 Return the first configured instance. 99 """ 100 return config.is_provider_configured( 101 __opts__, 102 _get_active_provider_name() or __virtualname__, 103 ("subscription_id", "certificate_path"), 104 ) 105 106 107def get_dependencies(): 108 """ 109 Warn if dependencies aren't met. 110 """ 111 return config.check_driver_dependencies(__virtualname__, {"azure": HAS_LIBS}) 112 113 114def get_conn(): 115 """ 116 Return a conn object for the passed VM data 117 """ 118 certificate_path = config.get_cloud_config_value( 119 "certificate_path", get_configured_provider(), __opts__, search_global=False 120 ) 121 subscription_id = salt.utils.stringutils.to_str( 122 config.get_cloud_config_value( 123 "subscription_id", get_configured_provider(), __opts__, search_global=False 124 ) 125 ) 126 management_host = config.get_cloud_config_value( 127 "management_host", 128 get_configured_provider(), 129 __opts__, 130 search_global=False, 131 default="management.core.windows.net", 132 ) 133 return azure.servicemanagement.ServiceManagementService( 134 subscription_id, certificate_path, management_host 135 ) 136 137 138def script(vm_): 139 """ 140 Return the script deployment object 141 """ 142 return salt.utils.cloud.os_script( 143 config.get_cloud_config_value("script", vm_, __opts__), 144 vm_, 145 __opts__, 146 salt.utils.cloud.salt_config_to_yaml( 147 salt.utils.cloud.minion_config(__opts__, vm_) 148 ), 149 ) 150 151 152def avail_locations(conn=None, call=None): 153 """ 154 List available locations for Azure 155 """ 156 if call == "action": 157 raise SaltCloudSystemExit( 158 "The avail_locations function must be called with " 159 "-f or --function, or with the --list-locations option" 160 ) 161 162 if not conn: 163 conn = get_conn() 164 165 ret = {} 166 locations = conn.list_locations() 167 for location in locations: 168 ret[location.name] = { 169 "name": location.name, 170 "display_name": location.display_name, 171 "available_services": location.available_services, 172 } 173 return ret 174 175 176def avail_images(conn=None, call=None): 177 """ 178 List available images for Azure 179 """ 180 if call == "action": 181 raise SaltCloudSystemExit( 182 "The avail_images function must be called with " 183 "-f or --function, or with the --list-images option" 184 ) 185 186 if not conn: 187 conn = get_conn() 188 189 ret = {} 190 for item in conn.list_os_images(): 191 ret[item.name] = object_to_dict(item) 192 for item in conn.list_vm_images(): 193 ret[item.name] = object_to_dict(item) 194 return ret 195 196 197def avail_sizes(call=None): 198 """ 199 Return a list of sizes from Azure 200 """ 201 if call == "action": 202 raise SaltCloudSystemExit( 203 "The avail_sizes function must be called with " 204 "-f or --function, or with the --list-sizes option" 205 ) 206 207 conn = get_conn() 208 data = conn.list_role_sizes() 209 ret = {} 210 for item in data.role_sizes: 211 ret[item.name] = object_to_dict(item) 212 return ret 213 214 215def list_nodes(conn=None, call=None): 216 """ 217 List VMs on this Azure account 218 """ 219 if call == "action": 220 raise SaltCloudSystemExit( 221 "The list_nodes function must be called with -f or --function." 222 ) 223 224 ret = {} 225 nodes = list_nodes_full(conn, call) 226 for node in nodes: 227 ret[node] = {"name": node} 228 for prop in ("id", "image", "size", "state", "private_ips", "public_ips"): 229 ret[node][prop] = nodes[node].get(prop) 230 return ret 231 232 233def list_nodes_full(conn=None, call=None): 234 """ 235 List VMs on this Azure account, with full information 236 """ 237 if call == "action": 238 raise SaltCloudSystemExit( 239 "The list_nodes_full function must be called with -f or --function." 240 ) 241 242 if not conn: 243 conn = get_conn() 244 245 ret = {} 246 services = list_hosted_services(conn=conn, call=call) 247 for service in services: 248 for deployment in services[service]["deployments"]: 249 deploy_dict = services[service]["deployments"][deployment] 250 deploy_dict_no_role_info = copy.deepcopy(deploy_dict) 251 del deploy_dict_no_role_info["role_list"] 252 del deploy_dict_no_role_info["role_instance_list"] 253 roles = deploy_dict["role_list"] 254 for role in roles: 255 role_instances = deploy_dict["role_instance_list"] 256 ret[role] = roles[role] 257 ret[role].update(role_instances[role]) 258 ret[role]["id"] = role 259 ret[role]["hosted_service"] = service 260 if role_instances[role]["power_state"] == "Started": 261 ret[role]["state"] = "running" 262 elif role_instances[role]["power_state"] == "Stopped": 263 ret[role]["state"] = "stopped" 264 else: 265 ret[role]["state"] = "pending" 266 ret[role]["private_ips"] = [] 267 ret[role]["public_ips"] = [] 268 ret[role]["deployment"] = deploy_dict_no_role_info 269 ret[role]["url"] = deploy_dict["url"] 270 ip_address = role_instances[role]["ip_address"] 271 if ip_address: 272 if salt.utils.cloud.is_public_ip(ip_address): 273 ret[role]["public_ips"].append(ip_address) 274 else: 275 ret[role]["private_ips"].append(ip_address) 276 ret[role]["size"] = role_instances[role]["instance_size"] 277 ret[role]["image"] = roles[role]["role_info"]["os_virtual_hard_disk"][ 278 "source_image_name" 279 ] 280 return ret 281 282 283def list_hosted_services(conn=None, call=None): 284 """ 285 List VMs on this Azure account, with full information 286 """ 287 if call == "action": 288 raise SaltCloudSystemExit( 289 "The list_hosted_services function must be called with -f or --function" 290 ) 291 292 if not conn: 293 conn = get_conn() 294 295 ret = {} 296 services = conn.list_hosted_services() 297 for service in services: 298 props = service.hosted_service_properties 299 ret[service.service_name] = { 300 "name": service.service_name, 301 "url": service.url, 302 "affinity_group": props.affinity_group, 303 "date_created": props.date_created, 304 "date_last_modified": props.date_last_modified, 305 "description": props.description, 306 "extended_properties": props.extended_properties, 307 "label": props.label, 308 "location": props.location, 309 "status": props.status, 310 "deployments": {}, 311 } 312 deployments = conn.get_hosted_service_properties( 313 service_name=service.service_name, embed_detail=True 314 ) 315 for deployment in deployments.deployments: 316 ret[service.service_name]["deployments"][deployment.name] = { 317 "configuration": deployment.configuration, 318 "created_time": deployment.created_time, 319 "deployment_slot": deployment.deployment_slot, 320 "extended_properties": deployment.extended_properties, 321 "input_endpoint_list": deployment.input_endpoint_list, 322 "label": deployment.label, 323 "last_modified_time": deployment.last_modified_time, 324 "locked": deployment.locked, 325 "name": deployment.name, 326 "persistent_vm_downtime_info": deployment.persistent_vm_downtime_info, 327 "private_id": deployment.private_id, 328 "role_instance_list": {}, 329 "role_list": {}, 330 "rollback_allowed": deployment.rollback_allowed, 331 "sdk_version": deployment.sdk_version, 332 "status": deployment.status, 333 "upgrade_domain_count": deployment.upgrade_domain_count, 334 "upgrade_status": deployment.upgrade_status, 335 "url": deployment.url, 336 } 337 for role_instance in deployment.role_instance_list: 338 ret[service.service_name]["deployments"][deployment.name][ 339 "role_instance_list" 340 ][role_instance.role_name] = { 341 "fqdn": role_instance.fqdn, 342 "instance_error_code": role_instance.instance_error_code, 343 "instance_fault_domain": role_instance.instance_fault_domain, 344 "instance_name": role_instance.instance_name, 345 "instance_size": role_instance.instance_size, 346 "instance_state_details": role_instance.instance_state_details, 347 "instance_status": role_instance.instance_status, 348 "instance_upgrade_domain": role_instance.instance_upgrade_domain, 349 "ip_address": role_instance.ip_address, 350 "power_state": role_instance.power_state, 351 "role_name": role_instance.role_name, 352 } 353 for role in deployment.role_list: 354 ret[service.service_name]["deployments"][deployment.name]["role_list"][ 355 role.role_name 356 ] = { 357 "role_name": role.role_name, 358 "os_version": role.os_version, 359 } 360 role_info = conn.get_role( 361 service_name=service.service_name, 362 deployment_name=deployment.name, 363 role_name=role.role_name, 364 ) 365 ret[service.service_name]["deployments"][deployment.name]["role_list"][ 366 role.role_name 367 ]["role_info"] = { 368 "availability_set_name": role_info.availability_set_name, 369 "configuration_sets": role_info.configuration_sets, 370 "data_virtual_hard_disks": role_info.data_virtual_hard_disks, 371 "os_version": role_info.os_version, 372 "role_name": role_info.role_name, 373 "role_size": role_info.role_size, 374 "role_type": role_info.role_type, 375 } 376 ret[service.service_name]["deployments"][deployment.name]["role_list"][ 377 role.role_name 378 ]["role_info"]["os_virtual_hard_disk"] = { 379 "disk_label": role_info.os_virtual_hard_disk.disk_label, 380 "disk_name": role_info.os_virtual_hard_disk.disk_name, 381 "host_caching": role_info.os_virtual_hard_disk.host_caching, 382 "media_link": role_info.os_virtual_hard_disk.media_link, 383 "os": role_info.os_virtual_hard_disk.os, 384 "source_image_name": role_info.os_virtual_hard_disk.source_image_name, 385 } 386 return ret 387 388 389def list_nodes_select(conn=None, call=None): 390 """ 391 Return a list of the VMs that are on the provider, with select fields 392 """ 393 if not conn: 394 conn = get_conn() 395 396 return salt.utils.cloud.list_nodes_select( 397 list_nodes_full(conn, "function"), 398 __opts__["query.selection"], 399 call, 400 ) 401 402 403def show_instance(name, call=None): 404 """ 405 Show the details from the provider concerning an instance 406 """ 407 if call != "action": 408 raise SaltCloudSystemExit( 409 "The show_instance action must be called with -a or --action." 410 ) 411 412 nodes = list_nodes_full() 413 # Find under which cloud service the name is listed, if any 414 if name not in nodes: 415 return {} 416 if "name" not in nodes[name]: 417 nodes[name]["name"] = nodes[name]["id"] 418 try: 419 __utils__["cloud.cache_node"]( 420 nodes[name], _get_active_provider_name(), __opts__ 421 ) 422 except TypeError: 423 log.warning( 424 "Unable to show cache node data; this may be because the node has been" 425 " deleted" 426 ) 427 return nodes[name] 428 429 430def create(vm_): 431 """ 432 Create a single VM from a data dict 433 """ 434 try: 435 # Check for required profile parameters before sending any API calls. 436 if ( 437 vm_["profile"] 438 and config.is_profile_configured( 439 __opts__, 440 _get_active_provider_name() or "azure", 441 vm_["profile"], 442 vm_=vm_, 443 ) 444 is False 445 ): 446 return False 447 except AttributeError: 448 pass 449 450 __utils__["cloud.fire_event"]( 451 "event", 452 "starting create", 453 "salt/cloud/{}/creating".format(vm_["name"]), 454 args=__utils__["cloud.filter_event"]( 455 "creating", vm_, ["name", "profile", "provider", "driver"] 456 ), 457 sock_dir=__opts__["sock_dir"], 458 transport=__opts__["transport"], 459 ) 460 461 log.info("Creating Cloud VM %s", vm_["name"]) 462 conn = get_conn() 463 464 label = vm_.get("label", vm_["name"]) 465 service_name = vm_.get("service_name", vm_["name"]) 466 service_kwargs = { 467 "service_name": service_name, 468 "label": label, 469 "description": vm_.get("desc", vm_["name"]), 470 } 471 472 loc_error = False 473 if "location" in vm_: 474 if "affinity_group" in vm_: 475 loc_error = True 476 else: 477 service_kwargs["location"] = vm_["location"] 478 elif "affinity_group" in vm_: 479 service_kwargs["affinity_group"] = vm_["affinity_group"] 480 else: 481 loc_error = True 482 483 if loc_error: 484 raise SaltCloudSystemExit( 485 "Either a location or affinity group must be specified, but not both" 486 ) 487 488 ssh_port = config.get_cloud_config_value( 489 "port", vm_, __opts__, default=22, search_global=True 490 ) 491 492 ssh_endpoint = azure.servicemanagement.ConfigurationSetInputEndpoint( 493 name="SSH", 494 protocol="TCP", 495 port=ssh_port, 496 local_port=22, 497 ) 498 499 network_config = azure.servicemanagement.ConfigurationSet() 500 network_config.input_endpoints.input_endpoints.append(ssh_endpoint) 501 network_config.configuration_set_type = "NetworkConfiguration" 502 503 if "win_username" in vm_: 504 system_config = azure.servicemanagement.WindowsConfigurationSet( 505 computer_name=vm_["name"], 506 admin_username=vm_["win_username"], 507 admin_password=vm_["win_password"], 508 ) 509 510 smb_port = "445" 511 if "smb_port" in vm_: 512 smb_port = vm_["smb_port"] 513 514 smb_endpoint = azure.servicemanagement.ConfigurationSetInputEndpoint( 515 name="SMB", 516 protocol="TCP", 517 port=smb_port, 518 local_port=smb_port, 519 ) 520 521 network_config.input_endpoints.input_endpoints.append(smb_endpoint) 522 523 # Domain and WinRM configuration not yet supported by Salt Cloud 524 system_config.domain_join = None 525 system_config.win_rm = None 526 527 else: 528 system_config = azure.servicemanagement.LinuxConfigurationSet( 529 host_name=vm_["name"], 530 user_name=vm_["ssh_username"], 531 user_password=vm_["ssh_password"], 532 disable_ssh_password_authentication=False, 533 ) 534 535 # TODO: Might need to create a storage account 536 media_link = vm_["media_link"] 537 # TODO: Probably better to use more than just the name in the media_link 538 media_link += "/{}.vhd".format(vm_["name"]) 539 os_hd = azure.servicemanagement.OSVirtualHardDisk(vm_["image"], media_link) 540 541 vm_kwargs = { 542 "service_name": service_name, 543 "deployment_name": service_name, 544 "deployment_slot": vm_["slot"], 545 "label": label, 546 "role_name": vm_["name"], 547 "system_config": system_config, 548 "os_virtual_hard_disk": os_hd, 549 "role_size": vm_["size"], 550 "network_config": network_config, 551 } 552 553 if "virtual_network_name" in vm_: 554 vm_kwargs["virtual_network_name"] = vm_["virtual_network_name"] 555 if "subnet_name" in vm_: 556 network_config.subnet_names.append(vm_["subnet_name"]) 557 558 log.debug("vm_kwargs: %s", vm_kwargs) 559 560 event_kwargs = { 561 "service_kwargs": service_kwargs.copy(), 562 "vm_kwargs": vm_kwargs.copy(), 563 } 564 del event_kwargs["vm_kwargs"]["system_config"] 565 del event_kwargs["vm_kwargs"]["os_virtual_hard_disk"] 566 del event_kwargs["vm_kwargs"]["network_config"] 567 __utils__["cloud.fire_event"]( 568 "event", 569 "requesting instance", 570 "salt/cloud/{}/requesting".format(vm_["name"]), 571 args=__utils__["cloud.filter_event"]( 572 "requesting", event_kwargs, list(event_kwargs) 573 ), 574 sock_dir=__opts__["sock_dir"], 575 transport=__opts__["transport"], 576 ) 577 log.debug("vm_kwargs: %s", vm_kwargs) 578 579 # Azure lets you open winrm on a new VM 580 # Can open up specific ports in Azure; but not on Windows 581 try: 582 conn.create_hosted_service(**service_kwargs) 583 except AzureConflictHttpError: 584 log.debug("Cloud service already exists") 585 except Exception as exc: # pylint: disable=broad-except 586 error = "The hosted service name is invalid." 587 if error in str(exc): 588 log.error( 589 "Error creating %s on Azure.\n\n" 590 "The hosted service name is invalid. The name can contain " 591 "only letters, numbers, and hyphens. The name must start with " 592 "a letter and must end with a letter or a number.", 593 vm_["name"], 594 # Show the traceback if the debug logging level is enabled 595 exc_info_on_loglevel=logging.DEBUG, 596 ) 597 else: 598 log.error( 599 "Error creating %s on Azure\n\n" 600 "The following exception was thrown when trying to " 601 "run the initial deployment: \n%s", 602 vm_["name"], 603 exc, 604 # Show the traceback if the debug logging level is enabled 605 exc_info_on_loglevel=logging.DEBUG, 606 ) 607 return False 608 try: 609 result = conn.create_virtual_machine_deployment(**vm_kwargs) 610 log.debug("Request ID for machine: %s", result.request_id) 611 _wait_for_async(conn, result.request_id) 612 except AzureConflictHttpError: 613 log.debug("Conflict error. The deployment may already exist, trying add_role") 614 # Deleting two useless keywords 615 del vm_kwargs["deployment_slot"] 616 del vm_kwargs["label"] 617 del vm_kwargs["virtual_network_name"] 618 result = conn.add_role(**vm_kwargs) # pylint: disable=unexpected-keyword-arg 619 _wait_for_async(conn, result.request_id) 620 except Exception as exc: # pylint: disable=broad-except 621 error = "The hosted service name is invalid." 622 if error in str(exc): 623 log.error( 624 "Error creating %s on Azure.\n\n" 625 "The VM name is invalid. The name can contain " 626 "only letters, numbers, and hyphens. The name must start with " 627 "a letter and must end with a letter or a number.", 628 vm_["name"], 629 # Show the traceback if the debug logging level is enabled 630 exc_info_on_loglevel=logging.DEBUG, 631 ) 632 else: 633 log.error( 634 "Error creating %s on Azure.\n\n" 635 "The Virtual Machine could not be created. If you " 636 "are using an already existing Cloud Service, " 637 "make sure you set up the `port` variable corresponding " 638 "to the SSH port exists and that the port number is not " 639 "already in use.\nThe following exception was thrown when trying to " 640 "run the initial deployment: \n%s", 641 vm_["name"], 642 exc, 643 # Show the traceback if the debug logging level is enabled 644 exc_info_on_loglevel=logging.DEBUG, 645 ) 646 return False 647 648 def wait_for_hostname(): 649 """ 650 Wait for the IP address to become available 651 """ 652 try: 653 conn.get_role(service_name, service_name, vm_["name"]) 654 data = show_instance(vm_["name"], call="action") 655 if "url" in data and data["url"] != "": 656 return data["url"] 657 except AzureMissingResourceHttpError: 658 pass 659 time.sleep(1) 660 return False 661 662 hostname = salt.utils.cloud.wait_for_fun( 663 wait_for_hostname, 664 timeout=config.get_cloud_config_value( 665 "wait_for_fun_timeout", vm_, __opts__, default=15 * 60 666 ), 667 ) 668 669 if not hostname: 670 log.error("Failed to get a value for the hostname.") 671 return False 672 673 vm_["ssh_host"] = hostname.replace("http://", "").replace("/", "") 674 vm_["password"] = config.get_cloud_config_value("ssh_password", vm_, __opts__) 675 ret = __utils__["cloud.bootstrap"](vm_, __opts__) 676 677 # Attaching volumes 678 volumes = config.get_cloud_config_value( 679 "volumes", vm_, __opts__, search_global=True 680 ) 681 if volumes: 682 __utils__["cloud.fire_event"]( 683 "event", 684 "attaching volumes", 685 "salt/cloud/{}/attaching_volumes".format(vm_["name"]), 686 args=__utils__["cloud.filter_event"]("attaching_volumes", vm_, ["volumes"]), 687 sock_dir=__opts__["sock_dir"], 688 transport=__opts__["transport"], 689 ) 690 691 log.info("Create and attach volumes to node %s", vm_["name"]) 692 created = create_attach_volumes( 693 vm_["name"], 694 { 695 "volumes": volumes, 696 "service_name": service_name, 697 "deployment_name": vm_["name"], 698 "media_link": media_link, 699 "role_name": vm_["name"], 700 "del_all_vols_on_destroy": vm_.get( 701 "set_del_all_vols_on_destroy", False 702 ), 703 }, 704 call="action", 705 ) 706 ret["Attached Volumes"] = created 707 708 data = show_instance(vm_["name"], call="action") 709 log.info("Created Cloud VM '%s'", vm_) 710 log.debug("'%s' VM creation details:\n%s", vm_["name"], pprint.pformat(data)) 711 712 ret.update(data) 713 714 __utils__["cloud.fire_event"]( 715 "event", 716 "created instance", 717 "salt/cloud/{}/created".format(vm_["name"]), 718 args=__utils__["cloud.filter_event"]( 719 "created", vm_, ["name", "profile", "provider", "driver"] 720 ), 721 sock_dir=__opts__["sock_dir"], 722 transport=__opts__["transport"], 723 ) 724 725 return ret 726 727 728def create_attach_volumes(name, kwargs, call=None, wait_to_finish=True): 729 """ 730 Create and attach volumes to created node 731 """ 732 if call != "action": 733 raise SaltCloudSystemExit( 734 "The create_attach_volumes action must be called with -a or --action." 735 ) 736 737 if kwargs is None: 738 kwargs = {} 739 740 if isinstance(kwargs["volumes"], str): 741 volumes = salt.utils.yaml.safe_load(kwargs["volumes"]) 742 else: 743 volumes = kwargs["volumes"] 744 745 # From the Azure .NET SDK doc 746 # 747 # The Create Data Disk operation adds a data disk to a virtual 748 # machine. There are three ways to create the data disk using the 749 # Add Data Disk operation. 750 # Option 1 - Attach an empty data disk to 751 # the role by specifying the disk label and location of the disk 752 # image. Do not include the DiskName and SourceMediaLink elements in 753 # the request body. Include the MediaLink element and reference a 754 # blob that is in the same geographical region as the role. You can 755 # also omit the MediaLink element. In this usage, Azure will create 756 # the data disk in the storage account configured as default for the 757 # role. 758 # Option 2 - Attach an existing data disk that is in the image 759 # repository. Do not include the DiskName and SourceMediaLink 760 # elements in the request body. Specify the data disk to use by 761 # including the DiskName element. Note: If included the in the 762 # response body, the MediaLink and LogicalDiskSizeInGB elements are 763 # ignored. 764 # Option 3 - Specify the location of a blob in your storage 765 # account that contain a disk image to use. Include the 766 # SourceMediaLink element. Note: If the MediaLink element 767 # isincluded, it is ignored. (see 768 # http://msdn.microsoft.com/en-us/library/windowsazure/jj157199.aspx 769 # for more information) 770 # 771 # Here only option 1 is implemented 772 conn = get_conn() 773 ret = [] 774 for volume in volumes: 775 if "disk_name" in volume: 776 log.error("You cannot specify a disk_name. Only new volumes are allowed") 777 return False 778 # Use the size keyword to set a size, but you can use the 779 # azure name too. If neither is set, the disk has size 100GB 780 volume.setdefault("logical_disk_size_in_gb", volume.get("size", 100)) 781 volume.setdefault("host_caching", "ReadOnly") 782 volume.setdefault("lun", 0) 783 # The media link is vm_name-disk-[0-15].vhd 784 volume.setdefault( 785 "media_link", 786 kwargs["media_link"][:-4] + "-disk-{}.vhd".format(volume["lun"]), 787 ) 788 volume.setdefault( 789 "disk_label", kwargs["role_name"] + "-disk-{}".format(volume["lun"]) 790 ) 791 volume_dict = {"volume_name": volume["lun"], "disk_label": volume["disk_label"]} 792 793 # Preparing the volume dict to be passed with ** 794 kwargs_add_data_disk = [ 795 "lun", 796 "host_caching", 797 "media_link", 798 "disk_label", 799 "disk_name", 800 "logical_disk_size_in_gb", 801 "source_media_link", 802 ] 803 for key in set(volume.keys()) - set(kwargs_add_data_disk): 804 del volume[key] 805 806 attach = conn.add_data_disk( 807 kwargs["service_name"], 808 kwargs["deployment_name"], 809 kwargs["role_name"], 810 **volume 811 ) 812 log.debug(attach) 813 814 # If attach is None then everything is fine 815 if attach: 816 msg = "{} attached to {} (aka {})".format( 817 volume_dict["volume_name"], 818 kwargs["role_name"], 819 name, 820 ) 821 log.info(msg) 822 ret.append(msg) 823 else: 824 log.error("Error attaching %s on Azure", volume_dict) 825 return ret 826 827 828def create_attach_volumes(name, kwargs, call=None, wait_to_finish=True): 829 """ 830 Create and attach volumes to created node 831 """ 832 if call != "action": 833 raise SaltCloudSystemExit( 834 "The create_attach_volumes action must be called with -a or --action." 835 ) 836 837 if kwargs is None: 838 kwargs = {} 839 840 if isinstance(kwargs["volumes"], str): 841 volumes = salt.utils.yaml.safe_load(kwargs["volumes"]) 842 else: 843 volumes = kwargs["volumes"] 844 845 # From the Azure .NET SDK doc 846 # 847 # The Create Data Disk operation adds a data disk to a virtual 848 # machine. There are three ways to create the data disk using the 849 # Add Data Disk operation. 850 # Option 1 - Attach an empty data disk to 851 # the role by specifying the disk label and location of the disk 852 # image. Do not include the DiskName and SourceMediaLink elements in 853 # the request body. Include the MediaLink element and reference a 854 # blob that is in the same geographical region as the role. You can 855 # also omit the MediaLink element. In this usage, Azure will create 856 # the data disk in the storage account configured as default for the 857 # role. 858 # Option 2 - Attach an existing data disk that is in the image 859 # repository. Do not include the DiskName and SourceMediaLink 860 # elements in the request body. Specify the data disk to use by 861 # including the DiskName element. Note: If included the in the 862 # response body, the MediaLink and LogicalDiskSizeInGB elements are 863 # ignored. 864 # Option 3 - Specify the location of a blob in your storage 865 # account that contain a disk image to use. Include the 866 # SourceMediaLink element. Note: If the MediaLink element 867 # isincluded, it is ignored. (see 868 # http://msdn.microsoft.com/en-us/library/windowsazure/jj157199.aspx 869 # for more information) 870 # 871 # Here only option 1 is implemented 872 conn = get_conn() 873 ret = [] 874 for volume in volumes: 875 if "disk_name" in volume: 876 log.error("You cannot specify a disk_name. Only new volumes are allowed") 877 return False 878 # Use the size keyword to set a size, but you can use the 879 # azure name too. If neither is set, the disk has size 100GB 880 volume.setdefault("logical_disk_size_in_gb", volume.get("size", 100)) 881 volume.setdefault("host_caching", "ReadOnly") 882 volume.setdefault("lun", 0) 883 # The media link is vm_name-disk-[0-15].vhd 884 volume.setdefault( 885 "media_link", 886 kwargs["media_link"][:-4] + "-disk-{}.vhd".format(volume["lun"]), 887 ) 888 volume.setdefault( 889 "disk_label", kwargs["role_name"] + "-disk-{}".format(volume["lun"]) 890 ) 891 volume_dict = {"volume_name": volume["lun"], "disk_label": volume["disk_label"]} 892 893 # Preparing the volume dict to be passed with ** 894 kwargs_add_data_disk = [ 895 "lun", 896 "host_caching", 897 "media_link", 898 "disk_label", 899 "disk_name", 900 "logical_disk_size_in_gb", 901 "source_media_link", 902 ] 903 for key in set(volume.keys()) - set(kwargs_add_data_disk): 904 del volume[key] 905 906 result = conn.add_data_disk( 907 kwargs["service_name"], 908 kwargs["deployment_name"], 909 kwargs["role_name"], 910 **volume 911 ) 912 _wait_for_async(conn, result.request_id) 913 914 msg = "{} attached to {} (aka {})".format( 915 volume_dict["volume_name"], kwargs["role_name"], name 916 ) 917 log.info(msg) 918 ret.append(msg) 919 return ret 920 921 922# Helper function for azure tests 923def _wait_for_async(conn, request_id): 924 """ 925 Helper function for azure tests 926 """ 927 count = 0 928 log.debug("Waiting for asynchronous operation to complete") 929 result = conn.get_operation_status(request_id) 930 while result.status == "InProgress": 931 count = count + 1 932 if count > 120: 933 raise ValueError( 934 "Timed out waiting for asynchronous operation to complete." 935 ) 936 time.sleep(5) 937 result = conn.get_operation_status(request_id) 938 939 if result.status != "Succeeded": 940 raise AzureException( 941 "Operation failed. {message} ({code})".format( 942 message=result.error.message, code=result.error.code 943 ) 944 ) 945 946 947def destroy(name, conn=None, call=None, kwargs=None): 948 """ 949 Destroy a VM 950 951 CLI Examples: 952 953 .. code-block:: bash 954 955 salt-cloud -d myminion 956 salt-cloud -a destroy myminion service_name=myservice 957 """ 958 if call == "function": 959 raise SaltCloudSystemExit( 960 "The destroy action must be called with -d, --destroy, -a or --action." 961 ) 962 963 if not conn: 964 conn = get_conn() 965 966 if kwargs is None: 967 kwargs = {} 968 969 instance_data = show_instance(name, call="action") 970 service_name = instance_data["deployment"]["name"] 971 disk_name = instance_data["role_info"]["os_virtual_hard_disk"]["disk_name"] 972 973 ret = {} 974 # TODO: Add the ability to delete or not delete a hosted service when 975 # deleting a VM 976 try: 977 log.debug("Deleting role") 978 result = conn.delete_role(service_name, service_name, name) 979 delete_type = "delete_role" 980 except AzureException: 981 log.debug("Failed to delete role, deleting deployment") 982 try: 983 result = conn.delete_deployment(service_name, service_name) 984 except AzureConflictHttpError as exc: 985 log.error(exc.message) 986 raise SaltCloudSystemExit("{}: {}".format(name, exc.message)) 987 delete_type = "delete_deployment" 988 _wait_for_async(conn, result.request_id) 989 ret[name] = { 990 delete_type: {"request_id": result.request_id}, 991 } 992 if __opts__.get("update_cachedir", False) is True: 993 __utils__["cloud.delete_minion_cachedir"]( 994 name, _get_active_provider_name().split(":")[0], __opts__ 995 ) 996 997 cleanup_disks = config.get_cloud_config_value( 998 "cleanup_disks", 999 get_configured_provider(), 1000 __opts__, 1001 search_global=False, 1002 default=False, 1003 ) 1004 if cleanup_disks: 1005 cleanup_vhds = kwargs.get( 1006 "delete_vhd", 1007 config.get_cloud_config_value( 1008 "cleanup_vhds", 1009 get_configured_provider(), 1010 __opts__, 1011 search_global=False, 1012 default=False, 1013 ), 1014 ) 1015 log.debug("Deleting disk %s", disk_name) 1016 if cleanup_vhds: 1017 log.debug("Deleting vhd") 1018 1019 def wait_for_destroy(): 1020 """ 1021 Wait for the VM to be deleted 1022 """ 1023 try: 1024 data = delete_disk( 1025 kwargs={"name": disk_name, "delete_vhd": cleanup_vhds}, 1026 call="function", 1027 ) 1028 return data 1029 except AzureConflictHttpError: 1030 log.debug("Waiting for VM to be destroyed...") 1031 time.sleep(5) 1032 return False 1033 1034 data = salt.utils.cloud.wait_for_fun( 1035 wait_for_destroy, 1036 timeout=config.get_cloud_config_value( 1037 "wait_for_fun_timeout", {}, __opts__, default=15 * 60 1038 ), 1039 ) 1040 ret[name]["delete_disk"] = { 1041 "name": disk_name, 1042 "delete_vhd": cleanup_vhds, 1043 "data": data, 1044 } 1045 1046 # Services can't be cleaned up unless disks are too 1047 cleanup_services = config.get_cloud_config_value( 1048 "cleanup_services", 1049 get_configured_provider(), 1050 __opts__, 1051 search_global=False, 1052 default=False, 1053 ) 1054 if cleanup_services: 1055 log.debug("Deleting service %s", service_name) 1056 1057 def wait_for_disk_delete(): 1058 """ 1059 Wait for the disk to be deleted 1060 """ 1061 try: 1062 data = delete_service( 1063 kwargs={"name": service_name}, call="function" 1064 ) 1065 return data 1066 except AzureConflictHttpError: 1067 log.debug("Waiting for disk to be deleted...") 1068 time.sleep(5) 1069 return False 1070 1071 data = salt.utils.cloud.wait_for_fun( 1072 wait_for_disk_delete, 1073 timeout=config.get_cloud_config_value( 1074 "wait_for_fun_timeout", {}, __opts__, default=15 * 60 1075 ), 1076 ) 1077 ret[name]["delete_services"] = {"name": service_name, "data": data} 1078 1079 return ret 1080 1081 1082def list_storage_services(conn=None, call=None): 1083 """ 1084 List VMs on this Azure account, with full information 1085 """ 1086 if call != "function": 1087 raise SaltCloudSystemExit( 1088 "The list_storage_services function must be called with -f or --function." 1089 ) 1090 1091 if not conn: 1092 conn = get_conn() 1093 1094 ret = {} 1095 accounts = conn.list_storage_accounts() 1096 for service in accounts.storage_services: 1097 ret[service.service_name] = { 1098 "capabilities": service.capabilities, 1099 "service_name": service.service_name, 1100 "storage_service_properties": service.storage_service_properties, 1101 "extended_properties": service.extended_properties, 1102 "storage_service_keys": service.storage_service_keys, 1103 "url": service.url, 1104 } 1105 return ret 1106 1107 1108def get_operation_status(kwargs=None, conn=None, call=None): 1109 """ 1110 .. versionadded:: 2015.8.0 1111 1112 Get Operation Status, based on a request ID 1113 1114 CLI Example: 1115 1116 .. code-block:: bash 1117 1118 salt-cloud -f get_operation_status my-azure id=0123456789abcdef0123456789abcdef 1119 """ 1120 if call != "function": 1121 raise SaltCloudSystemExit( 1122 "The show_instance function must be called with -f or --function." 1123 ) 1124 1125 if kwargs is None: 1126 kwargs = {} 1127 1128 if "id" not in kwargs: 1129 raise SaltCloudSystemExit('A request ID must be specified as "id"') 1130 1131 if not conn: 1132 conn = get_conn() 1133 1134 data = conn.get_operation_status(kwargs["id"]) 1135 ret = { 1136 "http_status_code": data.http_status_code, 1137 "id": kwargs["id"], 1138 "status": data.status, 1139 } 1140 if hasattr(data.error, "code"): 1141 ret["error"] = { 1142 "code": data.error.code, 1143 "message": data.error.message, 1144 } 1145 1146 return ret 1147 1148 1149def list_storage(kwargs=None, conn=None, call=None): 1150 """ 1151 .. versionadded:: 2015.8.0 1152 1153 List storage accounts associated with the account 1154 1155 CLI Example: 1156 1157 .. code-block:: bash 1158 1159 salt-cloud -f list_storage my-azure 1160 """ 1161 if call != "function": 1162 raise SaltCloudSystemExit( 1163 "The list_storage function must be called with -f or --function." 1164 ) 1165 1166 if not conn: 1167 conn = get_conn() 1168 1169 data = conn.list_storage_accounts() 1170 pprint.pprint(dir(data)) 1171 ret = {} 1172 for item in data.storage_services: 1173 ret[item.service_name] = object_to_dict(item) 1174 return ret 1175 1176 1177def show_storage(kwargs=None, conn=None, call=None): 1178 """ 1179 .. versionadded:: 2015.8.0 1180 1181 List storage service properties 1182 1183 CLI Example: 1184 1185 .. code-block:: bash 1186 1187 salt-cloud -f show_storage my-azure name=my_storage 1188 """ 1189 if call != "function": 1190 raise SaltCloudSystemExit( 1191 "The show_storage function must be called with -f or --function." 1192 ) 1193 1194 if not conn: 1195 conn = get_conn() 1196 1197 if kwargs is None: 1198 kwargs = {} 1199 1200 if "name" not in kwargs: 1201 raise SaltCloudSystemExit('A name must be specified as "name"') 1202 1203 data = conn.get_storage_account_properties( 1204 kwargs["name"], 1205 ) 1206 return object_to_dict(data) 1207 1208 1209# To reflect the Azure API 1210get_storage = show_storage 1211 1212 1213def show_storage_keys(kwargs=None, conn=None, call=None): 1214 """ 1215 .. versionadded:: 2015.8.0 1216 1217 Show storage account keys 1218 1219 CLI Example: 1220 1221 .. code-block:: bash 1222 1223 salt-cloud -f show_storage_keys my-azure name=my_storage 1224 """ 1225 if call != "function": 1226 raise SaltCloudSystemExit( 1227 "The show_storage_keys function must be called with -f or --function." 1228 ) 1229 1230 if not conn: 1231 conn = get_conn() 1232 1233 if kwargs is None: 1234 kwargs = {} 1235 1236 if "name" not in kwargs: 1237 raise SaltCloudSystemExit('A name must be specified as "name"') 1238 1239 try: 1240 data = conn.get_storage_account_keys( 1241 kwargs["name"], 1242 ) 1243 except AzureMissingResourceHttpError as exc: 1244 storage_data = show_storage(kwargs={"name": kwargs["name"]}, call="function") 1245 if storage_data["storage_service_properties"]["status"] == "Creating": 1246 raise SaltCloudSystemExit( 1247 "The storage account keys have not yet been created." 1248 ) 1249 else: 1250 raise SaltCloudSystemExit("{}: {}".format(kwargs["name"], exc.message)) 1251 return object_to_dict(data) 1252 1253 1254# To reflect the Azure API 1255get_storage_keys = show_storage_keys 1256 1257 1258def create_storage(kwargs=None, conn=None, call=None): 1259 """ 1260 .. versionadded:: 2015.8.0 1261 1262 Create a new storage account 1263 1264 CLI Example: 1265 1266 .. code-block:: bash 1267 1268 salt-cloud -f create_storage my-azure name=my_storage label=my_storage location='West US' 1269 """ 1270 if call != "function": 1271 raise SaltCloudSystemExit( 1272 "The show_storage function must be called with -f or --function." 1273 ) 1274 1275 if kwargs is None: 1276 kwargs = {} 1277 1278 if not conn: 1279 conn = get_conn() 1280 1281 if "name" not in kwargs: 1282 raise SaltCloudSystemExit('A name must be specified as "name"') 1283 1284 if "description" not in kwargs: 1285 raise SaltCloudSystemExit('A description must be specified as "description"') 1286 1287 if "label" not in kwargs: 1288 raise SaltCloudSystemExit('A label must be specified as "label"') 1289 1290 if "location" not in kwargs and "affinity_group" not in kwargs: 1291 raise SaltCloudSystemExit( 1292 "Either a location or an affinity_group must be specified (but not both)" 1293 ) 1294 1295 try: 1296 data = conn.create_storage_account( 1297 service_name=kwargs["name"], 1298 label=kwargs["label"], 1299 description=kwargs.get("description", None), 1300 location=kwargs.get("location", None), 1301 affinity_group=kwargs.get("affinity_group", None), 1302 extended_properties=kwargs.get("extended_properties", None), 1303 geo_replication_enabled=kwargs.get("geo_replication_enabled", None), 1304 account_type=kwargs.get("account_type", "Standard_GRS"), 1305 ) 1306 return {"Success": "The storage account was successfully created"} 1307 except AzureConflictHttpError: 1308 raise SaltCloudSystemExit( 1309 "There was a conflict. This usually means that the storage account already" 1310 " exists." 1311 ) 1312 1313 1314def update_storage(kwargs=None, conn=None, call=None): 1315 """ 1316 .. versionadded:: 2015.8.0 1317 1318 Update a storage account's properties 1319 1320 CLI Example: 1321 1322 .. code-block:: bash 1323 1324 salt-cloud -f update_storage my-azure name=my_storage label=my_storage 1325 """ 1326 if call != "function": 1327 raise SaltCloudSystemExit( 1328 "The show_storage function must be called with -f or --function." 1329 ) 1330 1331 if not conn: 1332 conn = get_conn() 1333 1334 if kwargs is None: 1335 kwargs = {} 1336 1337 if "name" not in kwargs: 1338 raise SaltCloudSystemExit('A name must be specified as "name"') 1339 1340 data = conn.update_storage_account( 1341 service_name=kwargs["name"], 1342 label=kwargs.get("label", None), 1343 description=kwargs.get("description", None), 1344 extended_properties=kwargs.get("extended_properties", None), 1345 geo_replication_enabled=kwargs.get("geo_replication_enabled", None), 1346 account_type=kwargs.get("account_type", "Standard_GRS"), 1347 ) 1348 return show_storage(kwargs={"name": kwargs["name"]}, call="function") 1349 1350 1351def regenerate_storage_keys(kwargs=None, conn=None, call=None): 1352 """ 1353 .. versionadded:: 2015.8.0 1354 1355 Regenerate storage account keys. Requires a key_type ("primary" or 1356 "secondary") to be specified. 1357 1358 CLI Example: 1359 1360 .. code-block:: bash 1361 1362 salt-cloud -f regenerate_storage_keys my-azure name=my_storage key_type=primary 1363 """ 1364 if call != "function": 1365 raise SaltCloudSystemExit( 1366 "The show_storage function must be called with -f or --function." 1367 ) 1368 1369 if not conn: 1370 conn = get_conn() 1371 1372 if kwargs is None: 1373 kwargs = {} 1374 1375 if "name" not in kwargs: 1376 raise SaltCloudSystemExit('A name must be specified as "name"') 1377 1378 if "key_type" not in kwargs or kwargs["key_type"] not in ("primary", "secondary"): 1379 raise SaltCloudSystemExit( 1380 'A key_type must be specified ("primary" or "secondary")' 1381 ) 1382 1383 try: 1384 data = conn.regenerate_storage_account_keys( 1385 service_name=kwargs["name"], 1386 key_type=kwargs["key_type"], 1387 ) 1388 return show_storage_keys(kwargs={"name": kwargs["name"]}, call="function") 1389 except AzureConflictHttpError: 1390 raise SaltCloudSystemExit( 1391 "There was a conflict. This usually means that the storage account already" 1392 " exists." 1393 ) 1394 1395 1396def delete_storage(kwargs=None, conn=None, call=None): 1397 """ 1398 .. versionadded:: 2015.8.0 1399 1400 Delete a specific storage account 1401 1402 CLI Examples: 1403 1404 .. code-block:: bash 1405 1406 salt-cloud -f delete_storage my-azure name=my_storage 1407 """ 1408 if call != "function": 1409 raise SaltCloudSystemExit( 1410 "The delete_storage function must be called with -f or --function." 1411 ) 1412 1413 if kwargs is None: 1414 kwargs = {} 1415 1416 if "name" not in kwargs: 1417 raise SaltCloudSystemExit('A name must be specified as "name"') 1418 1419 if not conn: 1420 conn = get_conn() 1421 1422 try: 1423 data = conn.delete_storage_account(kwargs["name"]) 1424 return {"Success": "The storage account was successfully deleted"} 1425 except AzureMissingResourceHttpError as exc: 1426 raise SaltCloudSystemExit("{}: {}".format(kwargs["name"], exc.message)) 1427 1428 1429def list_services(kwargs=None, conn=None, call=None): 1430 """ 1431 .. versionadded:: 2015.8.0 1432 1433 List hosted services associated with the account 1434 1435 CLI Example: 1436 1437 .. code-block:: bash 1438 1439 salt-cloud -f list_services my-azure 1440 """ 1441 if call != "function": 1442 raise SaltCloudSystemExit( 1443 "The list_services function must be called with -f or --function." 1444 ) 1445 1446 if not conn: 1447 conn = get_conn() 1448 1449 data = conn.list_hosted_services() 1450 ret = {} 1451 for item in data.hosted_services: 1452 ret[item.service_name] = object_to_dict(item) 1453 ret[item.service_name]["name"] = item.service_name 1454 return ret 1455 1456 1457def show_service(kwargs=None, conn=None, call=None): 1458 """ 1459 .. versionadded:: 2015.8.0 1460 1461 List hosted service properties 1462 1463 CLI Example: 1464 1465 .. code-block:: bash 1466 1467 salt-cloud -f show_service my-azure name=my_service 1468 """ 1469 if call != "function": 1470 raise SaltCloudSystemExit( 1471 "The show_service function must be called with -f or --function." 1472 ) 1473 1474 if not conn: 1475 conn = get_conn() 1476 1477 if kwargs is None: 1478 kwargs = {} 1479 1480 if "name" not in kwargs: 1481 raise SaltCloudSystemExit('A name must be specified as "name"') 1482 1483 data = conn.get_hosted_service_properties( 1484 kwargs["name"], kwargs.get("details", False) 1485 ) 1486 ret = object_to_dict(data) 1487 return ret 1488 1489 1490def create_service(kwargs=None, conn=None, call=None): 1491 """ 1492 .. versionadded:: 2015.8.0 1493 1494 Create a new hosted service 1495 1496 CLI Example: 1497 1498 .. code-block:: bash 1499 1500 salt-cloud -f create_service my-azure name=my_service label=my_service location='West US' 1501 """ 1502 if call != "function": 1503 raise SaltCloudSystemExit( 1504 "The create_service function must be called with -f or --function." 1505 ) 1506 1507 if not conn: 1508 conn = get_conn() 1509 1510 if kwargs is None: 1511 kwargs = {} 1512 1513 if "name" not in kwargs: 1514 raise SaltCloudSystemExit('A name must be specified as "name"') 1515 1516 if "label" not in kwargs: 1517 raise SaltCloudSystemExit('A label must be specified as "label"') 1518 1519 if "location" not in kwargs and "affinity_group" not in kwargs: 1520 raise SaltCloudSystemExit( 1521 "Either a location or an affinity_group must be specified (but not both)" 1522 ) 1523 1524 try: 1525 data = conn.create_hosted_service( 1526 kwargs["name"], 1527 kwargs["label"], 1528 kwargs.get("description", None), 1529 kwargs.get("location", None), 1530 kwargs.get("affinity_group", None), 1531 kwargs.get("extended_properties", None), 1532 ) 1533 return {"Success": "The service was successfully created"} 1534 except AzureConflictHttpError: 1535 raise SaltCloudSystemExit( 1536 "There was a conflict. This usually means that the service already exists." 1537 ) 1538 1539 1540def delete_service(kwargs=None, conn=None, call=None): 1541 """ 1542 .. versionadded:: 2015.8.0 1543 1544 Delete a specific service associated with the account 1545 1546 CLI Examples: 1547 1548 .. code-block:: bash 1549 1550 salt-cloud -f delete_service my-azure name=my_service 1551 """ 1552 if call != "function": 1553 raise SaltCloudSystemExit( 1554 "The delete_service function must be called with -f or --function." 1555 ) 1556 1557 if kwargs is None: 1558 kwargs = {} 1559 1560 if "name" not in kwargs: 1561 raise SaltCloudSystemExit('A name must be specified as "name"') 1562 1563 if not conn: 1564 conn = get_conn() 1565 1566 try: 1567 conn.delete_hosted_service(kwargs["name"]) 1568 return {"Success": "The service was successfully deleted"} 1569 except AzureMissingResourceHttpError as exc: 1570 raise SaltCloudSystemExit("{}: {}".format(kwargs["name"], exc.message)) 1571 1572 1573def list_disks(kwargs=None, conn=None, call=None): 1574 """ 1575 .. versionadded:: 2015.8.0 1576 1577 List disks associated with the account 1578 1579 CLI Example: 1580 1581 .. code-block:: bash 1582 1583 salt-cloud -f list_disks my-azure 1584 """ 1585 if call != "function": 1586 raise SaltCloudSystemExit( 1587 "The list_disks function must be called with -f or --function." 1588 ) 1589 1590 if not conn: 1591 conn = get_conn() 1592 1593 data = conn.list_disks() 1594 ret = {} 1595 for item in data.disks: 1596 ret[item.name] = object_to_dict(item) 1597 return ret 1598 1599 1600def show_disk(kwargs=None, conn=None, call=None): 1601 """ 1602 .. versionadded:: 2015.8.0 1603 1604 Return information about a disk 1605 1606 CLI Example: 1607 1608 .. code-block:: bash 1609 1610 salt-cloud -f show_disk my-azure name=my_disk 1611 """ 1612 if call != "function": 1613 raise SaltCloudSystemExit( 1614 "The get_disk function must be called with -f or --function." 1615 ) 1616 1617 if not conn: 1618 conn = get_conn() 1619 1620 if kwargs is None: 1621 kwargs = {} 1622 1623 if "name" not in kwargs: 1624 raise SaltCloudSystemExit('A name must be specified as "name"') 1625 1626 data = conn.get_disk(kwargs["name"]) 1627 return object_to_dict(data) 1628 1629 1630# For consistency with Azure SDK 1631get_disk = show_disk 1632 1633 1634def cleanup_unattached_disks(kwargs=None, conn=None, call=None): 1635 """ 1636 .. versionadded:: 2015.8.0 1637 1638 Cleans up all disks associated with the account, which are not attached. 1639 *** CAUTION *** This is a destructive function with no undo button, and no 1640 "Are you sure?" confirmation! 1641 1642 CLI Examples: 1643 1644 .. code-block:: bash 1645 1646 salt-cloud -f cleanup_unattached_disks my-azure name=my_disk 1647 salt-cloud -f cleanup_unattached_disks my-azure name=my_disk delete_vhd=True 1648 """ 1649 if call != "function": 1650 raise SaltCloudSystemExit( 1651 "The delete_disk function must be called with -f or --function." 1652 ) 1653 1654 if kwargs is None: 1655 kwargs = {} 1656 1657 disks = list_disks(kwargs=kwargs, conn=conn, call="function") 1658 for disk in disks: 1659 if disks[disk]["attached_to"] is None: 1660 del_kwargs = { 1661 "name": disks[disk]["name"], 1662 "delete_vhd": kwargs.get("delete_vhd", False), 1663 } 1664 log.info( 1665 "Deleting disk %s, deleting VHD: %s", 1666 del_kwargs["name"], 1667 del_kwargs["delete_vhd"], 1668 ) 1669 data = delete_disk(kwargs=del_kwargs, call="function") 1670 return True 1671 1672 1673def delete_disk(kwargs=None, conn=None, call=None): 1674 """ 1675 .. versionadded:: 2015.8.0 1676 1677 Delete a specific disk associated with the account 1678 1679 CLI Examples: 1680 1681 .. code-block:: bash 1682 1683 salt-cloud -f delete_disk my-azure name=my_disk 1684 salt-cloud -f delete_disk my-azure name=my_disk delete_vhd=True 1685 """ 1686 if call != "function": 1687 raise SaltCloudSystemExit( 1688 "The delete_disk function must be called with -f or --function." 1689 ) 1690 1691 if kwargs is None: 1692 kwargs = {} 1693 1694 if "name" not in kwargs: 1695 raise SaltCloudSystemExit('A name must be specified as "name"') 1696 1697 if not conn: 1698 conn = get_conn() 1699 1700 try: 1701 data = conn.delete_disk(kwargs["name"], kwargs.get("delete_vhd", False)) 1702 return {"Success": "The disk was successfully deleted"} 1703 except AzureMissingResourceHttpError as exc: 1704 raise SaltCloudSystemExit("{}: {}".format(kwargs["name"], exc.message)) 1705 1706 1707def update_disk(kwargs=None, conn=None, call=None): 1708 """ 1709 .. versionadded:: 2015.8.0 1710 1711 Update a disk's properties 1712 1713 CLI Example: 1714 1715 .. code-block:: bash 1716 1717 salt-cloud -f update_disk my-azure name=my_disk label=my_disk 1718 salt-cloud -f update_disk my-azure name=my_disk new_name=another_disk 1719 """ 1720 if call != "function": 1721 raise SaltCloudSystemExit( 1722 "The show_disk function must be called with -f or --function." 1723 ) 1724 1725 if not conn: 1726 conn = get_conn() 1727 1728 if kwargs is None: 1729 kwargs = {} 1730 1731 if "name" not in kwargs: 1732 raise SaltCloudSystemExit('A name must be specified as "name"') 1733 1734 old_data = show_disk(kwargs={"name": kwargs["name"]}, call="function") 1735 data = conn.update_disk( 1736 disk_name=kwargs["name"], 1737 has_operating_system=kwargs.get( 1738 "has_operating_system", old_data["has_operating_system"] 1739 ), 1740 label=kwargs.get("label", old_data["label"]), 1741 media_link=kwargs.get("media_link", old_data["media_link"]), 1742 name=kwargs.get("new_name", old_data["name"]), 1743 os=kwargs.get("os", old_data["os"]), 1744 ) 1745 return show_disk(kwargs={"name": kwargs["name"]}, call="function") 1746 1747 1748def list_service_certificates(kwargs=None, conn=None, call=None): 1749 """ 1750 .. versionadded:: 2015.8.0 1751 1752 List certificates associated with the service 1753 1754 CLI Example: 1755 1756 .. code-block:: bash 1757 1758 salt-cloud -f list_service_certificates my-azure name=my_service 1759 """ 1760 if call != "function": 1761 raise SaltCloudSystemExit( 1762 "The list_service_certificates function must be called with -f or" 1763 " --function." 1764 ) 1765 1766 if kwargs is None: 1767 kwargs = {} 1768 1769 if "name" not in kwargs: 1770 raise SaltCloudSystemExit('A service name must be specified as "name"') 1771 1772 if not conn: 1773 conn = get_conn() 1774 1775 data = conn.list_service_certificates(service_name=kwargs["name"]) 1776 ret = {} 1777 for item in data.certificates: 1778 ret[item.thumbprint] = object_to_dict(item) 1779 return ret 1780 1781 1782def show_service_certificate(kwargs=None, conn=None, call=None): 1783 """ 1784 .. versionadded:: 2015.8.0 1785 1786 Return information about a service certificate 1787 1788 CLI Example: 1789 1790 .. code-block:: bash 1791 1792 salt-cloud -f show_service_certificate my-azure name=my_service_certificate \\ 1793 thumbalgorithm=sha1 thumbprint=0123456789ABCDEF 1794 """ 1795 if call != "function": 1796 raise SaltCloudSystemExit( 1797 "The get_service_certificate function must be called with -f or --function." 1798 ) 1799 1800 if not conn: 1801 conn = get_conn() 1802 1803 if kwargs is None: 1804 kwargs = {} 1805 1806 if "name" not in kwargs: 1807 raise SaltCloudSystemExit('A service name must be specified as "name"') 1808 1809 if "thumbalgorithm" not in kwargs: 1810 raise SaltCloudSystemExit( 1811 'A thumbalgorithm must be specified as "thumbalgorithm"' 1812 ) 1813 1814 if "thumbprint" not in kwargs: 1815 raise SaltCloudSystemExit('A thumbprint must be specified as "thumbprint"') 1816 1817 data = conn.get_service_certificate( 1818 kwargs["name"], 1819 kwargs["thumbalgorithm"], 1820 kwargs["thumbprint"], 1821 ) 1822 return object_to_dict(data) 1823 1824 1825# For consistency with Azure SDK 1826get_service_certificate = show_service_certificate 1827 1828 1829def add_service_certificate(kwargs=None, conn=None, call=None): 1830 """ 1831 .. versionadded:: 2015.8.0 1832 1833 Add a new service certificate 1834 1835 CLI Example: 1836 1837 .. code-block:: bash 1838 1839 salt-cloud -f add_service_certificate my-azure name=my_service_certificate \\ 1840 data='...CERT_DATA...' certificate_format=sha1 password=verybadpass 1841 """ 1842 if call != "function": 1843 raise SaltCloudSystemExit( 1844 "The add_service_certificate function must be called with -f or --function." 1845 ) 1846 1847 if not conn: 1848 conn = get_conn() 1849 1850 if kwargs is None: 1851 kwargs = {} 1852 1853 if "name" not in kwargs: 1854 raise SaltCloudSystemExit('A name must be specified as "name"') 1855 1856 if "data" not in kwargs: 1857 raise SaltCloudSystemExit('Certificate data must be specified as "data"') 1858 1859 if "certificate_format" not in kwargs: 1860 raise SaltCloudSystemExit( 1861 'A certificate_format must be specified as "certificate_format"' 1862 ) 1863 1864 if "password" not in kwargs: 1865 raise SaltCloudSystemExit('A password must be specified as "password"') 1866 1867 try: 1868 data = conn.add_service_certificate( 1869 kwargs["name"], 1870 kwargs["data"], 1871 kwargs["certificate_format"], 1872 kwargs["password"], 1873 ) 1874 return {"Success": "The service certificate was successfully added"} 1875 except AzureConflictHttpError: 1876 raise SaltCloudSystemExit( 1877 "There was a conflict. This usually means that the " 1878 "service certificate already exists." 1879 ) 1880 1881 1882def delete_service_certificate(kwargs=None, conn=None, call=None): 1883 """ 1884 .. versionadded:: 2015.8.0 1885 1886 Delete a specific certificate associated with the service 1887 1888 CLI Examples: 1889 1890 .. code-block:: bash 1891 1892 salt-cloud -f delete_service_certificate my-azure name=my_service_certificate \\ 1893 thumbalgorithm=sha1 thumbprint=0123456789ABCDEF 1894 """ 1895 if call != "function": 1896 raise SaltCloudSystemExit( 1897 "The delete_service_certificate function must be called with -f or" 1898 " --function." 1899 ) 1900 1901 if kwargs is None: 1902 kwargs = {} 1903 1904 if "name" not in kwargs: 1905 raise SaltCloudSystemExit('A name must be specified as "name"') 1906 1907 if "thumbalgorithm" not in kwargs: 1908 raise SaltCloudSystemExit( 1909 'A thumbalgorithm must be specified as "thumbalgorithm"' 1910 ) 1911 1912 if "thumbprint" not in kwargs: 1913 raise SaltCloudSystemExit('A thumbprint must be specified as "thumbprint"') 1914 1915 if not conn: 1916 conn = get_conn() 1917 1918 try: 1919 data = conn.delete_service_certificate( 1920 kwargs["name"], 1921 kwargs["thumbalgorithm"], 1922 kwargs["thumbprint"], 1923 ) 1924 return {"Success": "The service certificate was successfully deleted"} 1925 except AzureMissingResourceHttpError as exc: 1926 raise SaltCloudSystemExit("{}: {}".format(kwargs["name"], exc.message)) 1927 1928 1929def list_management_certificates(kwargs=None, conn=None, call=None): 1930 """ 1931 .. versionadded:: 2015.8.0 1932 1933 List management certificates associated with the subscription 1934 1935 CLI Example: 1936 1937 .. code-block:: bash 1938 1939 salt-cloud -f list_management_certificates my-azure name=my_management 1940 """ 1941 if call != "function": 1942 raise SaltCloudSystemExit( 1943 "The list_management_certificates function must be called with -f or" 1944 " --function." 1945 ) 1946 1947 if not conn: 1948 conn = get_conn() 1949 1950 data = conn.list_management_certificates() 1951 ret = {} 1952 for item in data.subscription_certificates: 1953 ret[item.subscription_certificate_thumbprint] = object_to_dict(item) 1954 return ret 1955 1956 1957def show_management_certificate(kwargs=None, conn=None, call=None): 1958 """ 1959 .. versionadded:: 2015.8.0 1960 1961 Return information about a management_certificate 1962 1963 CLI Example: 1964 1965 .. code-block:: bash 1966 1967 salt-cloud -f get_management_certificate my-azure name=my_management_certificate \\ 1968 thumbalgorithm=sha1 thumbprint=0123456789ABCDEF 1969 """ 1970 if call != "function": 1971 raise SaltCloudSystemExit( 1972 "The get_management_certificate function must be called with -f or" 1973 " --function." 1974 ) 1975 1976 if not conn: 1977 conn = get_conn() 1978 1979 if kwargs is None: 1980 kwargs = {} 1981 1982 if "thumbprint" not in kwargs: 1983 raise SaltCloudSystemExit('A thumbprint must be specified as "thumbprint"') 1984 1985 data = conn.get_management_certificate(kwargs["thumbprint"]) 1986 return object_to_dict(data) 1987 1988 1989# For consistency with Azure SDK 1990get_management_certificate = show_management_certificate 1991 1992 1993def add_management_certificate(kwargs=None, conn=None, call=None): 1994 """ 1995 .. versionadded:: 2015.8.0 1996 1997 Add a new management certificate 1998 1999 CLI Example: 2000 2001 .. code-block:: bash 2002 2003 salt-cloud -f add_management_certificate my-azure public_key='...PUBKEY...' \\ 2004 thumbprint=0123456789ABCDEF data='...CERT_DATA...' 2005 """ 2006 if call != "function": 2007 raise SaltCloudSystemExit( 2008 "The add_management_certificate function must be called with -f or" 2009 " --function." 2010 ) 2011 2012 if not conn: 2013 conn = get_conn() 2014 2015 if kwargs is None: 2016 kwargs = {} 2017 2018 if "public_key" not in kwargs: 2019 raise SaltCloudSystemExit('A public_key must be specified as "public_key"') 2020 2021 if "thumbprint" not in kwargs: 2022 raise SaltCloudSystemExit('A thumbprint must be specified as "thumbprint"') 2023 2024 if "data" not in kwargs: 2025 raise SaltCloudSystemExit('Certificate data must be specified as "data"') 2026 2027 try: 2028 conn.add_management_certificate( 2029 kwargs["name"], 2030 kwargs["thumbprint"], 2031 kwargs["data"], 2032 ) 2033 return {"Success": "The management certificate was successfully added"} 2034 except AzureConflictHttpError: 2035 raise SaltCloudSystemExit( 2036 "There was a conflict. " 2037 "This usually means that the management certificate already exists." 2038 ) 2039 2040 2041def delete_management_certificate(kwargs=None, conn=None, call=None): 2042 """ 2043 .. versionadded:: 2015.8.0 2044 2045 Delete a specific certificate associated with the management 2046 2047 CLI Examples: 2048 2049 .. code-block:: bash 2050 2051 salt-cloud -f delete_management_certificate my-azure name=my_management_certificate \\ 2052 thumbalgorithm=sha1 thumbprint=0123456789ABCDEF 2053 """ 2054 if call != "function": 2055 raise SaltCloudSystemExit( 2056 "The delete_management_certificate function must be called with -f or" 2057 " --function." 2058 ) 2059 2060 if kwargs is None: 2061 kwargs = {} 2062 2063 if "thumbprint" not in kwargs: 2064 raise SaltCloudSystemExit('A thumbprint must be specified as "thumbprint"') 2065 2066 if not conn: 2067 conn = get_conn() 2068 2069 try: 2070 conn.delete_management_certificate(kwargs["thumbprint"]) 2071 return {"Success": "The management certificate was successfully deleted"} 2072 except AzureMissingResourceHttpError as exc: 2073 raise SaltCloudSystemExit("{}: {}".format(kwargs["thumbprint"], exc.message)) 2074 2075 2076def list_virtual_networks(kwargs=None, conn=None, call=None): 2077 """ 2078 .. versionadded:: 2015.8.0 2079 2080 List input endpoints associated with the deployment 2081 2082 CLI Example: 2083 2084 .. code-block:: bash 2085 2086 salt-cloud -f list_virtual_networks my-azure service=myservice deployment=mydeployment 2087 """ 2088 if call != "function": 2089 raise SaltCloudSystemExit( 2090 "The list_virtual_networks function must be called with -f or --function." 2091 ) 2092 2093 path = "services/networking/virtualnetwork" 2094 data = query(path) 2095 return data 2096 2097 2098def list_input_endpoints(kwargs=None, conn=None, call=None): 2099 """ 2100 .. versionadded:: 2015.8.0 2101 2102 List input endpoints associated with the deployment 2103 2104 CLI Example: 2105 2106 .. code-block:: bash 2107 2108 salt-cloud -f list_input_endpoints my-azure service=myservice deployment=mydeployment 2109 """ 2110 if call != "function": 2111 raise SaltCloudSystemExit( 2112 "The list_input_endpoints function must be called with -f or --function." 2113 ) 2114 2115 if kwargs is None: 2116 kwargs = {} 2117 2118 if "service" not in kwargs: 2119 raise SaltCloudSystemExit('A service name must be specified as "service"') 2120 2121 if "deployment" not in kwargs: 2122 raise SaltCloudSystemExit('A deployment name must be specified as "deployment"') 2123 2124 path = "services/hostedservices/{}/deployments/{}".format( 2125 kwargs["service"], 2126 kwargs["deployment"], 2127 ) 2128 2129 data = query(path) 2130 if data is None: 2131 raise SaltCloudSystemExit( 2132 "There was an error listing endpoints with the {} service on the {}" 2133 " deployment.".format(kwargs["service"], kwargs["deployment"]) 2134 ) 2135 2136 ret = {} 2137 for item in data: 2138 if "Role" in item: 2139 role = item["Role"] 2140 if not isinstance(role, dict): 2141 return ret 2142 input_endpoint = ( 2143 role["ConfigurationSets"]["ConfigurationSet"] 2144 .get("InputEndpoints", {}) 2145 .get("InputEndpoint") 2146 ) 2147 if not input_endpoint: 2148 continue 2149 if not isinstance(input_endpoint, list): 2150 input_endpoint = [input_endpoint] 2151 for endpoint in input_endpoint: 2152 ret[endpoint["Name"]] = endpoint 2153 return ret 2154 return ret 2155 2156 2157def show_input_endpoint(kwargs=None, conn=None, call=None): 2158 """ 2159 .. versionadded:: 2015.8.0 2160 2161 Show an input endpoint associated with the deployment 2162 2163 CLI Example: 2164 2165 .. code-block:: bash 2166 2167 salt-cloud -f show_input_endpoint my-azure service=myservice \\ 2168 deployment=mydeployment name=SSH 2169 """ 2170 if call != "function": 2171 raise SaltCloudSystemExit( 2172 "The show_input_endpoint function must be called with -f or --function." 2173 ) 2174 2175 if kwargs is None: 2176 kwargs = {} 2177 2178 if "name" not in kwargs: 2179 raise SaltCloudSystemExit('An endpoint name must be specified as "name"') 2180 2181 data = list_input_endpoints(kwargs=kwargs, call="function") 2182 return data.get(kwargs["name"], None) 2183 2184 2185# For consistency with Azure SDK 2186get_input_endpoint = show_input_endpoint 2187 2188 2189def update_input_endpoint(kwargs=None, conn=None, call=None, activity="update"): 2190 """ 2191 .. versionadded:: 2015.8.0 2192 2193 Update an input endpoint associated with the deployment. Please note that 2194 there may be a delay before the changes show up. 2195 2196 CLI Example: 2197 2198 .. code-block:: bash 2199 2200 salt-cloud -f update_input_endpoint my-azure service=myservice \\ 2201 deployment=mydeployment role=myrole name=HTTP local_port=80 \\ 2202 port=80 protocol=tcp enable_direct_server_return=False \\ 2203 timeout_for_tcp_idle_connection=4 2204 """ 2205 if call != "function": 2206 raise SaltCloudSystemExit( 2207 "The update_input_endpoint function must be called with -f or --function." 2208 ) 2209 2210 if kwargs is None: 2211 kwargs = {} 2212 2213 if "service" not in kwargs: 2214 raise SaltCloudSystemExit('A service name must be specified as "service"') 2215 2216 if "deployment" not in kwargs: 2217 raise SaltCloudSystemExit('A deployment name must be specified as "deployment"') 2218 2219 if "name" not in kwargs: 2220 raise SaltCloudSystemExit('An endpoint name must be specified as "name"') 2221 2222 if "role" not in kwargs: 2223 raise SaltCloudSystemExit('An role name must be specified as "role"') 2224 2225 if activity != "delete": 2226 if "port" not in kwargs: 2227 raise SaltCloudSystemExit('An endpoint port must be specified as "port"') 2228 2229 if "protocol" not in kwargs: 2230 raise SaltCloudSystemExit( 2231 'An endpoint protocol (tcp or udp) must be specified as "protocol"' 2232 ) 2233 2234 if "local_port" not in kwargs: 2235 kwargs["local_port"] = kwargs["port"] 2236 2237 if "enable_direct_server_return" not in kwargs: 2238 kwargs["enable_direct_server_return"] = False 2239 kwargs["enable_direct_server_return"] = str( 2240 kwargs["enable_direct_server_return"] 2241 ).lower() 2242 2243 if "timeout_for_tcp_idle_connection" not in kwargs: 2244 kwargs["timeout_for_tcp_idle_connection"] = 4 2245 2246 old_endpoints = list_input_endpoints(kwargs, call="function") 2247 2248 endpoints_xml = "" 2249 endpoint_xml = """ 2250 <InputEndpoint> 2251 <LocalPort>{local_port}</LocalPort> 2252 <Name>{name}</Name> 2253 <Port>{port}</Port> 2254 <Protocol>{protocol}</Protocol> 2255 <EnableDirectServerReturn>{enable_direct_server_return}</EnableDirectServerReturn> 2256 <IdleTimeoutInMinutes>{timeout_for_tcp_idle_connection}</IdleTimeoutInMinutes> 2257 </InputEndpoint>""" 2258 2259 if activity == "add": 2260 old_endpoints[kwargs["name"]] = kwargs 2261 old_endpoints[kwargs["name"]]["Name"] = kwargs["name"] 2262 2263 for endpoint in old_endpoints: 2264 if old_endpoints[endpoint]["Name"] == kwargs["name"]: 2265 if activity != "delete": 2266 this_endpoint_xml = endpoint_xml.format(**kwargs) 2267 endpoints_xml += this_endpoint_xml 2268 else: 2269 this_endpoint_xml = endpoint_xml.format( 2270 local_port=old_endpoints[endpoint]["LocalPort"], 2271 name=old_endpoints[endpoint]["Name"], 2272 port=old_endpoints[endpoint]["Port"], 2273 protocol=old_endpoints[endpoint]["Protocol"], 2274 enable_direct_server_return=old_endpoints[endpoint][ 2275 "EnableDirectServerReturn" 2276 ], 2277 timeout_for_tcp_idle_connection=old_endpoints[endpoint].get( 2278 "IdleTimeoutInMinutes", 4 2279 ), 2280 ) 2281 endpoints_xml += this_endpoint_xml 2282 2283 request_xml = """<PersistentVMRole xmlns="http://schemas.microsoft.com/windowsazure" 2284xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> 2285 <ConfigurationSets> 2286 <ConfigurationSet> 2287 <ConfigurationSetType>NetworkConfiguration</ConfigurationSetType> 2288 <InputEndpoints>{} 2289 </InputEndpoints> 2290 </ConfigurationSet> 2291 </ConfigurationSets> 2292 <OSVirtualHardDisk> 2293 </OSVirtualHardDisk> 2294</PersistentVMRole>""".format( 2295 endpoints_xml 2296 ) 2297 2298 path = "services/hostedservices/{}/deployments/{}/roles/{}".format( 2299 kwargs["service"], 2300 kwargs["deployment"], 2301 kwargs["role"], 2302 ) 2303 query( 2304 path=path, 2305 method="PUT", 2306 header_dict={"Content-Type": "application/xml"}, 2307 data=request_xml, 2308 decode=False, 2309 ) 2310 return True 2311 2312 2313def add_input_endpoint(kwargs=None, conn=None, call=None): 2314 """ 2315 .. versionadded:: 2015.8.0 2316 2317 Add an input endpoint to the deployment. Please note that 2318 there may be a delay before the changes show up. 2319 2320 CLI Example: 2321 2322 .. code-block:: bash 2323 2324 salt-cloud -f add_input_endpoint my-azure service=myservice \\ 2325 deployment=mydeployment role=myrole name=HTTP local_port=80 \\ 2326 port=80 protocol=tcp enable_direct_server_return=False \\ 2327 timeout_for_tcp_idle_connection=4 2328 """ 2329 return update_input_endpoint( 2330 kwargs=kwargs, 2331 conn=conn, 2332 call="function", 2333 activity="add", 2334 ) 2335 2336 2337def delete_input_endpoint(kwargs=None, conn=None, call=None): 2338 """ 2339 .. versionadded:: 2015.8.0 2340 2341 Delete an input endpoint from the deployment. Please note that 2342 there may be a delay before the changes show up. 2343 2344 CLI Example: 2345 2346 .. code-block:: bash 2347 2348 salt-cloud -f delete_input_endpoint my-azure service=myservice \\ 2349 deployment=mydeployment role=myrole name=HTTP 2350 """ 2351 return update_input_endpoint( 2352 kwargs=kwargs, 2353 conn=conn, 2354 call="function", 2355 activity="delete", 2356 ) 2357 2358 2359def show_deployment(kwargs=None, conn=None, call=None): 2360 """ 2361 .. versionadded:: 2015.8.0 2362 2363 Return information about a deployment 2364 2365 CLI Example: 2366 2367 .. code-block:: bash 2368 2369 salt-cloud -f show_deployment my-azure name=my_deployment 2370 """ 2371 if call != "function": 2372 raise SaltCloudSystemExit( 2373 "The get_deployment function must be called with -f or --function." 2374 ) 2375 2376 if not conn: 2377 conn = get_conn() 2378 2379 if kwargs is None: 2380 kwargs = {} 2381 2382 if "service_name" not in kwargs: 2383 raise SaltCloudSystemExit('A service name must be specified as "service_name"') 2384 2385 if "deployment_name" not in kwargs: 2386 raise SaltCloudSystemExit( 2387 'A deployment name must be specified as "deployment_name"' 2388 ) 2389 2390 data = conn.get_deployment_by_name( 2391 service_name=kwargs["service_name"], 2392 deployment_name=kwargs["deployment_name"], 2393 ) 2394 return object_to_dict(data) 2395 2396 2397# For consistency with Azure SDK 2398get_deployment = show_deployment 2399 2400 2401def list_affinity_groups(kwargs=None, conn=None, call=None): 2402 """ 2403 .. versionadded:: 2015.8.0 2404 2405 List input endpoints associated with the deployment 2406 2407 CLI Example: 2408 2409 .. code-block:: bash 2410 2411 salt-cloud -f list_affinity_groups my-azure 2412 """ 2413 if call != "function": 2414 raise SaltCloudSystemExit( 2415 "The list_affinity_groups function must be called with -f or --function." 2416 ) 2417 2418 if not conn: 2419 conn = get_conn() 2420 2421 data = conn.list_affinity_groups() 2422 ret = {} 2423 for item in data.affinity_groups: 2424 ret[item.name] = object_to_dict(item) 2425 return ret 2426 2427 2428def show_affinity_group(kwargs=None, conn=None, call=None): 2429 """ 2430 .. versionadded:: 2015.8.0 2431 2432 Show an affinity group associated with the account 2433 2434 CLI Example: 2435 2436 .. code-block:: bash 2437 2438 salt-cloud -f show_affinity_group my-azure service=myservice \\ 2439 deployment=mydeployment name=SSH 2440 """ 2441 if call != "function": 2442 raise SaltCloudSystemExit( 2443 "The show_affinity_group function must be called with -f or --function." 2444 ) 2445 2446 if not conn: 2447 conn = get_conn() 2448 2449 if kwargs is None: 2450 kwargs = {} 2451 2452 if "name" not in kwargs: 2453 raise SaltCloudSystemExit('An affinity group name must be specified as "name"') 2454 2455 data = conn.get_affinity_group_properties(affinity_group_name=kwargs["name"]) 2456 return object_to_dict(data) 2457 2458 2459# For consistency with Azure SDK 2460get_affinity_group = show_affinity_group 2461 2462 2463def create_affinity_group(kwargs=None, conn=None, call=None): 2464 """ 2465 .. versionadded:: 2015.8.0 2466 2467 Create a new affinity group 2468 2469 CLI Example: 2470 2471 .. code-block:: bash 2472 2473 salt-cloud -f create_affinity_group my-azure name=my_affinity_group 2474 """ 2475 if call != "function": 2476 raise SaltCloudSystemExit( 2477 "The create_affinity_group function must be called with -f or --function." 2478 ) 2479 2480 if not conn: 2481 conn = get_conn() 2482 2483 if kwargs is None: 2484 kwargs = {} 2485 2486 if "name" not in kwargs: 2487 raise SaltCloudSystemExit('A name must be specified as "name"') 2488 2489 if "label" not in kwargs: 2490 raise SaltCloudSystemExit('A label must be specified as "label"') 2491 2492 if "location" not in kwargs: 2493 raise SaltCloudSystemExit('A location must be specified as "location"') 2494 2495 try: 2496 conn.create_affinity_group( 2497 kwargs["name"], 2498 kwargs["label"], 2499 kwargs["location"], 2500 kwargs.get("description", None), 2501 ) 2502 return {"Success": "The affinity group was successfully created"} 2503 except AzureConflictHttpError: 2504 raise SaltCloudSystemExit( 2505 "There was a conflict. This usually means that the affinity group already" 2506 " exists." 2507 ) 2508 2509 2510def update_affinity_group(kwargs=None, conn=None, call=None): 2511 """ 2512 .. versionadded:: 2015.8.0 2513 2514 Update an affinity group's properties 2515 2516 CLI Example: 2517 2518 .. code-block:: bash 2519 2520 salt-cloud -f update_affinity_group my-azure name=my_group label=my_group 2521 """ 2522 if call != "function": 2523 raise SaltCloudSystemExit( 2524 "The update_affinity_group function must be called with -f or --function." 2525 ) 2526 2527 if not conn: 2528 conn = get_conn() 2529 2530 if kwargs is None: 2531 kwargs = {} 2532 2533 if "name" not in kwargs: 2534 raise SaltCloudSystemExit('A name must be specified as "name"') 2535 2536 if "label" not in kwargs: 2537 raise SaltCloudSystemExit('A label must be specified as "label"') 2538 2539 conn.update_affinity_group( 2540 affinity_group_name=kwargs["name"], 2541 label=kwargs["label"], 2542 description=kwargs.get("description", None), 2543 ) 2544 return show_affinity_group(kwargs={"name": kwargs["name"]}, call="function") 2545 2546 2547def delete_affinity_group(kwargs=None, conn=None, call=None): 2548 """ 2549 .. versionadded:: 2015.8.0 2550 2551 Delete a specific affinity group associated with the account 2552 2553 CLI Examples: 2554 2555 .. code-block:: bash 2556 2557 salt-cloud -f delete_affinity_group my-azure name=my_affinity_group 2558 """ 2559 if call != "function": 2560 raise SaltCloudSystemExit( 2561 "The delete_affinity_group function must be called with -f or --function." 2562 ) 2563 2564 if kwargs is None: 2565 kwargs = {} 2566 2567 if "name" not in kwargs: 2568 raise SaltCloudSystemExit('A name must be specified as "name"') 2569 2570 if not conn: 2571 conn = get_conn() 2572 2573 try: 2574 conn.delete_affinity_group(kwargs["name"]) 2575 return {"Success": "The affinity group was successfully deleted"} 2576 except AzureMissingResourceHttpError as exc: 2577 raise SaltCloudSystemExit("{}: {}".format(kwargs["name"], exc.message)) 2578 2579 2580def get_storage_conn(storage_account=None, storage_key=None, conn_kwargs=None): 2581 """ 2582 .. versionadded:: 2015.8.0 2583 2584 Return a storage_conn object for the storage account 2585 """ 2586 if conn_kwargs is None: 2587 conn_kwargs = {} 2588 2589 if not storage_account: 2590 storage_account = config.get_cloud_config_value( 2591 "storage_account", 2592 get_configured_provider(), 2593 __opts__, 2594 search_global=False, 2595 default=conn_kwargs.get("storage_account", None), 2596 ) 2597 if not storage_key: 2598 storage_key = config.get_cloud_config_value( 2599 "storage_key", 2600 get_configured_provider(), 2601 __opts__, 2602 search_global=False, 2603 default=conn_kwargs.get("storage_key", None), 2604 ) 2605 return azure.storage.BlobService(storage_account, storage_key) 2606 2607 2608def make_blob_url(kwargs=None, storage_conn=None, call=None): 2609 """ 2610 .. versionadded:: 2015.8.0 2611 2612 Creates the URL to access a blob 2613 2614 CLI Example: 2615 2616 .. code-block:: bash 2617 2618 salt-cloud -f make_blob_url my-azure container=mycontainer blob=myblob 2619 2620 container: 2621 Name of the container. 2622 blob: 2623 Name of the blob. 2624 account: 2625 Name of the storage account. If not specified, derives the host base 2626 from the provider configuration. 2627 protocol: 2628 Protocol to use: 'http' or 'https'. If not specified, derives the host 2629 base from the provider configuration. 2630 host_base: 2631 Live host base URL. If not specified, derives the host base from the 2632 provider configuration. 2633 """ 2634 if call != "function": 2635 raise SaltCloudSystemExit( 2636 "The make_blob_url function must be called with -f or --function." 2637 ) 2638 2639 if kwargs is None: 2640 kwargs = {} 2641 2642 if "container" not in kwargs: 2643 raise SaltCloudSystemExit('A container name must be specified as "container"') 2644 2645 if "blob" not in kwargs: 2646 raise SaltCloudSystemExit('A blob name must be specified as "blob"') 2647 2648 if not storage_conn: 2649 storage_conn = get_storage_conn(conn_kwargs=kwargs) 2650 2651 data = storage_conn.make_blob_url( 2652 kwargs["container"], 2653 kwargs["blob"], 2654 kwargs.get("account", None), 2655 kwargs.get("protocol", None), 2656 kwargs.get("host_base", None), 2657 ) 2658 ret = {} 2659 for item in data.containers: 2660 ret[item.name] = object_to_dict(item) 2661 return ret 2662 2663 2664def list_storage_containers(kwargs=None, storage_conn=None, call=None): 2665 """ 2666 .. versionadded:: 2015.8.0 2667 2668 List containers associated with the storage account 2669 2670 CLI Example: 2671 2672 .. code-block:: bash 2673 2674 salt-cloud -f list_storage_containers my-azure 2675 """ 2676 if call != "function": 2677 raise SaltCloudSystemExit( 2678 "The list_storage_containers function must be called with -f or --function." 2679 ) 2680 2681 if not storage_conn: 2682 storage_conn = get_storage_conn(conn_kwargs=kwargs) 2683 2684 data = storage_conn.list_containers() 2685 ret = {} 2686 for item in data.containers: 2687 ret[item.name] = object_to_dict(item) 2688 return ret 2689 2690 2691def create_storage_container(kwargs=None, storage_conn=None, call=None): 2692 """ 2693 .. versionadded:: 2015.8.0 2694 2695 Create a storage container 2696 2697 CLI Example: 2698 2699 .. code-block:: bash 2700 2701 salt-cloud -f create_storage_container my-azure name=mycontainer 2702 2703 name: 2704 Name of container to create. 2705 meta_name_values: 2706 Optional. A dict with name_value pairs to associate with the 2707 container as metadata. Example:{'Category':'test'} 2708 blob_public_access: 2709 Optional. Possible values include: container, blob 2710 fail_on_exist: 2711 Specify whether to throw an exception when the container exists. 2712 """ 2713 if call != "function": 2714 raise SaltCloudSystemExit( 2715 "The create_storage_container function must be called with -f or" 2716 " --function." 2717 ) 2718 2719 if not storage_conn: 2720 storage_conn = get_storage_conn(conn_kwargs=kwargs) 2721 2722 try: 2723 storage_conn.create_container( 2724 container_name=kwargs["name"], 2725 x_ms_meta_name_values=kwargs.get("meta_name_values", None), 2726 x_ms_blob_public_access=kwargs.get("blob_public_access", None), 2727 fail_on_exist=kwargs.get("fail_on_exist", False), 2728 ) 2729 return {"Success": "The storage container was successfully created"} 2730 except AzureConflictHttpError: 2731 raise SaltCloudSystemExit( 2732 "There was a conflict. This usually means that the storage container" 2733 " already exists." 2734 ) 2735 2736 2737def show_storage_container(kwargs=None, storage_conn=None, call=None): 2738 """ 2739 .. versionadded:: 2015.8.0 2740 2741 Show a container associated with the storage account 2742 2743 CLI Example: 2744 2745 .. code-block:: bash 2746 2747 salt-cloud -f show_storage_container my-azure name=myservice 2748 2749 name: 2750 Name of container to show. 2751 """ 2752 if call != "function": 2753 raise SaltCloudSystemExit( 2754 "The show_storage_container function must be called with -f or --function." 2755 ) 2756 2757 if kwargs is None: 2758 kwargs = {} 2759 2760 if "name" not in kwargs: 2761 raise SaltCloudSystemExit( 2762 'An storage container name must be specified as "name"' 2763 ) 2764 2765 if not storage_conn: 2766 storage_conn = get_storage_conn(conn_kwargs=kwargs) 2767 2768 data = storage_conn.get_container_properties( 2769 container_name=kwargs["name"], 2770 x_ms_lease_id=kwargs.get("lease_id", None), 2771 ) 2772 return data 2773 2774 2775# For consistency with Azure SDK 2776get_storage_container = show_storage_container 2777 2778 2779def show_storage_container_metadata(kwargs=None, storage_conn=None, call=None): 2780 """ 2781 .. versionadded:: 2015.8.0 2782 2783 Show a storage container's metadata 2784 2785 CLI Example: 2786 2787 .. code-block:: bash 2788 2789 salt-cloud -f show_storage_container_metadata my-azure name=myservice 2790 2791 name: 2792 Name of container to show. 2793 lease_id: 2794 If specified, show_storage_container_metadata only succeeds if the 2795 container's lease is active and matches this ID. 2796 """ 2797 if call != "function": 2798 raise SaltCloudSystemExit( 2799 "The show_storage_container function must be called with -f or --function." 2800 ) 2801 2802 if kwargs is None: 2803 kwargs = {} 2804 2805 if "name" not in kwargs: 2806 raise SaltCloudSystemExit( 2807 'An storage container name must be specified as "name"' 2808 ) 2809 2810 if not storage_conn: 2811 storage_conn = get_storage_conn(conn_kwargs=kwargs) 2812 2813 data = storage_conn.get_container_metadata( 2814 container_name=kwargs["name"], 2815 x_ms_lease_id=kwargs.get("lease_id", None), 2816 ) 2817 return data 2818 2819 2820# For consistency with Azure SDK 2821get_storage_container_metadata = show_storage_container_metadata 2822 2823 2824def set_storage_container_metadata(kwargs=None, storage_conn=None, call=None): 2825 """ 2826 .. versionadded:: 2015.8.0 2827 2828 Set a storage container's metadata 2829 2830 CLI Example: 2831 2832 .. code-block:: bash 2833 2834 salt-cloud -f set_storage_container my-azure name=mycontainer \\ 2835 x_ms_meta_name_values='{"my_name": "my_value"}' 2836 2837 name: 2838 Name of existing container. 2839 meta_name_values: 2840 A dict containing name, value for metadata. 2841 Example: {'category':'test'} 2842 lease_id: 2843 If specified, set_storage_container_metadata only succeeds if the 2844 container's lease is active and matches this ID. 2845 """ 2846 if call != "function": 2847 raise SaltCloudSystemExit( 2848 "The create_storage_container function must be called with -f or" 2849 " --function." 2850 ) 2851 2852 if kwargs is None: 2853 kwargs = {} 2854 2855 if "name" not in kwargs: 2856 raise SaltCloudSystemExit( 2857 'An storage container name must be specified as "name"' 2858 ) 2859 2860 x_ms_meta_name_values = salt.utils.yaml.safe_load( 2861 kwargs.get("meta_name_values", "") 2862 ) 2863 2864 if not storage_conn: 2865 storage_conn = get_storage_conn(conn_kwargs=kwargs) 2866 2867 try: 2868 storage_conn.set_container_metadata( 2869 container_name=kwargs["name"], 2870 x_ms_meta_name_values=x_ms_meta_name_values, 2871 x_ms_lease_id=kwargs.get("lease_id", None), 2872 ) 2873 return {"Success": "The storage container was successfully updated"} 2874 except AzureConflictHttpError: 2875 raise SaltCloudSystemExit("There was a conflict.") 2876 2877 2878def show_storage_container_acl(kwargs=None, storage_conn=None, call=None): 2879 """ 2880 .. versionadded:: 2015.8.0 2881 2882 Show a storage container's acl 2883 2884 CLI Example: 2885 2886 .. code-block:: bash 2887 2888 salt-cloud -f show_storage_container_acl my-azure name=myservice 2889 2890 name: 2891 Name of existing container. 2892 lease_id: 2893 If specified, show_storage_container_acl only succeeds if the 2894 container's lease is active and matches this ID. 2895 """ 2896 if call != "function": 2897 raise SaltCloudSystemExit( 2898 "The show_storage_container function must be called with -f or --function." 2899 ) 2900 2901 if kwargs is None: 2902 kwargs = {} 2903 2904 if "name" not in kwargs: 2905 raise SaltCloudSystemExit( 2906 'An storage container name must be specified as "name"' 2907 ) 2908 2909 if not storage_conn: 2910 storage_conn = get_storage_conn(conn_kwargs=kwargs) 2911 2912 data = storage_conn.get_container_acl( 2913 container_name=kwargs["name"], 2914 x_ms_lease_id=kwargs.get("lease_id", None), 2915 ) 2916 return data 2917 2918 2919# For consistency with Azure SDK 2920get_storage_container_acl = show_storage_container_acl 2921 2922 2923def set_storage_container_acl(kwargs=None, storage_conn=None, call=None): 2924 """ 2925 .. versionadded:: 2015.8.0 2926 2927 Set a storage container's acl 2928 2929 CLI Example: 2930 2931 .. code-block:: bash 2932 2933 salt-cloud -f set_storage_container my-azure name=mycontainer 2934 2935 name: 2936 Name of existing container. 2937 signed_identifiers: 2938 SignedIdentifers instance 2939 blob_public_access: 2940 Optional. Possible values include: container, blob 2941 lease_id: 2942 If specified, set_storage_container_acl only succeeds if the 2943 container's lease is active and matches this ID. 2944 """ 2945 if call != "function": 2946 raise SaltCloudSystemExit( 2947 "The create_storage_container function must be called with -f or" 2948 " --function." 2949 ) 2950 2951 if not storage_conn: 2952 storage_conn = get_storage_conn(conn_kwargs=kwargs) 2953 2954 try: 2955 data = storage_conn.set_container_acl( 2956 container_name=kwargs["name"], 2957 signed_identifiers=kwargs.get("signed_identifiers", None), 2958 x_ms_blob_public_access=kwargs.get("blob_public_access", None), 2959 x_ms_lease_id=kwargs.get("lease_id", None), 2960 ) 2961 return {"Success": "The storage container was successfully updated"} 2962 except AzureConflictHttpError: 2963 raise SaltCloudSystemExit("There was a conflict.") 2964 2965 2966def delete_storage_container(kwargs=None, storage_conn=None, call=None): 2967 """ 2968 .. versionadded:: 2015.8.0 2969 2970 Delete a container associated with the storage account 2971 2972 CLI Example: 2973 2974 .. code-block:: bash 2975 2976 salt-cloud -f delete_storage_container my-azure name=mycontainer 2977 2978 name: 2979 Name of container to create. 2980 fail_not_exist: 2981 Specify whether to throw an exception when the container exists. 2982 lease_id: 2983 If specified, delete_storage_container only succeeds if the 2984 container's lease is active and matches this ID. 2985 """ 2986 if call != "function": 2987 raise SaltCloudSystemExit( 2988 "The delete_storage_container function must be called with -f or" 2989 " --function." 2990 ) 2991 2992 if kwargs is None: 2993 kwargs = {} 2994 2995 if "name" not in kwargs: 2996 raise SaltCloudSystemExit( 2997 'An storage container name must be specified as "name"' 2998 ) 2999 3000 if not storage_conn: 3001 storage_conn = get_storage_conn(conn_kwargs=kwargs) 3002 3003 data = storage_conn.delete_container( 3004 container_name=kwargs["name"], 3005 fail_not_exist=kwargs.get("fail_not_exist", None), 3006 x_ms_lease_id=kwargs.get("lease_id", None), 3007 ) 3008 return data 3009 3010 3011def lease_storage_container(kwargs=None, storage_conn=None, call=None): 3012 """ 3013 .. versionadded:: 2015.8.0 3014 3015 Lease a container associated with the storage account 3016 3017 CLI Example: 3018 3019 .. code-block:: bash 3020 3021 salt-cloud -f lease_storage_container my-azure name=mycontainer 3022 3023 name: 3024 Name of container to create. 3025 lease_action: 3026 Required. Possible values: acquire|renew|release|break|change 3027 lease_id: 3028 Required if the container has an active lease. 3029 lease_duration: 3030 Specifies the duration of the lease, in seconds, or negative one 3031 (-1) for a lease that never expires. A non-infinite lease can be 3032 between 15 and 60 seconds. A lease duration cannot be changed 3033 using renew or change. For backwards compatibility, the default is 3034 60, and the value is only used on an acquire operation. 3035 lease_break_period: 3036 Optional. For a break operation, this is the proposed duration of 3037 seconds that the lease should continue before it is broken, between 3038 0 and 60 seconds. This break period is only used if it is shorter 3039 than the time remaining on the lease. If longer, the time remaining 3040 on the lease is used. A new lease will not be available before the 3041 break period has expired, but the lease may be held for longer than 3042 the break period. If this header does not appear with a break 3043 operation, a fixed-duration lease breaks after the remaining lease 3044 period elapses, and an infinite lease breaks immediately. 3045 proposed_lease_id: 3046 Optional for acquire, required for change. Proposed lease ID, in a 3047 GUID string format. 3048 """ 3049 if call != "function": 3050 raise SaltCloudSystemExit( 3051 "The lease_storage_container function must be called with -f or --function." 3052 ) 3053 3054 if kwargs is None: 3055 kwargs = {} 3056 3057 if "name" not in kwargs: 3058 raise SaltCloudSystemExit( 3059 'An storage container name must be specified as "name"' 3060 ) 3061 3062 lease_actions = ("acquire", "renew", "release", "break", "change") 3063 3064 if kwargs.get("lease_action", None) not in lease_actions: 3065 raise SaltCloudSystemExit( 3066 "A lease_action must be one of: {}".format(", ".join(lease_actions)) 3067 ) 3068 3069 if kwargs["lease_action"] != "acquire" and "lease_id" not in kwargs: 3070 raise SaltCloudSystemExit( 3071 'A lease ID must be specified for the "{}" lease action ' 3072 'as "lease_id"'.format(kwargs["lease_action"]) 3073 ) 3074 3075 if not storage_conn: 3076 storage_conn = get_storage_conn(conn_kwargs=kwargs) 3077 3078 data = storage_conn.lease_container( 3079 container_name=kwargs["name"], 3080 x_ms_lease_action=kwargs["lease_action"], 3081 x_ms_lease_id=kwargs.get("lease_id", None), 3082 x_ms_lease_duration=kwargs.get("lease_duration", 60), 3083 x_ms_lease_break_period=kwargs.get("lease_break_period", None), 3084 x_ms_proposed_lease_id=kwargs.get("proposed_lease_id", None), 3085 ) 3086 3087 return data 3088 3089 3090def list_blobs(kwargs=None, storage_conn=None, call=None): 3091 """ 3092 .. versionadded:: 2015.8.0 3093 3094 List blobs associated with the container 3095 3096 CLI Example: 3097 3098 .. code-block:: bash 3099 3100 salt-cloud -f list_blobs my-azure container=mycontainer 3101 3102 container: 3103 The name of the storage container 3104 prefix: 3105 Optional. Filters the results to return only blobs whose names 3106 begin with the specified prefix. 3107 marker: 3108 Optional. A string value that identifies the portion of the list 3109 to be returned with the next list operation. The operation returns 3110 a marker value within the response body if the list returned was 3111 not complete. The marker value may then be used in a subsequent 3112 call to request the next set of list items. The marker value is 3113 opaque to the client. 3114 maxresults: 3115 Optional. Specifies the maximum number of blobs to return, 3116 including all BlobPrefix elements. If the request does not specify 3117 maxresults or specifies a value greater than 5,000, the server will 3118 return up to 5,000 items. Setting maxresults to a value less than 3119 or equal to zero results in error response code 400 (Bad Request). 3120 include: 3121 Optional. Specifies one or more datasets to include in the 3122 response. To specify more than one of these options on the URI, 3123 you must separate each option with a comma. Valid values are: 3124 3125 snapshots: 3126 Specifies that snapshots should be included in the 3127 enumeration. Snapshots are listed from oldest to newest in 3128 the response. 3129 metadata: 3130 Specifies that blob metadata be returned in the response. 3131 uncommittedblobs: 3132 Specifies that blobs for which blocks have been uploaded, 3133 but which have not been committed using Put Block List 3134 (REST API), be included in the response. 3135 copy: 3136 Version 2012-02-12 and newer. Specifies that metadata 3137 related to any current or previous Copy Blob operation 3138 should be included in the response. 3139 delimiter: 3140 Optional. When the request includes this parameter, the operation 3141 returns a BlobPrefix element in the response body that acts as a 3142 placeholder for all blobs whose names begin with the same 3143 substring up to the appearance of the delimiter character. The 3144 delimiter may be a single character or a string. 3145 """ 3146 if call != "function": 3147 raise SaltCloudSystemExit( 3148 "The list_blobs function must be called with -f or --function." 3149 ) 3150 3151 if kwargs is None: 3152 kwargs = {} 3153 3154 if "container" not in kwargs: 3155 raise SaltCloudSystemExit( 3156 'An storage container name must be specified as "container"' 3157 ) 3158 3159 if not storage_conn: 3160 storage_conn = get_storage_conn(conn_kwargs=kwargs) 3161 3162 return salt.utils.msazure.list_blobs(storage_conn=storage_conn, **kwargs) 3163 3164 3165def show_blob_service_properties(kwargs=None, storage_conn=None, call=None): 3166 """ 3167 .. versionadded:: 2015.8.0 3168 3169 Show a blob's service properties 3170 3171 CLI Example: 3172 3173 .. code-block:: bash 3174 3175 salt-cloud -f show_blob_service_properties my-azure 3176 """ 3177 if call != "function": 3178 raise SaltCloudSystemExit( 3179 "The show_blob_service_properties function must be called with -f or" 3180 " --function." 3181 ) 3182 3183 if not storage_conn: 3184 storage_conn = get_storage_conn(conn_kwargs=kwargs) 3185 3186 data = storage_conn.get_blob_service_properties( 3187 timeout=kwargs.get("timeout", None), 3188 ) 3189 return data 3190 3191 3192# For consistency with Azure SDK 3193get_blob_service_properties = show_blob_service_properties 3194 3195 3196def set_blob_service_properties(kwargs=None, storage_conn=None, call=None): 3197 """ 3198 .. versionadded:: 2015.8.0 3199 3200 Sets the properties of a storage account's Blob service, including 3201 Windows Azure Storage Analytics. You can also use this operation to 3202 set the default request version for all incoming requests that do not 3203 have a version specified. 3204 3205 CLI Example: 3206 3207 .. code-block:: bash 3208 3209 salt-cloud -f set_blob_service_properties my-azure 3210 3211 properties: 3212 a StorageServiceProperties object. 3213 timeout: 3214 Optional. The timeout parameter is expressed in seconds. 3215 """ 3216 if call != "function": 3217 raise SaltCloudSystemExit( 3218 "The set_blob_service_properties function must be called with -f or" 3219 " --function." 3220 ) 3221 3222 if kwargs is None: 3223 kwargs = {} 3224 3225 if "properties" not in kwargs: 3226 raise SaltCloudSystemExit( 3227 'The blob service properties name must be specified as "properties"' 3228 ) 3229 3230 if not storage_conn: 3231 storage_conn = get_storage_conn(conn_kwargs=kwargs) 3232 3233 data = storage_conn.get_blob_service_properties( 3234 storage_service_properties=kwargs["properties"], 3235 timeout=kwargs.get("timeout", None), 3236 ) 3237 return data 3238 3239 3240def show_blob_properties(kwargs=None, storage_conn=None, call=None): 3241 """ 3242 .. versionadded:: 2015.8.0 3243 3244 Returns all user-defined metadata, standard HTTP properties, and 3245 system properties for the blob. 3246 3247 CLI Example: 3248 3249 .. code-block:: bash 3250 3251 salt-cloud -f show_blob_properties my-azure container=mycontainer blob=myblob 3252 3253 container: 3254 Name of existing container. 3255 blob: 3256 Name of existing blob. 3257 lease_id: 3258 Required if the blob has an active lease. 3259 """ 3260 if call != "function": 3261 raise SaltCloudSystemExit( 3262 "The show_blob_properties function must be called with -f or --function." 3263 ) 3264 3265 if kwargs is None: 3266 kwargs = {} 3267 3268 if "container" not in kwargs: 3269 raise SaltCloudSystemExit('The container name must be specified as "container"') 3270 3271 if "blob" not in kwargs: 3272 raise SaltCloudSystemExit('The blob name must be specified as "blob"') 3273 3274 if not storage_conn: 3275 storage_conn = get_storage_conn(conn_kwargs=kwargs) 3276 3277 try: 3278 data = storage_conn.get_blob_properties( 3279 container_name=kwargs["container"], 3280 blob_name=kwargs["blob"], 3281 x_ms_lease_id=kwargs.get("lease_id", None), 3282 ) 3283 except AzureMissingResourceHttpError: 3284 raise SaltCloudSystemExit("The specified blob does not exist.") 3285 3286 return data 3287 3288 3289# For consistency with Azure SDK 3290get_blob_properties = show_blob_properties 3291 3292 3293def set_blob_properties(kwargs=None, storage_conn=None, call=None): 3294 """ 3295 .. versionadded:: 2015.8.0 3296 3297 Set a blob's properties 3298 3299 CLI Example: 3300 3301 .. code-block:: bash 3302 3303 salt-cloud -f set_blob_properties my-azure 3304 3305 container: 3306 Name of existing container. 3307 blob: 3308 Name of existing blob. 3309 blob_cache_control: 3310 Optional. Modifies the cache control string for the blob. 3311 blob_content_type: 3312 Optional. Sets the blob's content type. 3313 blob_content_md5: 3314 Optional. Sets the blob's MD5 hash. 3315 blob_content_encoding: 3316 Optional. Sets the blob's content encoding. 3317 blob_content_language: 3318 Optional. Sets the blob's content language. 3319 lease_id: 3320 Required if the blob has an active lease. 3321 blob_content_disposition: 3322 Optional. Sets the blob's Content-Disposition header. 3323 The Content-Disposition response header field conveys additional 3324 information about how to process the response payload, and also can 3325 be used to attach additional metadata. For example, if set to 3326 attachment, it indicates that the user-agent should not display the 3327 response, but instead show a Save As dialog with a filename other 3328 than the blob name specified. 3329 """ 3330 if call != "function": 3331 raise SaltCloudSystemExit( 3332 "The set_blob_properties function must be called with -f or --function." 3333 ) 3334 3335 if kwargs is None: 3336 kwargs = {} 3337 3338 if "container" not in kwargs: 3339 raise SaltCloudSystemExit( 3340 'The blob container name must be specified as "container"' 3341 ) 3342 3343 if "blob" not in kwargs: 3344 raise SaltCloudSystemExit('The blob name must be specified as "blob"') 3345 3346 if not storage_conn: 3347 storage_conn = get_storage_conn(conn_kwargs=kwargs) 3348 3349 data = storage_conn.get_blob_properties( 3350 container_name=kwargs["container"], 3351 blob_name=kwargs["blob"], 3352 x_ms_blob_cache_control=kwargs.get("blob_cache_control", None), 3353 x_ms_blob_content_type=kwargs.get("blob_content_type", None), 3354 x_ms_blob_content_md5=kwargs.get("blob_content_md5", None), 3355 x_ms_blob_content_encoding=kwargs.get("blob_content_encoding", None), 3356 x_ms_blob_content_language=kwargs.get("blob_content_language", None), 3357 x_ms_lease_id=kwargs.get("lease_id", None), 3358 x_ms_blob_content_disposition=kwargs.get("blob_content_disposition", None), 3359 ) 3360 3361 return data 3362 3363 3364def put_blob(kwargs=None, storage_conn=None, call=None): 3365 """ 3366 .. versionadded:: 2015.8.0 3367 3368 Upload a blob 3369 3370 CLI Examples: 3371 3372 .. code-block:: bash 3373 3374 salt-cloud -f put_blob my-azure container=base name=top.sls blob_path=/srv/salt/top.sls 3375 salt-cloud -f put_blob my-azure container=base name=content.txt blob_content='Some content' 3376 3377 container: 3378 Name of existing container. 3379 name: 3380 Name of existing blob. 3381 blob_path: 3382 The path on the local machine of the file to upload as a blob. Either 3383 this or blob_content must be specified. 3384 blob_content: 3385 The actual content to be uploaded as a blob. Either this or blob_path 3386 must me specified. 3387 cache_control: 3388 Optional. The Blob service stores this value but does not use or 3389 modify it. 3390 content_language: 3391 Optional. Specifies the natural languages used by this resource. 3392 content_md5: 3393 Optional. An MD5 hash of the blob content. This hash is used to 3394 verify the integrity of the blob during transport. When this header 3395 is specified, the storage service checks the hash that has arrived 3396 with the one that was sent. If the two hashes do not match, the 3397 operation will fail with error code 400 (Bad Request). 3398 blob_content_type: 3399 Optional. Set the blob's content type. 3400 blob_content_encoding: 3401 Optional. Set the blob's content encoding. 3402 blob_content_language: 3403 Optional. Set the blob's content language. 3404 blob_content_md5: 3405 Optional. Set the blob's MD5 hash. 3406 blob_cache_control: 3407 Optional. Sets the blob's cache control. 3408 meta_name_values: 3409 A dict containing name, value for metadata. 3410 lease_id: 3411 Required if the blob has an active lease. 3412 """ 3413 if call != "function": 3414 raise SaltCloudSystemExit( 3415 "The put_blob function must be called with -f or --function." 3416 ) 3417 3418 if kwargs is None: 3419 kwargs = {} 3420 3421 if "container" not in kwargs: 3422 raise SaltCloudSystemExit( 3423 'The blob container name must be specified as "container"' 3424 ) 3425 3426 if "name" not in kwargs: 3427 raise SaltCloudSystemExit('The blob name must be specified as "name"') 3428 3429 if "blob_path" not in kwargs and "blob_content" not in kwargs: 3430 raise SaltCloudSystemExit( 3431 'Either a path to a file needs to be passed in as "blob_path" or ' 3432 'the contents of a blob as "blob_content."' 3433 ) 3434 3435 if not storage_conn: 3436 storage_conn = get_storage_conn(conn_kwargs=kwargs) 3437 3438 return salt.utils.msazure.put_blob(storage_conn=storage_conn, **kwargs) 3439 3440 3441def get_blob(kwargs=None, storage_conn=None, call=None): 3442 """ 3443 .. versionadded:: 2015.8.0 3444 3445 Download a blob 3446 3447 CLI Example: 3448 3449 .. code-block:: bash 3450 3451 salt-cloud -f get_blob my-azure container=base name=top.sls local_path=/srv/salt/top.sls 3452 salt-cloud -f get_blob my-azure container=base name=content.txt return_content=True 3453 3454 container: 3455 Name of existing container. 3456 name: 3457 Name of existing blob. 3458 local_path: 3459 The path on the local machine to download the blob to. Either this or 3460 return_content must be specified. 3461 return_content: 3462 Whether or not to return the content directly from the blob. If 3463 specified, must be True or False. Either this or the local_path must 3464 be specified. 3465 snapshot: 3466 Optional. The snapshot parameter is an opaque DateTime value that, 3467 when present, specifies the blob snapshot to retrieve. 3468 lease_id: 3469 Required if the blob has an active lease. 3470 progress_callback: 3471 callback for progress with signature function(current, total) where 3472 current is the number of bytes transferred so far, and total is the 3473 size of the blob. 3474 max_connections: 3475 Maximum number of parallel connections to use when the blob size 3476 exceeds 64MB. 3477 Set to 1 to download the blob chunks sequentially. 3478 Set to 2 or more to download the blob chunks in parallel. This uses 3479 more system resources but will download faster. 3480 max_retries: 3481 Number of times to retry download of blob chunk if an error occurs. 3482 retry_wait: 3483 Sleep time in secs between retries. 3484 """ 3485 if call != "function": 3486 raise SaltCloudSystemExit( 3487 "The get_blob function must be called with -f or --function." 3488 ) 3489 3490 if kwargs is None: 3491 kwargs = {} 3492 3493 if "container" not in kwargs: 3494 raise SaltCloudSystemExit( 3495 'The blob container name must be specified as "container"' 3496 ) 3497 3498 if "name" not in kwargs: 3499 raise SaltCloudSystemExit('The blob name must be specified as "name"') 3500 3501 if "local_path" not in kwargs and "return_content" not in kwargs: 3502 raise SaltCloudSystemExit( 3503 'Either a local path needs to be passed in as "local_path" or ' 3504 '"return_content" to return the blob contents directly' 3505 ) 3506 3507 if not storage_conn: 3508 storage_conn = get_storage_conn(conn_kwargs=kwargs) 3509 3510 return salt.utils.msazure.get_blob(storage_conn=storage_conn, **kwargs) 3511 3512 3513def query(path, method="GET", data=None, params=None, header_dict=None, decode=True): 3514 """ 3515 Perform a query directly against the Azure REST API 3516 """ 3517 certificate_path = config.get_cloud_config_value( 3518 "certificate_path", get_configured_provider(), __opts__, search_global=False 3519 ) 3520 subscription_id = salt.utils.stringutils.to_str( 3521 config.get_cloud_config_value( 3522 "subscription_id", get_configured_provider(), __opts__, search_global=False 3523 ) 3524 ) 3525 management_host = config.get_cloud_config_value( 3526 "management_host", 3527 get_configured_provider(), 3528 __opts__, 3529 search_global=False, 3530 default="management.core.windows.net", 3531 ) 3532 backend = config.get_cloud_config_value( 3533 "backend", get_configured_provider(), __opts__, search_global=False 3534 ) 3535 url = "https://{management_host}/{subscription_id}/{path}".format( 3536 management_host=management_host, 3537 subscription_id=subscription_id, 3538 path=path, 3539 ) 3540 3541 if header_dict is None: 3542 header_dict = {} 3543 3544 header_dict["x-ms-version"] = "2014-06-01" 3545 3546 result = salt.utils.http.query( 3547 url, 3548 method=method, 3549 params=params, 3550 data=data, 3551 header_dict=header_dict, 3552 port=443, 3553 text=True, 3554 cert=certificate_path, 3555 backend=backend, 3556 decode=decode, 3557 decode_type="xml", 3558 ) 3559 if "dict" in result: 3560 return result["dict"] 3561 return 3562