lib/fog/azurerm/requests/compute/create_virtual_machine.rb
require 'base64'
WHITE_SPACE = ' '.freeze
module Fog
module Compute
class AzureRM
# This class provides the actual implementation for service calls.
class Real
def create_virtual_machine(vm_config, async = false)
vm_name = vm_config[:name]
rg_name = vm_config[:resource_group]
# In case of updating the VM, we check if the user has passed any value for os_disk_name
# If the user has not passed any value, we try to retrieve the value of os_disk_name from the VM
# If the VM exists then the os_disk_name is retrieved; else it remains nil
os_disk_parameters = get_os_disk_parameters(rg_name, vm_name) if vm_config[:os_disk_name].nil? || vm_config[:os_disk_vhd_uri].nil?
vm_config[:os_disk_name] = os_disk_parameters[:os_disk_name] if vm_config[:os_disk_name].nil?
vm_config[:os_disk_vhd_uri] = os_disk_parameters[:os_disk_vhd_uri] if vm_config[:os_disk_vhd_uri].nil?
msg = "Creating Virtual Machine '#{vm_name}' in Resource Group '#{rg_name}'..."
Fog::Logger.debug msg
vm = Azure::ARM::Compute::Models::VirtualMachine.new
vm.location = vm_config[:location]
vm.tags = vm_config[:tags]
vm.availability_set = get_vm_availability_set(vm_config[:availability_set_id])
vm.hardware_profile = get_hardware_profile(vm_config[:vm_size])
vm.os_profile = get_os_profile(vm_config)
vm.network_profile = get_network_profile(vm_config[:network_interface_card_ids])
vm.storage_profile = get_storage_profile(vm_config)
begin
response = if async
@compute_mgmt_client.virtual_machines.create_or_update_async(rg_name, vm_name, vm)
else
@compute_mgmt_client.virtual_machines.create_or_update(rg_name, vm_name, vm)
end
rescue MsRestAzure::AzureOperationError => e
raise_azure_exception(e, msg)
end
unless async
unless vm_config[:vhd_path].nil? || vm_config[:managed_disk_storage_type].nil?
delete_image(rg_name, vm_name)
delete_storage_account_or_container(rg_name, vm_config[:storage_account_name], vm_name)
end
end
Fog::Logger.debug "Virtual Machine #{vm_name} created successfully!" unless async
response
end
private
def get_vm_availability_set(availability_set_id)
sub_resource = nil
unless availability_set_id.nil?
sub_resource = MsRestAzure::SubResource.new
sub_resource.id = availability_set_id
end
sub_resource
end
def get_hardware_profile(vm_size)
hw_profile = Azure::ARM::Compute::Models::HardwareProfile.new
hw_profile.vm_size = vm_size
hw_profile
end
def get_os_profile(vm_config)
# Arguments unpacking
platform = vm_config[:platform]
vm_name = vm_config[:name]
username = vm_config[:username]
password = vm_config[:password]
custom_data = vm_config[:custom_data] unless vm_config[:custom_data].nil?
provision_vm_agent = vm_config[:provision_vm_agent]
enable_automatic_updates = vm_config[:enable_automatic_updates]
disable_password_auth = vm_config[:disable_password_authentication]
ssh_key_path = vm_config[:ssh_key_path]
ssh_key_data = vm_config[:ssh_key_data]
# Build and return os profile object
os_profile = Azure::ARM::Compute::Models::OSProfile.new
os_profile.computer_name = vm_name
os_profile.admin_username = username
os_profile.admin_password = password
os_profile.custom_data = Base64.strict_encode64(custom_data) unless vm_config[:custom_data].nil?
if platform.casecmp(WINDOWS).zero?
os_profile.windows_configuration = get_windows_config(provision_vm_agent, enable_automatic_updates)
else
os_profile.linux_configuration = get_linux_config(disable_password_auth, ssh_key_path, ssh_key_data)
end
os_profile
end
def get_windows_config(provision_vm_agent, enable_automatic_updates)
windows_config = Azure::ARM::Compute::Models::WindowsConfiguration.new
windows_config.provision_vmagent = provision_vm_agent
windows_config.enable_automatic_updates = enable_automatic_updates
windows_config
end
def get_linux_config(disable_password_auth, ssh_key_path, ssh_key_data)
linux_config = Azure::ARM::Compute::Models::LinuxConfiguration.new
linux_config.disable_password_authentication = disable_password_auth
unless ssh_key_path.nil? || ssh_key_data.nil?
ssh_config = Azure::ARM::Compute::Models::SshConfiguration.new
ssh_public_key = Azure::ARM::Compute::Models::SshPublicKey.new
ssh_public_key.path = ssh_key_path
ssh_public_key.key_data = ssh_key_data
ssh_config.public_keys = [ssh_public_key]
linux_config.ssh = ssh_config
end
linux_config
end
def get_network_profile(network_interface_card_ids)
network_interface_cards = []
network_interface_card_ids.each_with_index do |id, index|
nic = Azure::ARM::Compute::Models::NetworkInterfaceReference.new
nic.id = id
nic.primary = index == PRIMARY_NIC_INDEX
network_interface_cards << nic
end
network_profile = Azure::ARM::Compute::Models::NetworkProfile.new
network_profile.network_interfaces = network_interface_cards
network_profile
end
def get_storage_profile(vm_config)
# Arguments unpacking
managed_disk_storage_type = vm_config[:managed_disk_storage_type]
storage_profile = if managed_disk_storage_type.nil?
get_unmanaged_vm_storage_profile(vm_config)
else
get_managed_vm_storage_profile(vm_config)
end
storage_profile
end
def get_unmanaged_vm_storage_profile(vm_config)
# Arguments unpacking
vm_name = vm_config[:name]
storage_account_name = vm_config[:storage_account_name]
publisher = vm_config[:publisher]
offer = vm_config[:offer]
sku = vm_config[:sku]
version = vm_config[:version]
vhd_path = vm_config[:vhd_path]
os_disk_caching = vm_config[:os_disk_caching]
platform = vm_config[:platform]
resource_group = vm_config[:resource_group]
os_disk_size = vm_config[:os_disk_size]
location = vm_config[:location]
image_ref = vm_config[:image_ref]
os_disk_name = vm_config[:os_disk_name]
os_disk_vhd_uri = vm_config[:os_disk_vhd_uri]
storage_profile = Azure::ARM::Compute::Models::StorageProfile.new
# Set OS disk VHD path
os_disk = Azure::ARM::Compute::Models::OSDisk.new
vhd = Azure::ARM::Compute::Models::VirtualHardDisk.new
vhd.uri = os_disk_vhd_uri.nil? ? get_blob_endpoint(storage_account_name) + "/vhds/#{vm_name}_os_disk.vhd" : os_disk_vhd_uri
os_disk.vhd = vhd
if vhd_path.nil? && image_ref.nil?
# Marketplace
storage_profile.image_reference = image_reference(publisher, offer, sku, version)
elsif !vhd_path.nil? && image_ref.nil?
# VHD
new_vhd_path = copy_vhd_to_storage_account(resource_group, storage_account_name, vhd_path, location, vm_name)
img_vhd = Azure::ARM::Compute::Models::VirtualHardDisk.new
img_vhd.uri = new_vhd_path
os_disk.image = img_vhd
else
# Image
image_resource_group = get_resource_group_from_id(image_ref)
image_name = get_image_name(image_ref)
image = get_image(image_resource_group, image_name)
storage_profile.image_reference = Azure::ARM::Compute::Models::ImageReference.new
storage_profile.image_reference.id = image.id
end
storage_profile.os_disk = configure_os_disk_object(os_disk, os_disk_name, os_disk_caching, os_disk_size, platform, vm_name)
storage_profile
end
def get_managed_vm_storage_profile(vm_config)
# Argument unpacking
managed_disk_storage_type = vm_config[:managed_disk_storage_type]
vhd_path = vm_config[:vhd_path]
resource_group = vm_config[:resource_group]
storage_account_name = vm_config[:storage_account_name]
location = vm_config[:location]
publisher = vm_config[:publisher]
offer = vm_config[:offer]
sku = vm_config[:sku]
version = vm_config[:version]
os_disk_caching = vm_config[:os_disk_caching]
os_disk_size = vm_config[:os_disk_size]
platform = vm_config[:platform]
vm_name = vm_config[:name]
image_ref = vm_config[:image_ref]
os_disk_name = vm_config[:os_disk_name]
# Build storage profile
storage_profile = Azure::ARM::Compute::Models::StorageProfile.new
os_disk = Azure::ARM::Compute::Models::OSDisk.new
managed_disk = Azure::ARM::Compute::Models::ManagedDiskParameters.new
managed_disk.storage_account_type = managed_disk_storage_type
os_disk.managed_disk = managed_disk
if vhd_path.nil? && image_ref.nil?
# Marketplace
storage_profile.image_reference = image_reference(publisher, offer, sku, version)
elsif !vhd_path.nil? && image_ref.nil?
# VHD
new_vhd_path = copy_vhd_to_storage_account(resource_group, storage_account_name, vhd_path, location, vm_name)
image = create_image(image_config_params(location, new_vhd_path, platform, resource_group, vm_name))
storage_profile.image_reference = Azure::ARM::Compute::Models::ImageReference.new
storage_profile.image_reference.id = image.id
else
# Image
image_resource_group = get_resource_group_from_id(image_ref)
image_name = get_image_name(image_ref)
image = get_image(image_resource_group, image_name)
storage_profile.image_reference = Azure::ARM::Compute::Models::ImageReference.new
storage_profile.image_reference.id = image.id
end
storage_profile.os_disk = configure_os_disk_object(os_disk, os_disk_name, os_disk_caching, os_disk_size, platform, vm_name)
storage_profile
end
def image_reference(publisher, offer, sku, version)
image_reference = Azure::ARM::Compute::Models::ImageReference.new
image_reference.publisher = publisher
image_reference.offer = offer
image_reference.sku = sku
image_reference.version = version
image_reference
end
def image_config_params(location, new_vhd_path, platform, resource_group, vm_name)
{
location: location,
new_vhd_path: new_vhd_path,
platform: platform,
resource_group: resource_group,
vm_name: vm_name
}
end
def configure_os_disk_object(os_disk, os_disk_name, os_disk_caching, os_disk_size, platform, vm_name)
# It will use the os_disk_name provided or it will generate a name for itself if it is nil
os_disk.name = os_disk_name.nil? ? "#{vm_name}_os_disk" : os_disk_name
os_disk.os_type = platform
os_disk.disk_size_gb = os_disk_size unless os_disk_size.nil?
os_disk.create_option = Azure::ARM::Compute::Models::DiskCreateOptionTypes::FromImage
os_disk.caching = unless os_disk_caching.nil?
case os_disk_caching
when 'None'
Azure::ARM::Compute::Models::CachingTypes::None
when 'ReadOnly'
Azure::ARM::Compute::Models::CachingTypes::ReadOnly
when 'ReadWrite'
Azure::ARM::Compute::Models::CachingTypes::ReadWrite
end
end
os_disk
end
def copy_vhd_to_storage_account(resource_group, storage_account_name, vhd_path, location, vm_name)
# Copy if VHD does not exist belongs to same storage account.
vhd_storage_account_name = (vhd_path.split('/')[2]).split('.')[0]
if storage_account_name != vhd_storage_account_name
if storage_account_name.nil?
new_time = current_time
storage_account_name = "sa#{new_time}"
storage_account_config = storage_account_config_params(location, resource_group, storage_account_name)
storage_account = @storage_service.storage_accounts.create(storage_account_config)
else
storage_account = @storage_service.storage_accounts.get(resource_group, storage_account_name)
end
access_key = storage_account.get_access_keys.first.value
storage_data = Fog::Storage::AzureRM.new(azure_storage_account_name: storage_account_name, azure_storage_access_key: access_key)
new_time = current_time
container_name = "customvhd-#{vm_name.downcase}-os-image"
blob_name = "vhd_image#{new_time}.vhd"
storage_data.directories.create(key: container_name)
storage_data.copy_blob_from_uri(container_name, blob_name, vhd_path)
until storage_data.get_blob_properties(container_name, blob_name).properties[:copy_status] == 'success'
Fog::Logger.debug 'Waiting disk to ready'
sleep(10)
end
new_vhd_path = get_blob_endpoint(storage_account_name) + "/#{container_name}/#{blob_name}"
Fog::Logger.debug "Path:#{new_vhd_path}. | Copy done"
else
new_vhd_path = vhd_path
end
new_vhd_path
end
def storage_account_config_params(location, resource_group, storage_account_name)
{
name: storage_account_name,
location: location,
resource_group: resource_group,
account_type: 'Standard',
replication: 'LRS',
tags:
{
TEMPORARY_STORAGE_ACCOUNT_TAG_KEY => TEMPORARY_STORAGE_ACCOUNT_TAG_VALUE
}
}
end
def delete_storage_account_or_container(resource_group, storage_account_name, vm_name)
delete_storage_account(resource_group) if storage_account_name.nil?
delete_storage_container(resource_group, storage_account_name, vm_name) unless storage_account_name.nil?
end
def delete_storage_container(resource_group, storage_account_name, vm_name)
access_key = @storage_service.get_storage_access_keys(resource_group, storage_account_name).first.value
container_name = "customvhd-#{vm_name.downcase}-os-image"
@storage_service.directories.delete_temporary_container(storage_account_name, access_key, container_name)
end
def delete_storage_account(resource_group)
@storage_service.storage_accounts.delete_storage_account_from_tag(resource_group,
TEMPORARY_STORAGE_ACCOUNT_TAG_KEY,
TEMPORARY_STORAGE_ACCOUNT_TAG_VALUE)
end
def get_os_disk_parameters(resource_group, virtual_machine_name)
os_disk_parameters = {}
begin
vm = get_virtual_machine(resource_group, virtual_machine_name, false)
rescue
return os_disk_parameters
end
unless vm.storage_profile.nil?
os_disk_parameters[:os_disk_name] = vm.storage_profile.os_disk.name
os_disk_parameters[:os_disk_vhd_uri] = vm.storage_profile.os_disk.vhd.uri unless vm.storage_profile.os_disk.vhd.nil?
end
os_disk_parameters
end
end
# This class provides the mock implementation for unit tests.
class Mock
def create_virtual_machine(*)
vm = {
'location' => 'westus',
'id' => '/subscriptions/########-####-####-####-############/resourceGroups/fog-test-rg/providers/Microsoft.Compute/virtualMachines/fog-test-server',
'name' => 'fog-test-server',
'type' => 'Microsoft.Compute/virtualMachines',
'properties' =>
{
'hardwareProfile' =>
{
'vmSize' => 'Standard_A0'
},
'storageProfile' =>
{
'imageReference' =>
{
'publisher' => 'MicrosoftWindowsServerEssentials',
'offer' => 'WindowsServerEssentials',
'sku' => 'WindowsServerEssentials',
'version' => 'latest'
},
'osDisk' =>
{
'name' => 'fog-test-server_os_disk',
'vhd' =>
{
'uri' => 'http://mystorage1.blob.core.windows.net/vhds/fog-test-server_os_disk.vhd'
},
'createOption' => 'FromImage',
'osType' => 'Linux',
'caching' => 'ReadWrite'
},
'dataDisks' => []
},
'osProfile' =>
{
'computerName' => 'fog-test-server',
'adminUsername' => 'fog',
'linuxConfiguration' =>
{
'disablePasswordAuthentication' => true
},
'secrets' => [],
'customData' => 'ZWNobyBjdXN0b21EYXRh'
},
'networkProfile' =>
{
'networkInterfaces' =>
[
{
'id' => '/subscriptions/########-####-####-####-############/resourceGroups/fog-test-rg/providers/Microsoft.Network/networkInterfaces/fog-test-vnet'
}
]
},
'provisioningState' => 'Succeeded'
}
}
vm_mapper = Azure::ARM::Compute::Models::VirtualMachine.mapper
@compute_mgmt_client.deserialize(vm_mapper, vm, 'result.body')
end
end
end
end
end