1
0
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:
Daniel Molina 2012-03-28 16:45:13 +02:00
parent 4310327cd4
commit d843e111d3
14 changed files with 357 additions and 5 deletions

View File

@ -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 \

View File

@ -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:

View File

@ -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]

View File

@ -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)

View 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

View 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>

View 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>

View 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>

View File

@ -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>

View 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>

View 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>

View File

@ -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)

View File

@ -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

View File

@ -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