def create_virtual_machine( self, location, resource_group_name, vm_name, size_name, nic_id, image_reference, username, password, custom_data=None, ssh_key=None, ): os_profile = OSProfile( computer_name=vm_name, admin_username=username, admin_password=password, ) if custom_data: os_profile.custom_data = custom_data if ssh_key: os_profile.linux_configuration = LinuxConfiguration( ssh=SshConfiguration(public_keys=[ SshPublicKey(key_data=ssh_key), ], )) try: return self.compute_client.virtual_machines.begin_create_or_update( resource_group_name, vm_name, { 'location': location, 'os_profile': os_profile, 'hardware_profile': { 'vm_size': size_name }, 'storage_profile': { 'image_reference': { 'publisher': image_reference['publisher'], 'offer': image_reference['offer'], 'sku': image_reference['sku'], 'version': image_reference['version'], }, }, 'network_profile': { 'network_interfaces': [{ 'id': nic_id, }] }, }, ) except ClientException as exc: raise AzureBackendError(exc)
def virtual_machine( self, name: str, network_interface: NetworkInterface = None, image: dict = None, size: str = "Standard_B2s", user: str = None, password: str = None, spot_instance: bool = True, max_price_per_hour: float = 2.0, disk_size_gb: int = 32, ssh_pubkey: str = None, ) -> VirtualMachine: """Get existing / Create new a Virtual Machine in Azure Args: name (str): Name of the virtual machine network_interface (NetworkInterface, optional): network interface to use. Defaults to None. image (dict, optional): image description to use. Defaults to None. size (str, optional): size of the VM. Defaults to "Standard_B2s". user (str, optional): default username. Defaults to None. password (str, optional): user's password. Defaults to None. spot_instance (bool, optional): Whether to deploy a spot / pay as you go instance. Defaults to True. max_price_per_hour (float, optional): Max price/hour in euros. Defaults to 2.0. disk_size_gb (int, optional): Size of the OS disk. Defaults to 32. ssh_pubkey (str, optional): SSH public key for logging in as user. Defaults to None. Raises: AzureError: If VM creation is not successful. Returns: VirtualMachine: The created virtual machine's descriptor. """ try: vm = self.client(ComputeManagementClient).virtual_machines.get( self.rsg, name) except CloudError: if not network_interface: raise AzureError( "Cannot create VM without network interface, please supply it." ) self.logger.info(f"Creating virtual machine: {name}") else: self.logger.info(f"Found virtual machine: {name}") return vm vm_params = { "location": self.config["location"], "os_profile": { "computer_name": name, "admin_username": user, "admin_password": password }, "hardware_profile": { "vm_size": size, "os_disk": OSDisk(disk_size_gb=disk_size_gb, create_option="FromImage"), }, "storage_profile": { "image_reference": image }, "network_profile": { "network_interfaces": [network_interface] }, "tags": { "persistent": "0", "development": "1" }, "plan": self.config["nvidia_plan"], } if spot_instance: # use Azure spot instance vm_params["priority"] = VirtualMachinePriorityTypes.spot # For Azure Spot virtual machines, the only supported value is 'Deallocate' vm_params[ "eviction_policy"] = VirtualMachineEvictionPolicyTypes.deallocate # set max price vm_params["billing_profile"] = BillingProfile( max_price=max_price_per_hour) if ssh_pubkey: key_path = f"/home/{user}/.ssh/authorized_keys" pubkey = SshPublicKey(path=key_path, key_data=ssh_pubkey) vm_params["os_profile"][ "linux_configuration"] = LinuxConfiguration( ssh=SshConfiguration(public_keys=[pubkey])) self.logger.info(f"Creating VM: {name}") vm_job = self.client( ComputeManagementClient).virtual_machines.create_or_update( self.rsg, name, vm_params) self._async_wait(vm_job) vm = self.client(ComputeManagementClient).virtual_machines.get( self.rsg, name) self.logger.info(f"Created VM: {vm.name}") return vm
def create_virtual_machine(self, credentials, network_client, network_id, parameters, vm_network_name): """ Creates an Azure virtual machine using the network interface created. Args: credentials: A ServicePrincipalCredentials instance, that can be used to access or create any resources. network_client: A NetworkManagementClient instance. network_id: The network id of the network interface created. parameters: A dict, containing all the parameters necessary to authenticate this user with Azure. vm_network_name: The name of the virtual machine to use. """ resource_group = parameters[self.PARAM_RESOURCE_GROUP] storage_account = parameters[self.PARAM_STORAGE_ACCOUNT] zone = parameters[self.PARAM_ZONE] utils.log("Creating a Virtual Machine '{}'".format(vm_network_name)) subscription_id = str(parameters[self.PARAM_SUBSCRIBER_ID]) azure_instance_type = parameters[self.PARAM_INSTANCE_TYPE] compute_client = ComputeManagementClient(credentials, subscription_id) auth_keys_path = self.AUTHORIZED_KEYS_FILE.format(self.ADMIN_USERNAME) with open(auth_keys_path, 'r') as pub_ssh_key_fd: pub_ssh_key = pub_ssh_key_fd.read() public_keys = [SshPublicKey(path=auth_keys_path, key_data=pub_ssh_key)] ssh_config = SshConfiguration(public_keys=public_keys) linux_config = LinuxConfiguration(disable_password_authentication=True, ssh=ssh_config) os_profile = OSProfile(admin_username=self.ADMIN_USERNAME, computer_name=vm_network_name, linux_configuration=linux_config) hardware_profile = HardwareProfile(vm_size=azure_instance_type) network_profile = NetworkProfile( network_interfaces=[NetworkInterfaceReference(id=network_id)]) virtual_hd = VirtualHardDisk( uri='https://{0}.blob.core.windows.net/vhds/{1}.vhd'. format(storage_account, vm_network_name)) image_hd = VirtualHardDisk(uri=parameters[self.PARAM_IMAGE_ID]) os_type = OperatingSystemTypes.linux os_disk = OSDisk(os_type=os_type, caching=CachingTypes.read_write, create_option=DiskCreateOptionTypes.from_image, name=vm_network_name, vhd=virtual_hd, image=image_hd) compute_client.virtual_machines.create_or_update( resource_group, vm_network_name, VirtualMachine( location=zone, os_profile=os_profile, hardware_profile=hardware_profile, network_profile=network_profile, storage_profile=StorageProfile(os_disk=os_disk))) # Sleep until an IP address gets associated with the VM. while True: public_ip_address = network_client.public_ip_addresses.get(resource_group, vm_network_name) if public_ip_address.ip_address: utils.log('Azure VM is available at {}'. format(public_ip_address.ip_address)) break utils.log("Waiting {} second(s) for IP address to be available". format(self.SLEEP_TIME)) time.sleep(self.SLEEP_TIME)
def exec_module(self, **kwargs): for key in list(self.module_arg_spec.keys()) + ['tags']: setattr(self, key, kwargs[key]) # make sure options are lower case self.remove_on_absent = set([resource.lower() for resource in self.remove_on_absent]) changed = False results = dict() vmss = None disable_ssh_password = None vmss_dict = None virtual_network = None subnet = None resource_group = self.get_resource_group(self.resource_group) if not self.location: # Set default location self.location = resource_group.location if self.state == 'present': # Verify parameters and resolve any defaults if self.vm_size and not self.vm_size_is_valid(): self.fail("Parameter error: vm_size {0} is not valid for your subscription and location.".format( self.vm_size )) # if self.virtual_network_name: # virtual_network = self.get_virtual_network(self.virtual_network_name) if self.ssh_public_keys: msg = "Parameter error: expecting ssh_public_keys to be a list of type dict where " \ "each dict contains keys: path, key_data." for key in self.ssh_public_keys: if not isinstance(key, dict): self.fail(msg) if not key.get('path') or not key.get('key_data'): self.fail(msg) if self.image: if not self.image.get('publisher') or not self.image.get('offer') or not self.image.get('sku') \ or not self.image.get('version'): self.error("parameter error: expecting image to contain publisher, offer, sku and version keys.") image_version = self.get_image_version() if self.image['version'] == 'latest': self.image['version'] = image_version.name self.log("Using image version {0}".format(self.image['version'])) disable_ssh_password = not self.ssh_password_enabled try: self.log("Fetching virtual machine scale set {0}".format(self.name)) vmss = self.compute_client.virtual_machine_scale_sets.get(self.resource_group, self.name) self.check_provisioning_state(vmss, self.state) vmss_dict = self.serialize_vmss(vmss) if self.state == 'present': differences = [] results = vmss_dict if self.os_disk_caching and \ self.os_disk_caching != vmss_dict['properties']['virtualMachineProfile']['storageProfile']['osDisk']['caching']: self.log('CHANGED: virtual machine scale set {0} - OS disk caching'.format(self.name)) differences.append('OS Disk caching') changed = True vmss_dict['properties']['virtualMachineProfile']['storageProfile']['osDisk']['caching'] = self.os_disk_caching if self.capacity and \ self.capacity != vmss_dict['sku']['capacity']: self.log('CHANGED: virtual machine scale set {0} - Capacity'.format(self.name)) differences.append('Capacity') changed = True vmss_dict['sku']['capacity'] = self.capacity if self.data_disks and \ len(self.data_disks) != len(vmss_dict['properties']['virtualMachineProfile']['storageProfile']['dataDisks']): self.log('CHANGED: virtual machine scale set {0} - Data Disks'.format(self.name)) differences.append('Data Disks') changed = True update_tags, vmss_dict['tags'] = self.update_tags(vmss_dict.get('tags', dict())) if update_tags: differences.append('Tags') changed = True self.differences = differences elif self.state == 'absent': self.log("CHANGED: virtual machine scale set {0} exists and requested state is 'absent'".format(self.name)) results = dict() changed = True except CloudError: self.log('Virtual machine scale set {0} does not exist'.format(self.name)) if self.state == 'present': self.log("CHANGED: virtual machine scale set {0} does not exist but state is 'present'.".format(self.name)) changed = True self.results['changed'] = changed self.results['ansible_facts']['azure_vmss'] = results if self.check_mode: return self.results if changed: if self.state == 'present': if not vmss: # Create the VMSS self.log("Create virtual machine scale set {0}".format(self.name)) self.results['actions'].append('Created VMSS {0}'.format(self.name)) # Validate parameters if not self.admin_username: self.fail("Parameter error: admin_username required when creating a virtual machine scale set.") if self.os_type == 'Linux': if disable_ssh_password and not self.ssh_public_keys: self.fail("Parameter error: ssh_public_keys required when disabling SSH password.") if self.subnet_name: subnet = self.get_subnet(self.virtual_network_name, self.subnet_name) if not self.virtual_network_name: default_vnet = self.create_default_vnet() virtual_network = default_vnet.id if not self.short_hostname: self.short_hostname = self.name managed_disk = VirtualMachineScaleSetManagedDiskParameters(storage_account_type=self.managed_disk_type) vmss_resource = VirtualMachineScaleSet( self.location, tags=self.tags, upgrade_policy=UpgradePolicy( mode=self.upgrade_policy ), sku=Sku( name=self.vm_size, capacity=self.capacity, tier=self.tier, ), virtual_machine_profile=VirtualMachineScaleSetVMProfile( os_profile=VirtualMachineScaleSetOSProfile( admin_username=self.admin_username, computer_name_prefix=self.short_hostname, ), storage_profile=VirtualMachineScaleSetStorageProfile( os_disk=VirtualMachineScaleSetOSDisk( managed_disk=managed_disk, create_option=DiskCreateOptionTypes.from_image, caching=self.os_disk_caching, ), image_reference=ImageReference( publisher=self.image['publisher'], offer=self.image['offer'], sku=self.image['sku'], version=self.image['version'], ), ), network_profile=VirtualMachineScaleSetNetworkProfile( network_interface_configurations=[ VirtualMachineScaleSetNetworkConfiguration( name=self.name, primary=True, ip_configurations=[ VirtualMachineScaleSetIPConfiguration( name='default', subnet=ApiEntityReference( id=subnet.id ) ) ] ) ] ) ) ) if self.admin_password: vmss_resource.virtual_machine_profile.os_profile.admin_password = self.admin_password if self.os_type == 'Linux': vmss_resource.virtual_machine_profile.os_profile.linux_configuration = LinuxConfiguration( disable_password_authentication=disable_ssh_password ) if self.ssh_public_keys: ssh_config = SshConfiguration() ssh_config.public_keys = \ [SshPublicKey(path=key['path'], key_data=key['key_data']) for key in self.ssh_public_keys] vmss_resource.virtual_machine_profile.os_profile.linux_configuration.ssh = ssh_config if self.data_disks: data_disks = [] for data_disk in self.data_disks: data_disk_managed_disk = VirtualMachineScaleSetManagedDiskParameters( storage_account_type=data_disk['managed_disk_type'] ) data_disk['caching'] = data_disk.get( 'caching', CachingTypes.read_only ) data_disks.append(VirtualMachineScaleSetDataDisk( lun=data_disk['lun'], caching=data_disk['caching'], create_option=DiskCreateOptionTypes.empty, disk_size_gb=data_disk['disk_size_gb'], managed_disk=data_disk_managed_disk, )) vmss_resource.virtual_machine_profile.storage_profile.data_disks = data_disks self.log("Create virtual machine with parameters:") self.create_or_update_vmss(vmss_resource) elif self.differences and len(self.differences) > 0: self.log("Update virtual machine scale set {0}".format(self.name)) self.results['actions'].append('Updated VMSS {0}'.format(self.name)) vmss_resource = self.get_vmss() vmss_resource.virtual_machine_profile.storage_profile.os_disk.caching = self.os_disk_caching vmss_resource.sku.capacity = self.capacity data_disks = [] for data_disk in self.data_disks: data_disks.append(VirtualMachineScaleSetDataDisk( lun=data_disk['lun'], caching=data_disk['caching'], create_option=DiskCreateOptionTypes.empty, disk_size_gb=data_disk['disk_size_gb'], managed_disk=VirtualMachineScaleSetManagedDiskParameters( storage_account_type=data_disk['managed_disk_type'] ), )) vmss_resource.virtual_machine_profile.storage_profile.data_disks = data_disks self.log("Update virtual machine with parameters:") self.create_or_update_vmss(vmss_resource) self.results['ansible_facts']['azure_vmss'] = self.serialize_vmss(self.get_vmss()) elif self.state == 'absent': # delete the VM self.log("Delete virtual machine scale set {0}".format(self.name)) self.results['ansible_facts']['azure_vmss'] = None self.delete_vmss(vmss) # until we sort out how we want to do this globally del self.results['actions'] return self.results
def exec_module(self, **kwargs): for key in list(self.module_arg_spec.keys()) + ['tags']: setattr(self, key, kwargs[key]) # make sure options are lower case self.remove_on_absent = set( [resource.lower() for resource in self.remove_on_absent]) changed = False results = dict() vmss = None disable_ssh_password = None vmss_dict = None virtual_network = None subnet = None resource_group = self.get_resource_group(self.resource_group) if not self.location: # Set default location self.location = resource_group.location if self.state == 'present': # Verify parameters and resolve any defaults if self.vm_size and not self.vm_size_is_valid(): self.fail( "Parameter error: vm_size {0} is not valid for your subscription and location." .format(self.vm_size)) # if self.virtual_network_name: # virtual_network = self.get_virtual_network(self.virtual_network_name) if self.ssh_public_keys: msg = "Parameter error: expecting ssh_public_keys to be a list of type dict where " \ "each dict contains keys: path, key_data." for key in self.ssh_public_keys: if not isinstance(key, dict): self.fail(msg) if not key.get('path') or not key.get('key_data'): self.fail(msg) if self.image: if not self.image.get('publisher') or not self.image.get('offer') or not self.image.get('sku') \ or not self.image.get('version'): self.error( "parameter error: expecting image to contain publisher, offer, sku and version keys." ) image_version = self.get_image_version() if self.image['version'] == 'latest': self.image['version'] = image_version.name self.log("Using image version {0}".format( self.image['version'])) disable_ssh_password = not self.ssh_password_enabled try: self.log("Fetching virtual machine scale set {0}".format( self.name)) vmss = self.compute_client.virtual_machine_scale_sets.get( self.resource_group, self.name) self.check_provisioning_state(vmss, self.state) vmss_dict = self.serialize_vmss(vmss) if self.state == 'present': differences = [] results = vmss_dict if self.os_disk_caching and \ self.os_disk_caching != vmss_dict['properties']['virtualMachineProfile']['storageProfile']['osDisk']['caching']: self.log( 'CHANGED: virtual machine scale set {0} - OS disk caching' .format(self.name)) differences.append('OS Disk caching') changed = True vmss_dict['properties']['virtualMachineProfile'][ 'storageProfile']['osDisk'][ 'caching'] = self.os_disk_caching if self.capacity and \ self.capacity != vmss_dict['sku']['capacity']: self.log( 'CHANGED: virtual machine scale set {0} - Capacity'. format(self.name)) differences.append('Capacity') changed = True vmss_dict['sku']['capacity'] = self.capacity if self.data_disks and \ len(self.data_disks) != len(vmss_dict['properties']['virtualMachineProfile']['storageProfile']['dataDisks']): self.log( 'CHANGED: virtual machine scale set {0} - Data Disks'. format(self.name)) differences.append('Data Disks') changed = True update_tags, vmss_dict['tags'] = self.update_tags( vmss_dict.get('tags', dict())) if update_tags: differences.append('Tags') changed = True self.differences = differences elif self.state == 'absent': self.log( "CHANGED: virtual machine scale set {0} exists and requested state is 'absent'" .format(self.name)) results = dict() changed = True except CloudError: self.log('Virtual machine scale set {0} does not exist'.format( self.name)) if self.state == 'present': self.log( "CHANGED: virtual machine scale set {0} does not exist but state is 'present'." .format(self.name)) changed = True self.results['changed'] = changed self.results['ansible_facts']['azure_vmss'] = results if self.check_mode: return self.results if changed: if self.state == 'present': if not vmss: # Create the VMSS self.log("Create virtual machine scale set {0}".format( self.name)) self.results['actions'].append('Created VMSS {0}'.format( self.name)) # Validate parameters if not self.admin_username: self.fail( "Parameter error: admin_username required when creating a virtual machine scale set." ) if self.os_type == 'Linux': if disable_ssh_password and not self.ssh_public_keys: self.fail( "Parameter error: ssh_public_keys required when disabling SSH password." ) if self.subnet_name: subnet = self.get_subnet(self.virtual_network_name, self.subnet_name) if not self.virtual_network_name: default_vnet = self.create_default_vnet() virtual_network = default_vnet.id if not self.short_hostname: self.short_hostname = self.name managed_disk = VirtualMachineScaleSetManagedDiskParameters( storage_account_type=self.managed_disk_type) vmss_resource = VirtualMachineScaleSet( self.location, tags=self.tags, upgrade_policy=UpgradePolicy(mode=self.upgrade_policy), sku=Sku( name=self.vm_size, capacity=self.capacity, tier=self.tier, ), virtual_machine_profile=VirtualMachineScaleSetVMProfile( os_profile=VirtualMachineScaleSetOSProfile( admin_username=self.admin_username, computer_name_prefix=self.short_hostname, ), storage_profile= VirtualMachineScaleSetStorageProfile( os_disk=VirtualMachineScaleSetOSDisk( managed_disk=managed_disk, create_option=DiskCreateOptionTypes. from_image, caching=self.os_disk_caching, ), image_reference=ImageReference( publisher=self.image['publisher'], offer=self.image['offer'], sku=self.image['sku'], version=self.image['version'], ), ), network_profile= VirtualMachineScaleSetNetworkProfile( network_interface_configurations=[ VirtualMachineScaleSetNetworkConfiguration( name=self.name, primary=True, ip_configurations=[ VirtualMachineScaleSetIPConfiguration( name='default', subnet=ApiEntityReference( id=subnet.id)) ]) ]))) if self.admin_password: vmss_resource.virtual_machine_profile.os_profile.admin_password = self.admin_password if self.os_type == 'Linux': vmss_resource.virtual_machine_profile.os_profile.linux_configuration = LinuxConfiguration( disable_password_authentication=disable_ssh_password ) if self.ssh_public_keys: ssh_config = SshConfiguration() ssh_config.public_keys = \ [SshPublicKey(path=key['path'], key_data=key['key_data']) for key in self.ssh_public_keys] vmss_resource.virtual_machine_profile.os_profile.linux_configuration.ssh = ssh_config if self.data_disks: data_disks = [] for data_disk in self.data_disks: data_disk_managed_disk = VirtualMachineScaleSetManagedDiskParameters( storage_account_type=data_disk[ 'managed_disk_type']) data_disk['caching'] = data_disk.get( 'caching', CachingTypes.read_only) data_disks.append( VirtualMachineScaleSetDataDisk( lun=data_disk['lun'], caching=data_disk['caching'], create_option=DiskCreateOptionTypes.empty, disk_size_gb=data_disk['disk_size_gb'], managed_disk=data_disk_managed_disk, )) vmss_resource.virtual_machine_profile.storage_profile.data_disks = data_disks self.log("Create virtual machine with parameters:") self.create_or_update_vmss(vmss_resource) elif self.differences and len(self.differences) > 0: self.log("Update virtual machine scale set {0}".format( self.name)) self.results['actions'].append('Updated VMSS {0}'.format( self.name)) vmss_resource = self.get_vmss() vmss_resource.virtual_machine_profile.storage_profile.os_disk.caching = self.os_disk_caching vmss_resource.sku.capacity = self.capacity data_disks = [] for data_disk in self.data_disks: data_disks.append( VirtualMachineScaleSetDataDisk( lun=data_disk['lun'], caching=data_disk['caching'], create_option=DiskCreateOptionTypes.empty, disk_size_gb=data_disk['disk_size_gb'], managed_disk= VirtualMachineScaleSetManagedDiskParameters( storage_account_type=data_disk[ 'managed_disk_type']), )) vmss_resource.virtual_machine_profile.storage_profile.data_disks = data_disks self.log("Update virtual machine with parameters:") self.create_or_update_vmss(vmss_resource) self.results['ansible_facts'][ 'azure_vmss'] = self.serialize_vmss(self.get_vmss()) elif self.state == 'absent': # delete the VM self.log("Delete virtual machine scale set {0}".format( self.name)) self.results['ansible_facts']['azure_vmss'] = None self.delete_vmss(vmss) # until we sort out how we want to do this globally del self.results['actions'] return self.results