From b3b18df5a2c9c232318915947ed0198080e70ebd Mon Sep 17 00:00:00 2001 From: "Ruben S. Montero" Date: Tue, 25 Sep 2012 19:52:24 +0200 Subject: [PATCH] feature #1383: Initial merge of the patch contributed by Ricardo Duarte to add support for EC2 Keypairs --- src/cloud/ec2/etc/templates/m1.small.erb | 9 +- src/cloud/ec2/lib/EC2QueryServer.rb | 33 ++-- src/cloud/ec2/lib/econe-server.rb | 6 + src/cloud/ec2/lib/instance.rb | 9 +- src/cloud/ec2/lib/keypair.rb | 153 ++++++++++++++++++ src/cloud/ec2/lib/views/create_keypair.erb | 5 + src/cloud/ec2/lib/views/delete_keypair.erb | 4 + .../ec2/lib/views/describe_instances.erb | 6 +- src/cloud/ec2/lib/views/describe_keypairs.erb | 11 ++ 9 files changed, 220 insertions(+), 16 deletions(-) create mode 100644 src/cloud/ec2/lib/keypair.rb create mode 100644 src/cloud/ec2/lib/views/create_keypair.erb create mode 100644 src/cloud/ec2/lib/views/delete_keypair.erb create mode 100644 src/cloud/ec2/lib/views/describe_keypairs.erb diff --git a/src/cloud/ec2/etc/templates/m1.small.erb b/src/cloud/ec2/etc/templates/m1.small.erb index 4883e539fd..f651f1752b 100644 --- a/src/cloud/ec2/etc/templates/m1.small.erb +++ b/src/cloud/ec2/etc/templates/m1.small.erb @@ -18,7 +18,12 @@ NIC=[NETWORK_ID=] IMAGE_ID = <%= erb_vm_info[:ec2_img_id] %> INSTANCE_TYPE = <%= erb_vm_info[:instance_type ]%> - -<% if erb_vm_info[:user_data] %> + +<% if erb_vm_info[:user_data] && erb_vm_info[:public_key] %> +CONTEXT = [ EC2_USER_DATA="<%= erb_vm_info[:user_data] %>", EC2_PUBLIC_KEY="<%= erb_vm_info[:public_key] %>", EC2_KEYNAME ="<%= erb_vm_info[:key_name] %>" ] +<% elsif erb_vm_info[:user_data] %> CONTEXT = [ EC2_USER_DATA="<%= erb_vm_info[:user_data] %>" ] +<% elsif erb_vm_info[:public_key] %> +CONTEXT = [ EC2_PUBLIC_KEY="<%= erb_vm_info[:public_key] %>", EC2_KEYNAME ="<%= erb_vm_info[:key_name] %>" ] <% end %> + diff --git a/src/cloud/ec2/lib/EC2QueryServer.rb b/src/cloud/ec2/lib/EC2QueryServer.rb index 7367c3278b..95d07d3fff 100644 --- a/src/cloud/ec2/lib/EC2QueryServer.rb +++ b/src/cloud/ec2/lib/EC2QueryServer.rb @@ -17,14 +17,25 @@ require 'rubygems' require 'erb' require 'time' -require 'AWS' require 'base64' -require 'CloudServer' +require 'json' +require 'openssl' +require 'digest/md5' +require 'AWS' + +require 'CloudServer' require 'ImageEC2' + require 'ebs' require 'elastic_ip' require 'instance' +require 'keypair' + +################################################################################ +# Extends the OpenNebula::Error class to include an EC2 render of error +# messages +################################################################################ module OpenNebula EC2_ERROR = %q{ @@ -54,12 +65,14 @@ end ############################################################################### class EC2QueryServer < CloudServer - ########################################################################### - + ############################################################################ + # + # + ############################################################################ def initialize(client, oneadmin_client, config, logger) super(config, logger) - @client = client + @client = client @oneadmin_client = oneadmin_client if config[:ssl_server] @@ -68,12 +81,15 @@ class EC2QueryServer < CloudServer @base_url="http://#{config[:server]}:#{config[:port]}" end + # ----------- Load EC2 API modules ------------ + if @config[:elasticips_vnet_id].nil? logger.error { 'ElasticIP module not loaded' } else extend ElasticIP end + extend Keypair extend EBS extend Instance end @@ -83,8 +99,7 @@ class EC2QueryServer < CloudServer ########################################################################### def describe_availability_zones(params) - response = ERB.new( - File.read(@config[:views]+"/describe_availability_zones.erb")) + response = ERB.new(File.read(@config[:views]+"/describe_availability_zones.erb")) return response.result(binding), 200 end @@ -153,9 +168,8 @@ class EC2QueryServer < CloudServer return response.result(binding), 200 end - ########################################################################### - # Elastic IP + # Provide defaults for Elastic IP if not loaded ########################################################################### def allocate_address(params) return OpenNebula::Error.new('Unsupported') @@ -182,7 +196,6 @@ class EC2QueryServer < CloudServer ########################################################################### private - def render_launch_time(vm) return "#{Time.at(vm["STIME"].to_i).xmlschema}" end diff --git a/src/cloud/ec2/lib/econe-server.rb b/src/cloud/ec2/lib/econe-server.rb index 925d643d49..541508efc9 100644 --- a/src/cloud/ec2/lib/econe-server.rb +++ b/src/cloud/ec2/lib/econe-server.rb @@ -208,6 +208,12 @@ def do_http_request(params) result,rc = @econe_server.detach_volume(params) when 'DeleteVolume' result,rc = @econe_server.delete_volume(params) + when 'DescribeKeyPairs' + result,rc = @econe_server.describe_keypairs(params) + when 'CreateKeyPair' + result,rc = @econe_server.create_keypair(params) + when 'DeleteKeyPair' + result,rc = @econe_server.delete_keypair(params) else result = OpenNebula::Error.new( "#{params['Action']} feature is not supported", diff --git a/src/cloud/ec2/lib/instance.rb b/src/cloud/ec2/lib/instance.rb index 951c080dd4..2fa75872c1 100644 --- a/src/cloud/ec2/lib/instance.rb +++ b/src/cloud/ec2/lib/instance.rb @@ -59,15 +59,18 @@ module Instance end # Get the image - tmp, img=params['ImageId'].split('-') + tmp, img = params['ImageId'].split('-') # Build the VM - erb_vm_info=Hash.new + erb_vm_info = Hash.new + erb_vm_info[:img_id] = img.to_i erb_vm_info[:ec2_img_id] = params['ImageId'] erb_vm_info[:instance_type] = instance_type_name erb_vm_info[:template] = path erb_vm_info[:user_data] = params['UserData'] + erb_vm_info[:public_key] = fetch_publickey(params) + erb_vm_info[:key_name] = params['KeyName'] template = ERB.new(File.read(erb_vm_info[:template])) template_text = template.result(binding) @@ -184,4 +187,4 @@ module Instance instance_id = "i-" + sprintf('%08i', vm.id) return "#{instance_id}" end -end \ No newline at end of file +end diff --git a/src/cloud/ec2/lib/keypair.rb b/src/cloud/ec2/lib/keypair.rb new file mode 100644 index 0000000000..55710ef5e9 --- /dev/null +++ b/src/cloud/ec2/lib/keypair.rb @@ -0,0 +1,153 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2012, OpenNebula Project Leads (OpenNebula.org) # +# # +# 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. # +#--------------------------------------------------------------------------- # + +module Keypair + ############################################################################ + # Extends the OpenNebula::User class to include Keypair management + ############################################################################ + class ::OpenNebula::User + + EC2_KP_XPATH = '/USER/TEMPLATE/EC2_KEYPAIRS' + EC2_KP_ELEM = 'EC2_KEYPAIRS' + + ######################################################################## + # Extracts a key pair for the user. Keypairs are stored in the user + # template as base64 json documents + # @param [OpenNebula::User] the user + # + # @return [Hash] with the keypairs. It may be empty + ######################################################################## + def add_keypair(keypairs) + kp = keypairs.to_json + kp64 = Base64.encode64(kp) + + add_element('TEMPLATE', EC2_KP_ELEM => kp64) + + return update(template_xml) + end + + ######################################################################## + # Extracts a key pair for the user. Keypairs are stored in the user + # template as base64 json documents + # @param [OpenNebula::User] the user + # + # @return [Hash] with the keypairs. It may be empty + ######################################################################## + def get_keypair + if has_elements?(EC2_KP_XPATH) + kp64 = Base64.decode64(user[EC2_KP_XPATH]) + kp = JSON.parse(kp64) + else + kp = Hash.new + end + + return kp + end + end + + ############################################################################ + # KeyPair managment functions for EC2 Query API Server + ############################################################################ + + ############################################################################ + # + ############################################################################ + def create_keypair(params) + erb_version = params['Version'] + erb_keyname = params['KeyName'] + erb_user_name = params['AWSAccessKeyId'] + + user = User.new_with_id(OpenNebula::User::SELF, @client) + user.info + + kp = user.get_keypair + rsa_kp = OpenSSL::PKey::RSA.generate(2048) + + erb_private_key = rsa_kp + erb_public_key = rsa_kp.public_key + + erb_key_fingerprint = Digest::MD5.hexdigest(rsa_kp.to_der).gsub(/(.{2})(?=.)/, '\1:\2') + + kp[erb_keyname] = { + "fingerprint" => erb_key_fingerprint, + "public_key" => erb_public_key + } + + rc = user.add_keypair(kp) + + return rc if OpenNebula::is_error?(rc) + + response = ERB.new(File.read(@config[:views]+"/create_keypair.erb")) + return response.result(binding), 200 + end + + ############################################################################ + # + ############################################################################ + def describe_keypairs(params) + erb_version = params['Version'] + erb_user_name = params['AWSAccessKeyId'] + + user = User.new_with_id(OpenNebula::User::SELF, @client) + user.info + + erb_keypairs = user.get_keypair + + response = ERB.new(File.read(@config[:views]+"/describe_keypairs.erb")) + return response.result(binding), 200 + end + + ############################################################################ + # + ############################################################################ + def delete_keypair(params) + erb_version = params['Version'] + erb_user_name = params['AWSAccessKeyId'] + erb_key_name = params['KeyName'] + erb_result = "false" + + user = User.new_with_id(OpenNebula::User::SELF, @client) + user.info + + kp = user.get_keypair + + if !kp.empty? + kp.delete(erb_key_name) + rc = user.add_keypair(kp) + + erb_result = "true" if !OpenNebula::is_error?(rc) + end + + response = ERB.new(File.read(@config[:views]+"/delete_keypair.erb")) + return response.result(binding), 200 + end + + ############################################################################ + # + ############################################################################ + def fetch_publickey(params) + keyname = params['KeyName'] + + user = User.new_with_id(OpenNebula::User::SELF, @client) + user.info + + kp = user.get_keypair + + return nil if keyname.nil? || kp.empty? || kp[keyname].nil? + + kp[keyname]['public_key'] + end +end diff --git a/src/cloud/ec2/lib/views/create_keypair.erb b/src/cloud/ec2/lib/views/create_keypair.erb new file mode 100644 index 0000000000..ef0669ab85 --- /dev/null +++ b/src/cloud/ec2/lib/views/create_keypair.erb @@ -0,0 +1,5 @@ + + <%= erb_keyname %> + <%= erb_key_fingerprint %> + <%= erb_private_key %> + diff --git a/src/cloud/ec2/lib/views/delete_keypair.erb b/src/cloud/ec2/lib/views/delete_keypair.erb new file mode 100644 index 0000000000..5822d79f6c --- /dev/null +++ b/src/cloud/ec2/lib/views/delete_keypair.erb @@ -0,0 +1,4 @@ + + + <%= erb_result %> + diff --git a/src/cloud/ec2/lib/views/describe_instances.erb b/src/cloud/ec2/lib/views/describe_instances.erb index 3718770fbb..38a6833bc6 100644 --- a/src/cloud/ec2/lib/views/describe_instances.erb +++ b/src/cloud/ec2/lib/views/describe_instances.erb @@ -20,7 +20,11 @@ <%= vm["TEMPLATE/NIC/IP"] %> <%= vm["TEMPLATE/NIC/IP"] %> - default + <% if vm['TEMPLATE/CONTEXT/EC2_KEYNAME'].nil? %> + none + <% else %> + <%= vm['TEMPLATE/CONTEXT/EC2_KEYNAME'] %> + <% end %> 0 <%= vm['TEMPLATE/INSTANCE_TYPE'] %> diff --git a/src/cloud/ec2/lib/views/describe_keypairs.erb b/src/cloud/ec2/lib/views/describe_keypairs.erb new file mode 100644 index 0000000000..cdbe99a4fb --- /dev/null +++ b/src/cloud/ec2/lib/views/describe_keypairs.erb @@ -0,0 +1,11 @@ + + + + <% erb_keypairs.each_pair do |k,v| %> + + <%= k %> + <%= v['fingerprint'] %> + + <% end %> + +