diff --git a/install.sh b/install.sh
index ca951588b5..55726b6566 100755
--- a/install.sh
+++ b/install.sh
@@ -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 \
diff --git a/src/cloud/ec2/etc/econe.conf b/src/cloud/ec2/etc/econe.conf
index 02e352aa1b..0e6c5a0b5d 100644
--- a/src/cloud/ec2/etc/econe.conf
+++ b/src/cloud/ec2/etc/econe.conf
@@ -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:
diff --git a/src/cloud/ec2/lib/EC2QueryServer.rb b/src/cloud/ec2/lib/EC2QueryServer.rb
index 9b02650a9c..dc9234d5c8 100644
--- a/src/cloud/ec2/lib/EC2QueryServer.rb
+++ b/src/cloud/ec2/lib/EC2QueryServer.rb
@@ -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]
diff --git a/src/cloud/ec2/lib/econe-server.rb b/src/cloud/ec2/lib/econe-server.rb
index bcff7bda0b..24601cefc9 100644
--- a/src/cloud/ec2/lib/econe-server.rb
+++ b/src/cloud/ec2/lib/econe-server.rb
@@ -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)
diff --git a/src/cloud/ec2/lib/elastic_ip.rb b/src/cloud/ec2/lib/elastic_ip.rb
new file mode 100644
index 0000000000..0a93d34120
--- /dev/null
+++ b/src/cloud/ec2/lib/elastic_ip.rb
@@ -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
\ No newline at end of file
diff --git a/src/cloud/ec2/lib/views/allocate_address.erb b/src/cloud/ec2/lib/views/allocate_address.erb
new file mode 100644
index 0000000000..69f131d427
--- /dev/null
+++ b/src/cloud/ec2/lib/views/allocate_address.erb
@@ -0,0 +1,8 @@
+
+
+ 4ac62eaf-e266-4058-a970-2c01568cd417
+ <%= eip %>
+ standard
+ 9090909090
+
+
diff --git a/src/cloud/ec2/lib/views/associate_address.erb b/src/cloud/ec2/lib/views/associate_address.erb
new file mode 100644
index 0000000000..8ce2c3ca9e
--- /dev/null
+++ b/src/cloud/ec2/lib/views/associate_address.erb
@@ -0,0 +1,6 @@
+
+
+ 4ac62eaf-e266-4058-a970-2c01568cd417
+ true
+
+
diff --git a/src/cloud/ec2/lib/views/describe_addresses.erb b/src/cloud/ec2/lib/views/describe_addresses.erb
new file mode 100644
index 0000000000..3b42278cca
--- /dev/null
+++ b/src/cloud/ec2/lib/views/describe_addresses.erb
@@ -0,0 +1,21 @@
+
+
+ 4ac62eaf-e266-4058-a970-2c01568cd417
+
+ <% vnet.each("LEASES/LEASE[USED=1 and VID=-1]") do |eip| %>
+ <% if vnet["TEMPLATE/EC2_ADDRESSES[IP=\"#{eip["IP"]}\"]/UID"] == user_id.to_s %>
+ -
+ <%= eip["IP"] %>
+ standard
+ <% if vm_id = vnet["TEMPLATE/EC2_ADDRESSES[IP=\"#{eip["IP"]}\"]/VMID"] %>
+ <%= vm_id %>
+ <% else %>
+
+ <% end %>
+
+
+
+ <% end %>
+ <% end %>
+
+
\ No newline at end of file
diff --git a/src/cloud/ec2/lib/views/describe_images.erb b/src/cloud/ec2/lib/views/describe_images.erb
index 163241c670..fff015f68a 100644
--- a/src/cloud/ec2/lib/views/describe_images.erb
+++ b/src/cloud/ec2/lib/views/describe_images.erb
@@ -2,7 +2,6 @@
<% impool.each do |im| %>
- <% im.info %>
-
ami-<%= sprintf('%08i', im.id) %>
<%= im['SOURCE'].split('/').last %>
diff --git a/src/cloud/ec2/lib/views/disassociate_address.erb b/src/cloud/ec2/lib/views/disassociate_address.erb
new file mode 100644
index 0000000000..491092a84e
--- /dev/null
+++ b/src/cloud/ec2/lib/views/disassociate_address.erb
@@ -0,0 +1,6 @@
+
+
+ 4ac62eaf-e266-4058-a970-2c01568cd417
+ true
+
+
diff --git a/src/cloud/ec2/lib/views/release_address.erb b/src/cloud/ec2/lib/views/release_address.erb
new file mode 100644
index 0000000000..d7b6e529a4
--- /dev/null
+++ b/src/cloud/ec2/lib/views/release_address.erb
@@ -0,0 +1,5 @@
+
+
+ 4ac62eaf-e266-4058-a970-2c01568cd417
+ true
+
\ No newline at end of file
diff --git a/src/oca/ruby/OpenNebula/Pool.rb b/src/oca/ruby/OpenNebula/Pool.rb
index 53bde5ff48..46ddc68652 100644
--- a/src/oca/ruby/OpenNebula/Pool.rb
+++ b/src/oca/ruby/OpenNebula/Pool.rb
@@ -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)
diff --git a/src/oca/ruby/OpenNebula/VirtualNetwork.rb b/src/oca/ruby/OpenNebula/VirtualNetwork.rb
index 2b82cc1622..332c646720 100644
--- a/src/oca/ruby/OpenNebula/VirtualNetwork.rb
+++ b/src/oca/ruby/OpenNebula/VirtualNetwork.rb
@@ -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
diff --git a/src/oca/ruby/OpenNebula/XMLUtils.rb b/src/oca/ruby/OpenNebula/XMLUtils.rb
index e709b6a8a4..36f62606ca 100644
--- a/src/oca/ruby/OpenNebula/XMLUtils.rb
+++ b/src/oca/ruby/OpenNebula/XMLUtils.rb
@@ -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