mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-16 22:50:10 +03:00
feature #1183: Add ElasticIP support to EC2
This commit is contained in:
parent
4310327cd4
commit
d843e111d3
@ -1011,6 +1011,7 @@ CLOUD_AUTH_LIB_FILES="src/cloud/common/CloudAuth/OCCICloudAuth.rb \
|
||||
ECO_LIB_FILES="src/cloud/ec2/lib/EC2QueryClient.rb \
|
||||
src/cloud/ec2/lib/EC2QueryServer.rb \
|
||||
src/cloud/ec2/lib/ImageEC2.rb \
|
||||
src/cloud/ec2/lib/elastic_ip.rb \
|
||||
src/cloud/ec2/lib/econe-server.rb"
|
||||
|
||||
ECO_LIB_CLIENT_FILES="src/cloud/ec2/lib/EC2QueryClient.rb"
|
||||
@ -1019,6 +1020,11 @@ ECO_LIB_VIEW_FILES="src/cloud/ec2/lib/views/describe_images.erb \
|
||||
src/cloud/ec2/lib/views/describe_instances.erb \
|
||||
src/cloud/ec2/lib/views/register_image.erb \
|
||||
src/cloud/ec2/lib/views/run_instances.erb \
|
||||
src/cloud/ec2/lib/views/allocate_address.erb \
|
||||
src/cloud/ec2/lib/views/associate_address.erb \
|
||||
src/cloud/ec2/lib/views/disassociate_address.erb \
|
||||
src/cloud/ec2/lib/views/describe_addresses.erb \
|
||||
src/cloud/ec2/lib/views/release_address.erb \
|
||||
src/cloud/ec2/lib/views/terminate_instances.erb"
|
||||
|
||||
ECO_BIN_FILES="src/cloud/ec2/bin/econe-server \
|
||||
|
@ -37,9 +37,20 @@
|
||||
# 0 = ERROR, 1 = WARNING, 2 = INFO, 3 = DEBUG
|
||||
:debug_level: 3
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
:cluster_id:
|
||||
:datastore_id:
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
:elasticips_vnet_id: 0
|
||||
|
||||
:associate_script: /usr/bin/false
|
||||
:disassociate_script: /usr/bin/false
|
||||
|
||||
# VM types allowed and its template file (inside templates directory)
|
||||
:instance_types:
|
||||
:m1.small:
|
||||
|
@ -61,10 +61,18 @@ class EC2QueryServer < CloudServer
|
||||
|
||||
###########################################################################
|
||||
|
||||
def initialize(client, config, logger)
|
||||
def initialize(client, oneadmin_client, config, logger)
|
||||
super(config, logger)
|
||||
|
||||
@client = client
|
||||
@oneadmin_client = oneadmin_client
|
||||
|
||||
if @config[:elasticips_vnet_id].nil?
|
||||
logger.error { 'ElasticIP module not loaded' }
|
||||
else
|
||||
require 'elastic_ip'
|
||||
extend ElasticIP
|
||||
end
|
||||
end
|
||||
|
||||
###########################################################################
|
||||
@ -207,9 +215,34 @@ class EC2QueryServer < CloudServer
|
||||
return response.result(binding), 200
|
||||
end
|
||||
|
||||
###########################################################################
|
||||
# Elastic IP
|
||||
###########################################################################
|
||||
def allocate_address(params)
|
||||
return OpenNebula::Error.new('Unsupported'),400
|
||||
end
|
||||
|
||||
def release_address(params)
|
||||
return OpenNebula::Error.new('Unsupported'),400
|
||||
end
|
||||
|
||||
def describe_addresses(params)
|
||||
return OpenNebula::Error.new('Unsupported'),400
|
||||
end
|
||||
|
||||
def associate_address(params)
|
||||
return OpenNebula::Error.new('Unsupported'),400
|
||||
end
|
||||
|
||||
def disassociate_address(params)
|
||||
return OpenNebula::Error.new('Unsupported'),400
|
||||
end
|
||||
|
||||
###########################################################################
|
||||
# Helper functions
|
||||
###########################################################################
|
||||
private
|
||||
|
||||
def render_state(vm)
|
||||
one_state = ONE_STATES[vm.status]
|
||||
ec2_state = EC2_STATES[one_state||:pending]
|
||||
|
@ -135,8 +135,9 @@ before do
|
||||
if username.nil?
|
||||
error 401, error_xml("AuthFailure", 0)
|
||||
else
|
||||
client = settings.cloud_auth.client(username)
|
||||
@econe_server = EC2QueryServer.new(client, settings.config, settings.logger)
|
||||
client = settings.cloud_auth.client(username)
|
||||
oneadmin_client = settings.cloud_auth.client
|
||||
@econe_server = EC2QueryServer.new(client, oneadmin_client, settings.config, settings.logger)
|
||||
end
|
||||
end
|
||||
|
||||
@ -189,6 +190,16 @@ def do_http_request(params)
|
||||
result,rc = @econe_server.describe_instances(params)
|
||||
when 'TerminateInstances'
|
||||
result,rc = @econe_server.terminate_instances(params)
|
||||
when 'AllocateAddress'
|
||||
result,rc = @econe_server.allocate_address(params)
|
||||
when 'AssociateAddress'
|
||||
result,rc = @econe_server.associate_address(params)
|
||||
when 'DisassociateAddress'
|
||||
result,rc = @econe_server.disassociate_address(params)
|
||||
when 'ReleaseAddress'
|
||||
result,rc = @econe_server.release_address(params)
|
||||
when 'DescribeAddresses'
|
||||
result,rc = @econe_server.describe_addresses(params)
|
||||
end
|
||||
|
||||
if OpenNebula::is_error?(result)
|
||||
|
198
src/cloud/ec2/lib/elastic_ip.rb
Normal file
198
src/cloud/ec2/lib/elastic_ip.rb
Normal file
@ -0,0 +1,198 @@
|
||||
module ElasticIP
|
||||
def allocate_address(params)
|
||||
# Get public IP
|
||||
vnet = retrieve_eip_vnet
|
||||
return vnet, 400 if OpenNebula::is_error?(vnet)
|
||||
|
||||
ips = vnet.retrieve_elements('LEASES/LEASE[USED=0]/IP')
|
||||
if ips.nil?
|
||||
logger.error { "There is no lease available to be allocated" }
|
||||
return OpenNebula::Error.new('AddressLimitExceeded'), 400
|
||||
end
|
||||
|
||||
eip = ips.first
|
||||
|
||||
# Hold IP
|
||||
rc = vnet.hold(eip)
|
||||
if OpenNebula::is_error?(rc)
|
||||
logger.error rc.message
|
||||
return OpenNebula::Error.new('Unsupported'),400
|
||||
end
|
||||
|
||||
# Update EC2_ADDRESSES list
|
||||
xml_hash = {'EC2_ADDRESSES' => {'IP' => eip, "UID" => retrieve_uid}}
|
||||
vnet.add_element('TEMPLATE', xml_hash)
|
||||
rc = vnet.update
|
||||
if OpenNebula::is_error?(rc)
|
||||
logger.error rc.message
|
||||
return OpenNebula::Error.new('Unsupported'),400
|
||||
end
|
||||
|
||||
response = ERB.new(File.read(@config[:views]+"/allocate_address.erb"))
|
||||
return response.result(binding), 200
|
||||
end
|
||||
|
||||
def release_address(params)
|
||||
# Check public IP
|
||||
vnet = retrieve_eip_vnet
|
||||
return vnet, 400 if OpenNebula::is_error?(vnet)
|
||||
|
||||
eip = params["PublicIp"]
|
||||
unless vnet["TEMPLATE/EC2_ADDRESSES[IP=\"#{eip}\" and UID=\"#{retrieve_uid}\"]/IP"]
|
||||
logger.error { "address:#{eip} does not exist" }
|
||||
return OpenNebula::Error.new('Unsupported'),400
|
||||
end
|
||||
|
||||
# Disassociate address if needed
|
||||
if vnet["TEMPLATE/EC2_ADDRESSES[IP=\"#{eip}\"]/VMID"]
|
||||
cmd_output = `#{@config[:disassociate_script]} #{eip}`
|
||||
if $?.to_i != 0
|
||||
logger.error { cmd_output }
|
||||
return OpenNebula::Error.new('Unsupported'),400
|
||||
end
|
||||
|
||||
vnet.delete_element("TEMPLATE/EC2_ADDRESSES[IP=\"#{eip}\"]/VMID")
|
||||
end
|
||||
|
||||
# Release IP
|
||||
rc = vnet.release(eip)
|
||||
if OpenNebula::is_error?(rc)
|
||||
logger.error {rc.message}
|
||||
return OpenNebula::Error.new('Unsupported'),400
|
||||
end
|
||||
|
||||
# Update EC2_ADDRESSES list
|
||||
vnet.delete_element("TEMPLATE/EC2_ADDRESSES[IP=\"#{eip}\"]")
|
||||
rc = vnet.update
|
||||
if OpenNebula::is_error?(rc)
|
||||
logger.error {rc.message}
|
||||
return OpenNebula::Error.new('Unsupported'),400
|
||||
end
|
||||
|
||||
response = ERB.new(File.read(@config[:views]+"/release_address.erb"))
|
||||
return response.result(binding), 200
|
||||
end
|
||||
|
||||
def describe_addresses(params)
|
||||
vnet = retrieve_eip_vnet
|
||||
return vnet, 400 if OpenNebula::is_error?(vnet)
|
||||
|
||||
erb_version = params['Version']
|
||||
user_id = retrieve_uid
|
||||
|
||||
response = ERB.new(File.read(@config[:views]+"/describe_addresses.erb"))
|
||||
return response.result(binding), 200
|
||||
end
|
||||
|
||||
def associate_address(params)
|
||||
# Check public IP
|
||||
vnet = retrieve_eip_vnet
|
||||
return vnet, 400 if OpenNebula::is_error?(vnet)
|
||||
|
||||
user_id = retrieve_uid
|
||||
eip = params["PublicIp"]
|
||||
vmid = params['InstanceId']
|
||||
vmid = vmid.split('-')[1] if vmid[0]==?i
|
||||
|
||||
unless vnet["TEMPLATE/EC2_ADDRESSES[IP=\"#{eip}\" and UID=\"#{retrieve_uid}\"]/IP"]
|
||||
logger.error { "address:#{eip} does not exist" }
|
||||
return OpenNebula::Error.new('Unsupported'),400
|
||||
end
|
||||
|
||||
# Get private IP of the Instance
|
||||
vm = VirtualMachine.new(VirtualMachine.build_xml(vmid), @client)
|
||||
rc = vm.info
|
||||
if OpenNebula::is_error?(rc)
|
||||
logger.error {rc.message}
|
||||
return OpenNebula::Error.new('Unsupported'),400
|
||||
end
|
||||
|
||||
ips = vm.retrieve_elements('TEMPLATE/NIC/IP')
|
||||
if ips.nil?
|
||||
logger.error { "The instance does not have any NIC" }
|
||||
return OpenNebula::Error.new('Unsupported'),400
|
||||
end
|
||||
|
||||
private_ip = ips.first
|
||||
|
||||
# Disassociate address if needed
|
||||
if vnet["TEMPLATE/EC2_ADDRESSES[IP=\"#{eip}\"]/VMID"]
|
||||
cmd_output = `#{@config[:disassociate_script]} #{eip}`
|
||||
if $?.to_i != 0
|
||||
logger.error { cmd_output }
|
||||
return OpenNebula::Error.new('Unsupported'),400
|
||||
end
|
||||
|
||||
vnet.delete_element("TEMPLATE/EC2_ADDRESSES[IP=\"#{eip}\"]/VMID")
|
||||
end
|
||||
|
||||
# Run external script
|
||||
vnet_base64 = Base64.encode64(vnet.to_xml).delete("\n")
|
||||
cmd_output = `#{@config[:associate_script]} #{eip} #{private_ip} \"#{vnet_base64}\"`
|
||||
if $?.to_i != 0
|
||||
logger.error { "associate_script" << cmd_output }
|
||||
return OpenNebula::Error.new('Unsupported'),400
|
||||
end
|
||||
|
||||
# Update EC2_ADDRESSES list
|
||||
vnet.add_element("TEMPLATE/EC2_ADDRESSES[IP=\"#{eip}\"]", "VMID" => vmid)
|
||||
rc = vnet.update
|
||||
if OpenNebula::is_error?(rc)
|
||||
logger.error {rc.message}
|
||||
return OpenNebula::Error.new('Unsupported'),400
|
||||
end
|
||||
|
||||
response = ERB.new(File.read(@config[:views]+"/associate_address.erb"))
|
||||
return response.result(binding), 200
|
||||
end
|
||||
|
||||
def disassociate_address(params)
|
||||
# Check public IP
|
||||
vnet = retrieve_eip_vnet
|
||||
return vnet, 400 if OpenNebula::is_error?(vnet)
|
||||
|
||||
eip = params["PublicIp"]
|
||||
unless vnet["TEMPLATE/EC2_ADDRESSES[IP=\"#{eip}\" and UID=\"#{retrieve_uid}\"]/VMID"]
|
||||
logger.error { "address:#{eip} does not exist or is not associated with any instance" }
|
||||
return OpenNebula::Error.new('Unsupported'),400
|
||||
end
|
||||
|
||||
# Run external script
|
||||
cmd_output = `#{@config[:disassociate_script]} #{eip}`
|
||||
if $?.to_i != 0
|
||||
logger.error { cmd_output }
|
||||
return OpenNebula::Error.new('Unsupported'),400
|
||||
end
|
||||
|
||||
# Update EC2_ADDRESSES list
|
||||
vnet.delete_element("TEMPLATE/EC2_ADDRESSES[IP=\"#{eip}\"]/VMID")
|
||||
rc = vnet.update
|
||||
if OpenNebula::is_error?(rc)
|
||||
logger.error {rc.message}
|
||||
return OpenNebula::Error.new('Unsupported'),400
|
||||
end
|
||||
|
||||
response = ERB.new(File.read(@config[:views]+"/disassociate_address.erb"))
|
||||
return response.result(binding), 200
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def retrieve_eip_vnet
|
||||
vnet = VirtualNetwork.new(VirtualNetwork.build_xml(@config[:elasticips_vnet_id]),@oneadmin_client)
|
||||
rc = vnet.info
|
||||
|
||||
if OpenNebula::is_error?(rc)
|
||||
logger.error {rc.message}
|
||||
return OpenNebula::Error.new('Unsupported')
|
||||
end
|
||||
|
||||
vnet
|
||||
end
|
||||
|
||||
def retrieve_uid
|
||||
user = User.new_with_id(OpenNebula::User::SELF, @client)
|
||||
user.info
|
||||
user.id
|
||||
end
|
||||
end
|
8
src/cloud/ec2/lib/views/allocate_address.erb
Normal file
8
src/cloud/ec2/lib/views/allocate_address.erb
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
<AllocateAddressResponse xmlns="http://ec2.amazonaws.com/doc/<%= params['Version'] %>/">
|
||||
<requestId>4ac62eaf-e266-4058-a970-2c01568cd417</requestId>
|
||||
<publicIp><%= eip %></publicIp>
|
||||
<domain>standard</domain>
|
||||
<allocationId>9090909090</allocationId>
|
||||
|
||||
</AllocateAddressResponse>
|
6
src/cloud/ec2/lib/views/associate_address.erb
Normal file
6
src/cloud/ec2/lib/views/associate_address.erb
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0"?>
|
||||
<AssociateAddressResponse xmlns="http://ec2.amazonaws.com/doc/<%= params['Version'] %>/">
|
||||
<requestId>4ac62eaf-e266-4058-a970-2c01568cd417</requestId>
|
||||
<return>true</return>
|
||||
<associationId/>
|
||||
</AssociateAddressResponse>
|
21
src/cloud/ec2/lib/views/describe_addresses.erb
Normal file
21
src/cloud/ec2/lib/views/describe_addresses.erb
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0"?>
|
||||
<DescribeAddressesResponse xmlns="http://ec2.amazonaws.com/doc/<%= erb_version %>/">
|
||||
<requestId>4ac62eaf-e266-4058-a970-2c01568cd417</requestId>
|
||||
<addressesSet>
|
||||
<% vnet.each("LEASES/LEASE[USED=1 and VID=-1]") do |eip| %>
|
||||
<% if vnet["TEMPLATE/EC2_ADDRESSES[IP=\"#{eip["IP"]}\"]/UID"] == user_id.to_s %>
|
||||
<item>
|
||||
<publicIp><%= eip["IP"] %></publicIp>
|
||||
<domain>standard</domain>
|
||||
<% if vm_id = vnet["TEMPLATE/EC2_ADDRESSES[IP=\"#{eip["IP"]}\"]/VMID"] %>
|
||||
<instanceId><%= vm_id %></instanceId>
|
||||
<% else %>
|
||||
<instanceId/>
|
||||
<% end %>
|
||||
<associationId/>
|
||||
<allocationId/>
|
||||
</item>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</addressesSet>
|
||||
</DescribeAddressesResponse>
|
@ -2,7 +2,6 @@
|
||||
<DescribeImagesResponse xmlns="http://ec2.amazonaws.com/doc/<%=erb_version%>/">
|
||||
<imagesSet>
|
||||
<% impool.each do |im| %>
|
||||
<% im.info %>
|
||||
<item>
|
||||
<imageId>ami-<%= sprintf('%08i', im.id) %></imageId>
|
||||
<imageLocation><%= im['SOURCE'].split('/').last %></imageLocation>
|
||||
|
6
src/cloud/ec2/lib/views/disassociate_address.erb
Normal file
6
src/cloud/ec2/lib/views/disassociate_address.erb
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0"?>
|
||||
<ReleaseAddressResponse xmlns="http://ec2.amazonaws.com/doc/<%= params['Version'] %>/">
|
||||
<requestId>4ac62eaf-e266-4058-a970-2c01568cd417</requestId>
|
||||
<return>true</return>
|
||||
</ReleaseAddressResponse>
|
||||
|
5
src/cloud/ec2/lib/views/release_address.erb
Normal file
5
src/cloud/ec2/lib/views/release_address.erb
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<ReleaseAddressResponse xmlns="http://ec2.amazonaws.com/doc/<%= params['Version'] %>/">
|
||||
<requestId>4ac62eaf-e266-4058-a970-2c01568cd417</requestId>
|
||||
<return>true</return>
|
||||
</ReleaseAddressResponse>
|
@ -178,6 +178,8 @@ module OpenNebula
|
||||
def update(xml_method, new_template)
|
||||
return Error.new('ID not defined') if !@pe_id
|
||||
|
||||
new_template ||= template_xml.delete("\n").gsub(/\s+/m,'')
|
||||
|
||||
rc = @client.call(xml_method,@pe_id, new_template)
|
||||
rc = nil if !OpenNebula.is_error?(rc)
|
||||
|
||||
|
@ -90,7 +90,7 @@ module OpenNebula
|
||||
# Replaces the template contents
|
||||
#
|
||||
# +new_template+ New template contents
|
||||
def update(new_template)
|
||||
def update(new_template=nil)
|
||||
super(VN_METHODS[:update], new_template)
|
||||
end
|
||||
|
||||
|
@ -101,6 +101,44 @@ module OpenNebula
|
||||
end
|
||||
end
|
||||
|
||||
def delete_element(xpath)
|
||||
if NOKOGIRI
|
||||
@xml.xpath(xpath.to_s).remove
|
||||
else
|
||||
@xml.delete_element(xpath.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def add_element(xpath, elems)
|
||||
elems.each { |key, value|
|
||||
if value.instance_of?(Hash)
|
||||
if NOKOGIRI
|
||||
elem = Nokogiri::XML::Node.new key, @xml.document
|
||||
value.each { |k2, v2|
|
||||
child = Nokogiri::XML::Node.new k2, elem
|
||||
child.content = v2
|
||||
elem.add_child(child)
|
||||
}
|
||||
@xml.xpath(xpath.to_s).first.add_child(elem)
|
||||
else
|
||||
elem = REXML::Element.new(key)
|
||||
value.each { |k2, v2|
|
||||
elem.add_element(k2).text = v2
|
||||
}
|
||||
@xml.elements[xpath].add_element(elem)
|
||||
end
|
||||
else
|
||||
if NOKOGIRI
|
||||
elem = Nokogiri::XML::Node.new key, @xml.document
|
||||
elem.content = value
|
||||
@xml.xpath(xpath.to_s).first.add_child(elem)
|
||||
else
|
||||
@xml.elements[xpath].add_element(key).text = value
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
# Gets an array of text from elemenets extracted
|
||||
# using the XPATH expression passed as filter
|
||||
def retrieve_elements(filter)
|
||||
@ -201,6 +239,14 @@ module OpenNebula
|
||||
template_like_str('TEMPLATE', indent)
|
||||
end
|
||||
|
||||
def template_xml
|
||||
if NOKOGIRI
|
||||
@xml.xpath('TEMPLATE').to_s
|
||||
else
|
||||
@xml.elements['TEMPLATE'].to_s
|
||||
end
|
||||
end
|
||||
|
||||
def template_like_str(root_element, indent=true, xpath_exp=nil)
|
||||
if NOKOGIRI
|
||||
xml_template=@xml.xpath(root_element).to_s
|
||||
|
Loading…
x
Reference in New Issue
Block a user