mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-16 22:50:10 +03:00
221 lines
6.3 KiB
Ruby
Executable File
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
|
|
|