Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow vApp customization prior provisioning #185

Merged
merged 1 commit into from
Feb 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,77 +3,125 @@ class Vmware::CloudManager::OrchestrationServiceOptionConverter < ::ServiceOrche
include Vmdb::Logging

def stack_create_options
template = ManageIQ::Providers::Vmware::CloudManager::OvfTemplate.new(self.class.get_template(@dialog_options).content)
options = {
:deploy => stack_parameters['deploy'] == 't',
:powerOn => stack_parameters['powerOn'] == 't'
}
options[:vdc_id] = @dialog_options['dialog_availability_zone'] unless @dialog_options['dialog_availability_zone'].blank?

options.merge!(customize_vapp_template(collect_vm_params))
options.merge!(customize_vapp_template(collect_vm_params(template), collect_vapp_net_params(template)))
end

private

# customize_vapp_template will prepare the options in a format suitable for the fog-vcloud-director.
# This mainly results in creating two top level objects in a hash. The :InstantiationParams, contains
# a single :NetworkConfig element with the array of all references to the networks used in the
# deployed vApp. We are using IDs here and let fog library create concrete HREFs that are required
# by the vCloud director. The second object is an array of source items. Each source item references
# a single VM from the vApp template, customises its name and optionally sets network info.
def customize_vapp_template(vm_params)
network_config = {}

source_vms = vm_params.collect do |vm_id, vm_opts|
src_vm = { :vm_id => "vm-#{vm_id}" }
src_vm[:name] = vm_opts["instance_name"] if vm_opts.key?("instance_name")

network_id = vm_opts["vdc_network"]
unless network_id.nil?
# Create new network config if it hasn't been created before.
network_config[network_id] ||= {
:networkName => network_id,
:networkId => network_id,
:fenceMode => "bridged"
# See https://github.com/xlab-si/fog-vcloud-director/blob/master/docs/examples-vapp-instantiate.md
def customize_vapp_template(vm_params, vapp_net_params)
source_vms = vm_params.map do |_, vm_opts|
src_vm = {
:vm_id => "vm-#{vm_opts[:vm_id]}",
:networks => parse_nics(vm_opts),
:hardware => {
:cpu => { :num_cores => vm_opts['num_cores'], :cores_per_socket => vm_opts['cores_per_socket'] },
:memory => { :quantity_mb => vm_opts['memory_mb'] },
:disk => parse_disks(vm_opts)
}

# Add network configuration to the source VM.
src_vm[:networks] = [
:networkName => network_id,
:IsConnected => true,
:IpAddressAllocationMode => "DHCP"
]
end

}
src_vm[:name] = vm_opts["instance_name"] if vm_opts.key?("instance_name")
src_vm[:guest_customization] = { :ComputerName => vm_opts['hostname'] } if vm_opts.key?("hostname")
src_vm
end

# Create options suitable for VMware vCloud provider.
custom_opts = {
:source_vms => source_vms
vapp_networks = vapp_net_params.map do |_, opts|
{
:name => opts[:vapp_net_name],
:parent => opts['parent'],
:fence_mode => opts['fence_mode'],
:subnet => parse_subnets(opts)
}
end

{
:source_vms => source_vms,
:vapp_networks => vapp_networks
}
custom_opts[:InstantiationParams] = {
:NetworkConfig => network_config.values
} unless network_config.empty?
end

custom_opts
def collect_vm_params(template)
vm_params = collect_stack_parameters(
%w(instance_name vdc_network num_cores cores_per_socket memory_mb disk_mb hostname nic_network nic_mode nic_ip_address)
)
# Reverse lookup by indeces.
vm_params.each do |vm_idx, obj|
obj[:vm_id] = template.vm_id_from_idx(vm_idx)
obj['disk_mb'].each do |disk|
disk[:disk_id] = template.disk_id_from_idx(vm_idx, *disk[:subkeys])
end
end
vm_params
end

def collect_vm_params
allowed_vm_params = %w(instance_name vdc_network)
stack_parameters.each_with_object({}) do |(key, value), vm_params|
allowed_vm_params.each do |param|
# VM-specific parameters are named as instance_name-<VM_ID>. The
# following will test the param name for this kind of pattern and use
# the <VM_ID> to store the configuration about this VM.
param_match = key.match(/#{param}-([0-9a-f-]*)/)
def collect_vapp_net_params(template)
vapp_net_params = collect_stack_parameters(%w(gateway netmask dns1 dns2 parent fence_mode))
# Reverse lookup by indeces.
vapp_net_params.each do |vapp_net_idx, obj|
obj[:vapp_net_name] = template.vapp_net_name_from_idx(vapp_net_idx)
end
vapp_net_params
end

def collect_stack_parameters(allowed)
stack_parameters.each_with_object({}) do |(k, value), params|
allowed.each do |param|
param_match = k.match(/#{param}(-[0-9]+)?(-[0-9]+)?(-[0-9]+)?/)
next if param_match.nil?

vm_id = param_match.captures.first
vm_params[vm_id] ||= {}
# Store the parameter value.
vm_params[vm_id][param] = value
keys = param_match.captures.compact.map { |c| Integer(c.sub(/^-/, '')) }
params[keys.first] ||= {}

if keys.count > 1
params[keys.first][param] ||= []
params[keys.first][param] << { :subkeys => keys[1..-1], :value => value }
params[keys.first][param].sort_by! { |el| el[:subkeys] }
else
params[keys.first][param] = value
end
end
end
end

def parse_disks(opts)
return if opts['disk_mb'].blank?
opts['disk_mb'].map { |disk| { :id => disk[:disk_id], :capacity_mb => disk[:value] } }
end

def parse_subnets(opts)
return unless opts['gateway']
Array.new(opts['gateway'].size) do |idx|
{
:gateway => option_value(opts['gateway'], [idx]),
:netmask => option_value(opts['netmask'], [idx]),
:dns1 => option_value(opts['dns1'], [idx]),
:dns2 => option_value(opts['dns2'], [idx]),
}
end
end

def parse_nics(opts)
return unless opts['nic_network']
Array.new(opts['nic_network'].size) do |idx|
{
:networkName => option_value(opts['nic_network'], [idx]).presence || 'none',
:IpAddressAllocationMode => option_value(opts['nic_mode'], [idx]),
:IpAddress => option_value(opts['nic_ip_address'], [idx]),
:IsConnected => true
}
end
end

def option_value(opts_group, subkeys)
opt = opts_group.detect { |o| o[:subkeys] == subkeys }
opt[:value] unless opt.nil?
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ class ManageIQ::Providers::Vmware::CloudManager::OrchestrationStack < ManageIQ::
require_nested :Status

def self.raw_create_stack(orchestration_manager, stack_name, template, options = {})
log_prefix = "stack=[#{stack_name}]"
orchestration_manager.with_provider_connection do |service|
create_options = {:stack_name => stack_name, :template => template.ems_ref}.merge(options)

$vcloud_log.info("#{log_prefix} create_options: #{create_options}")
service.instantiate_template(create_options)
end
rescue => err
$vcloud_log.error("stack=[#{stack_name}], error: #{err}")
$vcloud_log.error("#{log_prefix} error: #{err}")
raise MiqException::MiqOrchestrationProvisionError, err.to_s, err.backtrace
end

Expand Down
Loading