lib/vagrant-vcloudair/command.rb
require 'awesome_print'
require 'terminal-table'
module VagrantPlugins
module VCloudAir
# This Class exposes an additional 'vcloudair' namespace within the vagrant
# command.
class Command < Vagrant.plugin('2', :command)
def self.synopsis
'namespace to interact with vCloud Air specifics [vCloud Air provider]'
end
def command_vcloud_status(cfg, vapp_id)
# Set our handlers to the driver and objects
puts 'Fetching vCloud Air status...'
cnx = cfg.vcloudair_cnx.driver
vapp = cnx.get_vapp(vapp_id)
organization = cnx.get_organization_by_name(cfg.vdc_name)
cfg.vdc_id = cnx.get_vdc_id_by_name(organization, cfg.vdc_name)
# Create a new table for the general information
table = Terminal::Table.new
table.title = "Vagrant vCloud Air Status : #{cfg.hostname}"
table << ['Organization Name', cfg.vdc_name]
table << ['Organization vDC Name', cfg.vdc_name]
table << ['Organization vDC ID', cfg.vdc_id]
table << ['Organization vDC Network Name', cfg.vdc_network_name]
table << ['Organization vDC Edge Gateway Name',
cfg.vdc_edge_gateway] unless cfg.vdc_edge_gateway.nil?
table << ['Organization vDC Edge IP',
cfg.vdc_edge_gateway_ip] unless cfg.vdc_edge_gateway_ip.nil?
table << :separator
table << ['vApp Name', vapp[:name]]
table << ['vAppID', vapp_id]
vapp[:vms_hash].each do |vm|
# This should be checked indivudually
# When 1 VM is destroyed, ID is still populated, should be cleaned.
table << ["-> #{vm[0]}", vm[1][:id]]
end
# Print the General Information Table
puts table
end
def command_vcloud_network(cfg, vapp_id, ssh_host)
# FIXME: this needs to be fixed to accomodate the bridged scenario
# potentially showing only the assigned IPs in the VMs
puts 'Fetching vCloud Air network settings ...'
cnx = cfg.vcloudair_cnx.driver
vapp = cnx.get_vapp(vapp_id)
organization = cnx.get_organization_by_name(cfg.vdc_name)
cfg.vdc_id = cnx.get_vdc_id_by_name(organization, cfg.vdc_name)
if !cfg.network_bridge.nil?
# Create a new table for the network information
network_table = Terminal::Table.new
network_table.title = 'Network Map'
network_table << ['VM Name', 'IP Address', 'Connection']
network_table << :separator
vapp[:vms_hash].each do |vm|
network_table << [vm[0], vm[1][:addresses][0], 'Direct']
end
else
vapp_edge_ip = cnx.get_vapp_edge_public_ip(vapp_id)
vapp_edge_rules = cnx.get_vapp_port_forwarding_rules(vapp_id)
edge_gateway_rules = cnx.get_edge_gateway_rules(cfg.vdc_edge_gateway,
cfg.vdc_id)
# Create a new table for the network information
network_table = Terminal::Table.new
network_table.title = 'Vagrant vCloud Air Network Map'
network_table << ['VM Name', 'Destination NAT Mapping', 'Enabled']
network_table << :separator
# Fetching Destination NAT Rules for each vApp/Edge/VM/Mapping
vapp_edge_rules.each do |vapp_edge_rule|
edge_gateway_rule = edge_gateway_rules.find {|r|
(r[:rule_type] == 'DNAT' &&
r[:original_ip] == cfg.vdc_edge_gateway_ip &&
r[:translated_ip] == vapp_edge_ip)}
# Loop on every VM in the vApp
vapp[:vms_hash].each do |vm|
# Only Map valid vAppEdge scope to VM scope
vm_scope = vm[1][:vapp_scoped_local_id]
vapp_edge_scope = vapp_edge_rule[:vapp_scoped_local_id]
if vm_scope == vapp_edge_scope
# Generate DNAT Mappings for the valid machines
# If rules don't match, you will not see them !
if edge_gateway_rule
# DNAT rule from edge to vapp to vm
connect_host = nil
# Add support for config.ssh.host
if ssh_host
connect_host = "#{ssh_host}:" +
"#{vapp_edge_rule[:nat_external_port]}" +
' -> ' +
"#{cfg.vdc_edge_gateway_ip}:" +
"#{vapp_edge_rule[:nat_external_port]}"
else
connect_host = "#{cfg.vdc_edge_gateway_ip}:" +
"#{vapp_edge_rule[:nat_external_port]}"
end
network_table << [
"#{vm[0]}",
"#{connect_host}" +
" -> #{vapp_edge_ip}:" +
"#{vapp_edge_rule[:nat_external_port]}" +
" -> #{vm[1][:addresses][0]}:" +
"#{vapp_edge_rule[:nat_internal_port]}",
edge_gateway_rule[:is_enabled]
]
else
# DNAT rule only from vapp to vm
network_table << [
"#{vm[0]}",
"#{vapp_edge_ip}:" +
"#{vapp_edge_rule[:nat_external_port]}" +
" -> #{vm[1][:addresses][0]}:" +
"#{vapp_edge_rule[:nat_internal_port]}",
true
]
end
end
end
end
# Fetching Source NAT Rules for the vApp
network_table << :separator
network_table << ['Network Name', 'Source NAT Mapping', 'Enabled']
network_table << :separator
edge_gateway_rules.each do |edge_gateway_rule|
# Only check SNAT and src/dst
if edge_gateway_rule[:rule_type] == 'SNAT' &&
edge_gateway_rule[:original_ip] == vapp_edge_ip &&
edge_gateway_rule[:translated_ip] == cfg.vdc_edge_gateway_ip
network_table << [
edge_gateway_rule[:interface_name],
"#{vapp_edge_ip} -> #{cfg.vdc_edge_gateway_ip}",
edge_gateway_rule[:is_enabled]
]
end
end
# Fetching Edge Gateway Firewall Rules
network_table << :separator
network_table << ['Rule# - Description', 'Firewall Rules', 'Enabled']
network_table << :separator
edge_gateway_rules.each do |edge_gateway_rule|
# Only add firewall rules
if edge_gateway_rule[:rule_type] == 'Firewall'
network_table << [
"#{edge_gateway_rule[:id]} - " +
"(#{edge_gateway_rule[:description]})",
"#{edge_gateway_rule[:policy]} " +
"SRC:#{edge_gateway_rule[:source_ip]}:" +
"#{edge_gateway_rule[:source_portrange]} to " +
"DST:#{edge_gateway_rule[:destination_ip]}:" +
"#{edge_gateway_rule[:destination_portrange]}",
"#{edge_gateway_rule[:is_enabled]}"
]
end
end
end
# Print the Network Table
puts network_table
end
def command_vcloud_redeploy_edge_gw(cfg)
cnx = cfg.vcloudair_cnx.driver
organization = cnx.get_organization_by_name(cfg.vdc_name)
cfg.vdc_id = cnx.get_vdc_id_by_name(organization, cfg.vdc_name)
edge_gw_id = cnx.find_edge_gateway_id(cfg.vdc_edge_gateway, cfg.vdc_id)
task_id = cnx.redeploy_edge_gateway(edge_gw_id)
puts "Redeploying #{cfg.vdc_edge_gateway} vShield Edge Gateway... " +
'(This task can take a few minutes)'
cnx.wait_task_completion(task_id)
puts 'Done'
end
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = 'Usage: vagrant vcloud [options]'
# We can probably extend this if needed for specific information
o.on(
'-n',
'--network',
'Display the vCloud Air network mapping information'
) do |f|
options[:network] = true
end
o.on(
'-s',
'--status',
'Display the vCloud Air objects IDs'
) do |f|
options[:status] = true
end
o.on(
'-r',
'--redeploy-edge-gw',
'Redeploy the vCloud Air Edge Gateway'
) do |f|
options[:redeploy_edge_gw] = true
end
end
@argv = parse_options(opts)
return unless @argv
# If no arguments, print help
if options.keys.count() == 0
puts opts
exit 1
end
puts 'Initializing vCloud Air provider...'
# initialize some variables
ssh_host = nil
vapp_id = nil
cfg = nil
# Go through the vagrant machines
with_target_vms(@argv) do |machine|
# FIXME/Bug: why does the provider switch to virtualbox when
# destroying VMs within the the vApp:
# .vagrant/machines/<machine>/virtualbox Cannot trace why this
# happens :-( (tsugliani)
if machine.provider_name != :vcloudair
# Not a vCloud Air provider, exit with explicit error message
puts "#{machine.provider_name} provider is incompatible with " +
'this command'
exit 1
end
# Force reloads on objects by fetching the ssh_info
machine.provider.ssh_info
# populate cfg & vApp Id for later use.
cfg = machine.provider_config
vapp_id = machine.get_vapp_id
ssh_host = machine.config.ssh.host
break
end
# iterate through each option and call the according command.
options.keys.each do |key|
case key
when :status
command_vcloud_status(cfg, vapp_id)
when :network
command_vcloud_network(cfg, vapp_id, ssh_host)
when :redeploy_edge_gw
command_vcloud_redeploy_edge_gw(cfg)
end
end
0
end
end
end
end