1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-16 22:50:10 +03:00
one/share/hooks/alias_ip/alias_ip.rb

221 lines
6.3 KiB
Ruby
Executable File

#!/usr/bin/env ruby
# -------------------------------------------------------------------------- #
# Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may #
# not use this file except in compliance with the License. You may obtain #
# a copy of the License at #
# #
# http://www.apache.org/licenses/LICENSE-2.0 #
# #
# Unless required by applicable law or agreed to in writing, software #
# distributed under the License is distributed on an "AS IS" BASIS, #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
# See the License for the specific language governing permissions and #
# limitations under the License. #
#--------------------------------------------------------------------------- #
ONE_LOCATION = ENV['ONE_LOCATION']
if !ONE_LOCATION
RUBY_LIB_LOCATION = '/usr/lib/one/ruby'
GEMS_LOCATION = '/usr/share/one/gems'
PACKET_LOCATION = '/usr/lib/one/ruby/vendors/packethost/lib'
LOG_FILE = '/var/log/one/hook-alias_ip.log'
else
RUBY_LIB_LOCATION = ONE_LOCATION + '/lib/ruby'
GEMS_LOCATION = ONE_LOCATION + '/share/gems'
PACKET_LOCATION = ONE_LOCATION + '/ruby/vendors/packethost/lib'
LOG_FILE = ONE_LOCATION + '/var/hook-alias_ip.log'
end
if File.directory?(GEMS_LOCATION)
$LOAD_PATH.reject! {|l| l =~ /vendor_ruby/ }
require 'rubygems'
Gem.use_paths(File.realpath(GEMS_LOCATION))
end
$LOAD_PATH << RUBY_LIB_LOCATION
$LOAD_PATH << PACKET_LOCATION
# rubocop:disable Style/MixinUsage
require 'opennebula'
include OpenNebula
# rubocop:enable Style/MixinUsage
require 'base64'
require 'nokogiri'
require 'open3'
require 'packet'
###
raw_vm_template = Base64.decode64(STDIN.read)
xml_vm_template = Nokogiri::XML(raw_vm_template)
VM_ID = xml_vm_template.xpath('VM/ID').text
VM_XML = raw_vm_template
##########
# Helpers
def log(msg, level = 'I')
File.open(LOG_FILE, 'a') do |f|
msg.lines do |l|
f.puts "[#{Time.now}][VM #{VM_ID}][#{level}] #{l}"
end
end
end
def log_error(msg)
log(msg, 'E')
end
def one_fetch(client, type, id)
object = type.new_with_id(id, client)
rc = object.info(true)
if OpenNebula.is_error?(rc)
STDERR.puts(rc.message)
exit(-1)
end
object
end
def find_packet_ip_assignment(packet_client, id, cidr)
packet_client.get_ip(id).assignments.each do |a|
assignment_id = a['href'].split('/')[-1]
begin
packet_ip = packet_client.get_ip(assignment_id)
rescue StandardError
next
end
packet_cidr = "#{packet_ip.network}/#{packet_ip.cidr}"
if packet_cidr == cidr
return packet_ip
end
end
nil
end
def device_has_ip?(packet_client, device_id, ip_id)
packet_client.get_device(device_id).ip_addresses.each do |ip_address|
return true if ip_address['id'] == ip_id
end
false
end
def manage_packet(host, ip, address_range, assign = true)
cidr = "#{ip}/32"
ar_deploy_id = address_range['DEPLOY_ID']
packet_client = Packet::Client.new(address_range['PACKET_TOKEN'])
packet_ip = find_packet_ip_assignment(packet_client, ar_deploy_id, cidr)
if assign == true
unless host
log("Packet: Unable to assign IP #{ip} to unknown host")
return
end
device_id = host['/HOST/TEMPLATE/PROVISION/DEPLOY_ID']
if packet_ip
if device_has_ip?(packet_client, device_id, packet_ip.id)
log("Packet: Already assigned IP #{ip}")
return
else
log("Packet: Unassign IP #{ip}")
packet_client.delete_ip(packet_ip)
end
end
log("Packet: Assign IP #{ip}")
packet_client.assign_cidr_device(cidr, device_id)
elsif packet_ip
log("Packet: Unassign IP #{ip}")
packet_client.delete_ip(packet_ip)
end
end
##########
# Main
begin
client = Client.new
rescue StandardError => e
STDERR.puts(e.to_s)
exit(-1)
end
host = nil
assign = false
xml_vm = OpenNebula::XMLElement.new
xml_vm.initialize_xml(VM_XML, 'VM')
vm_state_str = OpenNebula::VirtualMachine::VM_STATE[xml_vm['/VM/STATE'].to_i]
log("Alias hook triggered for state=#{vm_state_str}")
# if VM is associated with particular host, get the
# metadata and force the operation to assign
# the aliased IPs to the host
if %w[ACTIVE SUSPENDED POWEROFF].include? vm_state_str
assign = true
host_id = xml_vm['/VM/HISTORY_RECORDS/HISTORY[last()]/HID']
host = one_fetch(client, OpenNebula::Host, host_id)
end
# process each NIC_ALIAS and check each address host assignment
xml_vm.each('/VM/TEMPLATE/NIC_ALIAS') do |nic|
next unless nic['IP'] # or nic['IP6']
nic_ip = nic['IP']
vnet_id = nic['NETWORK_ID']
ar_id = nic['AR_ID']
nic_assign = assign
if nic['ATTACH'] == 'YES'
# we have to detect NIC hot detach, fetch latest VM
# template and check last 2 history entries
# (last entry tends to contain ACTION=0)
vm = one_fetch(client, OpenNebula::VirtualMachine, xml_vm['/VM/ID'])
last_seq = xml_vm['/VM/HISTORY_RECORDS/HISTORY[last()]/SEQ'].to_i
[last_seq, last_seq - 1].each do |seq|
action_id = vm["/VM/HISTORY_RECORDS/HISTORY[SEQ=#{seq}]/ACTION"]
action_id = action_id.to_i
case OpenNebula::VirtualMachine::HISTORY_ACTION[action_id]
when 'none'
next
when 'alias-detach'
nic_assign = false
else
break
end
end
end
vnet = one_fetch(client, OpenNebula::VirtualNetwork, vnet_id)
vnet.each("/VNET/AR_POOL/AR[AR_ID=#{ar_id}]") do |ar|
case ar['IPAM_MAD']
when 'packet'
manage_packet(host, nic_ip, ar, nic_assign)
end
end
end
log('Alias hook done')
exit 0