2019-07-04 12:19:55 +02:00
#!/usr/bin/env ruby
# -------------------------------------------------------------------------- #
2020-04-30 15:00:02 +02:00
# Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
2019-07-04 12:19:55 +02:00
# #
# 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. #
#--------------------------------------------------------------------------- #
2019-07-04 12:38:25 +02:00
ONE_LOCATION = ENV [ 'ONE_LOCATION' ]
2019-07-04 12:19:55 +02:00
if ! ONE_LOCATION
RUBY_LIB_LOCATION = '/usr/lib/one/ruby'
2019-09-03 14:09:24 +02:00
GEMS_LOCATION = '/usr/share/one/gems'
PACKET_LOCATION = '/usr/lib/one/ruby/vendors/packethost/lib'
LOG_FILE = '/var/log/one/hook-alias_ip.log'
2019-07-04 12:19:55 +02:00
else
RUBY_LIB_LOCATION = ONE_LOCATION + '/lib/ruby'
2019-09-03 14:09:24 +02:00
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 )
2020-06-22 15:18:57 +02:00
$LOAD_PATH . reject! { | l | l =~ / vendor_ruby / }
require 'rubygems'
Gem . use_paths ( File . realpath ( GEMS_LOCATION ) )
2019-07-04 12:19:55 +02:00
end
2019-07-04 12:38:25 +02:00
$LOAD_PATH << RUBY_LIB_LOCATION
$LOAD_PATH << PACKET_LOCATION
2019-07-04 12:19:55 +02:00
2019-07-04 12:38:25 +02:00
# rubocop:disable Style/MixinUsage
2019-07-04 12:19:55 +02:00
require 'opennebula'
include OpenNebula
2019-07-04 12:38:25 +02:00
# rubocop:enable Style/MixinUsage
2019-07-04 12:19:55 +02:00
require 'base64'
2019-09-19 11:28:36 +02:00
require 'nokogiri'
2019-07-04 12:19:55 +02:00
require 'open3'
require 'packet'
###
2019-09-19 11:28:36 +02:00
raw_vm_template = Base64 . decode64 ( STDIN . read )
xml_vm_template = Nokogiri :: XML ( raw_vm_template )
2019-07-04 12:19:55 +02:00
2019-09-19 11:28:36 +02:00
VM_ID = xml_vm_template . xpath ( 'VM/ID' ) . text
VM_XML = raw_vm_template
2019-07-04 12:19:55 +02:00
##########
# Helpers
2019-07-04 12:38:25 +02:00
def log ( msg , level = 'I' )
2019-07-04 12:19:55 +02:00
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 )
2019-09-17 15:29:01 +02:00
rc = object . info ( true )
2019-07-04 12:19:55 +02:00
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 )
2019-07-04 12:38:25 +02:00
rescue StandardError
2019-07-04 12:19:55 +02:00
next
end
2019-07-04 12:38:25 +02:00
2019-07-04 12:19:55 +02:00
packet_cidr = " #{ packet_ip . network } / #{ packet_ip . cidr } "
if packet_cidr == cidr
return packet_ip
end
end
2019-07-04 12:38:25 +02:00
nil
2019-07-04 12:19:55 +02:00
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
2019-07-04 12:38:25 +02:00
false
2019-07-04 12:19:55 +02:00
end
2019-07-04 12:38:25 +02:00
def manage_packet ( host , ip , address_range , assign = true )
2019-09-17 15:29:01 +02:00
cidr = " #{ ip } /32 "
2019-07-04 12:38:25 +02:00
ar_deploy_id = address_range [ 'DEPLOY_ID' ]
2019-07-04 12:19:55 +02:00
2019-09-17 15:29:01 +02:00
packet_client = Packet :: Client . new ( address_range [ 'PACKET_TOKEN' ] )
2019-07-04 12:19:55 +02:00
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' ]
2019-07-04 12:38:25 +02:00
if packet_ip
2019-07-04 12:19:55 +02:00
if device_has_ip? ( packet_client , device_id , packet_ip . id )
log ( " Packet: Already assigned IP #{ ip } " )
return
else
log ( " Packet: Unassign IP #{ ip } " )
2019-07-04 12:38:25 +02:00
packet_client . delete_ip ( packet_ip )
2019-07-04 12:19:55 +02:00
end
end
log ( " Packet: Assign IP #{ ip } " )
packet_client . assign_cidr_device ( cidr , device_id )
elsif packet_ip
log ( " Packet: Unassign IP #{ ip } " )
2019-07-04 12:38:25 +02:00
packet_client . delete_ip ( packet_ip )
2019-07-04 12:19:55 +02:00
end
end
##########
# Main
begin
2019-07-04 12:38:25 +02:00
client = Client . new
2019-07-04 12:19:55 +02:00
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
2019-07-04 12:38:25 +02:00
# metadata and force the operation to assign
2019-07-04 12:19:55 +02:00
# the aliased IPs to the host
2019-07-04 12:38:25 +02:00
if %w[ ACTIVE SUSPENDED POWEROFF ] . include? vm_state_str
2019-07-04 12:19:55 +02:00
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 |
2019-07-04 12:38:25 +02:00
next unless nic [ 'IP' ] # or nic['IP6']
2019-07-04 12:19:55 +02:00
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
2019-07-04 12:38:25 +02:00
[ last_seq , last_seq - 1 ] . each do | seq |
action_id = vm [ " /VM/HISTORY_RECORDS/HISTORY[SEQ= #{ seq } ]/ACTION " ]
action_id = action_id . to_i
2019-07-04 12:19:55 +02:00
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'
2019-07-04 12:38:25 +02:00
manage_packet ( host , nic_ip , ar , nic_assign )
2019-07-04 12:19:55 +02:00
end
end
end
log ( 'Alias hook done' )
exit 0