From: marios marios@redhat.com
--- server/lib/deltacloud/drivers/ec2/ec2_driver.rb | 92 ++++++++++++++++- .../drivers/terremark/terremark_driver.rb | 112 ++++++++++++++++++-- 2 files changed, 195 insertions(+), 9 deletions(-)
diff --git a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb index 076bfc4..6ced942 100644 --- a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb +++ b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb @@ -36,6 +36,7 @@ class EC2Driver < Deltacloud::BaseDriver
feature :instances, :user_data feature :instances, :authentication_key + feature :access_rules, :show
define_hardware_profile('m1.small') do cpu 1 @@ -230,7 +231,6 @@ class EC2Driver < Deltacloud::BaseDriver # # Storage Snapshots # - def storage_snapshots(credentials, opts=nil) ec2 = new_client( credentials ) snapshots = [] @@ -250,6 +250,70 @@ class EC2Driver < Deltacloud::BaseDriver snapshots end
+#-- +# ACCESS RULES +#-- + def access_rules(credentials, opts={}) + rules = [] + ec2 = new_client(credentials) + ec2.describe_security_groups().securityGroupInfo.item.each do |group| + group_name = group['groupName'] + unless group.ipPermissions.nil? + group.ipPermissions.item.each do |rule| + if rule['ipRanges'] #i.e. with cidr address - not interested in access to groups + protocol = rule['ipProtocol'] + port_start = rule['fromPort'] + port_end = rule['toPort'] + ports = (protocol.downcase == 'icmp') ? '' : ((port_start == port_end) ? port_start : "#{port_start}-#{port_end}") + id = "#{group_name}_#{protocol}:#{ports}" + rules << AccessRule.new({ :id => id, + :port => ports, + :protocol => protocol, + :instance_group => group_name + }) + end #if + end #ipPermissions.item.each do + end #unless + end #securityGroupInfo.item.each do + rules + end + +#-- +# CREATE_ACCESS_RULE +#-- + def create_access_rule(credentials, opts={}) + #at minimum can expect: opts['protocol'] and opts['port'] + ec2 = new_client(credentials) + protocol = opts['protocol'].downcase + icmp = (protocol.downcase == 'icmp') #if icmp, ports should be -1 + port = (icmp ? '-1' : opts['port']) + instance_group = opts['instance_group'] + cidr_ip = '0.0.0.0/0' #open to all + opts = { :ip_protocol => protocol, :from_port => port, + :to_port => port, :cidr_ip => cidr_ip, :group_name => instance_group } + ec2.authorize_security_group_ingress(opts) + end + +#-- +# DELETE_ACCESS_RULE +#-- + def delete_access_rule(credentials, opts={}) + ec2 = new_client(credentials) + #opts['id'] eg "default_tcp:22" + instance_group = opts[:id].to_s.split("_").first + protocol = opts[:id].to_s.split("_").last.split(":").first + icmp = (protocol.downcase == 'icmp') #if icmp, ports should be -1 + port = (icmp ? '-1' : opts[:id].to_s.split("_").last.split(":").last) + options = { :group_name => instance_group, + :ip_protocol => protocol, + :from_port => port, + :to_port => port, + :cidr_ip => "0.0.0.0/0", + } + ec2.revoke_security_group_ingress(options) + end + + private
def new_client(credentials) @@ -338,3 +402,29 @@ end end end end + + +#(13:15:54) mfojtik_: i'll write a notes about features here, because it will look messy on channel ;-) +#(13:16:41) mfojtik_: so first of all you need to define a new feature in "lib/deltacloud/base_driver/features.rb" +#(13:16:55) mfojtik_: fir example: +#(13:16:56) mfojtik_: declare_feature :instances, :authentication_password do +#(13:16:56) mfojtik_: operation :create do +#(13:16:56) mfojtik_: param :password, :string, :optional +#(13:16:56) mfojtik_: end +#(13:16:58) mfojtik_: end +#(13:17:32) mfojtik_: so now, this feature is accessible to all drivers +#(13:17:47) mfojtik_: then you open ec2_driver and declare that feature here: +#(13:17:49) mfojtik_: feature :instances, :authentication_key +#(13:18:39) mfojtik_: that's all, now this feature will be advertised in /api +#(13:18:46) marios: great... thats very cool +#(13:18:56) mfojtik_: then next, if you want add a new fields into Instance class +#(13:19:06) mfojtik_: you can do this in driver: +#(13:19:14) mfojtik_: class Instance attr_accessor :keyname attr_accessor :authn_error +#(13:19:16) mfojtik_: err. +#(13:19:19) mfojtik_: again: +#(13:19:21) mfojtik_: class Instance +#(13:19:29) mfojtik_: attr_accessor :security_group +#(13:19:30) mfojtik_: end + + + diff --git a/server/lib/deltacloud/drivers/terremark/terremark_driver.rb b/server/lib/deltacloud/drivers/terremark/terremark_driver.rb index ae3c554..fe27eb3 100644 --- a/server/lib/deltacloud/drivers/terremark/terremark_driver.rb +++ b/server/lib/deltacloud/drivers/terremark/terremark_driver.rb @@ -32,8 +32,8 @@ module Deltacloud
class TerremarkDriver < Deltacloud::BaseDriver
- feature :instances, :user_name - +feature :instances, :user_name +feature :access_rules, :show #-- # Vapp State Map... for use with convert_instance (get an integer back from terremark) #-- @@ -95,7 +95,7 @@ VAPP_STATE_MAP = { "0" => "PENDING", "1" => "PENDING", "2" => "STOPPED", "4" if(current_item['type'] == 'application/vnd.vmware.vcloud.vApp+xml') vapp_id = current_item['href'].split('/').last vapp = terremark_client.get_vapp(vapp_id) - instances << convert_instance(vapp, terremark_client, credentials.user) + instances << convert_instance(vapp, terremark_client, {'account_name' => credentials.user}) end }#end vdc_items.each instances = filter_on( instances, :id, opts ) @@ -140,7 +140,7 @@ VAPP_STATE_MAP = { "0" => "PENDING", "1" => "PENDING", "2" => "STOPPED", "4" ####### vapp_id = terremark_client.instantiate_vapp_template(name, image_id, vapp_opts).body['href'].split('/').last new_vapp = terremark_client.get_vapp(vapp_id) - return convert_instance(new_vapp, terremark_client, credentials.user) #return an Instance object + return convert_instance(new_vapp, terremark_client, {'account_name' => credentials.user}) #return an Instance object end
#-- @@ -148,15 +148,19 @@ VAPP_STATE_MAP = { "0" => "PENDING", "1" => "PENDING", "2" => "STOPPED", "4" #-- def reboot_instance(credentials, id) terremark_client = new_client(credentials) + vapp = terremark_client.get_vapp(id) terremark_client.power_reset(id) + return convert_instance(vapp, terremark_client, {'vapp_status' => '4', 'account_name' => credentials.user}) end - +#status == 0/1->BEING_CREATED 2->OFF 4->ON #-- # START INSTANCE #-- def start_instance(credentials, id) terremark_client = new_client(credentials) + vapp = terremark_client.get_vapp(id) terremark_client.power_on(id) + return convert_instance(vapp, terremark_client, {'vapp_status' => '4', 'account_name' => credentials.user}) end
#-- @@ -164,7 +168,9 @@ end #-- def stop_instance(credentials, id) terremark_client = new_client(credentials) + vapp = terremark_client.get_vapp(id) terremark_client.power_shutdown(id) + return convert_instance(vapp, terremark_client, {'vapp_status' => '2', 'account_name' => credentials.user}) end
#-- @@ -177,12 +183,99 @@ def destroy_instance(credentials, id) end
#-- +# ACCESS RULES +#-- + def access_rules(credentials, opts=nil) + access_rules = [] + terremark_client = new_client(credentials) + services = terremark_client.get_internet_services(terremark_client.default_vdc_id).body['InternetServices'].each do |service| + port = service['Port'] + s_id = service['Id'].to_s + protocol = service['Protocol'] + nodes = terremark_client.get_node_services(s_id).body['NodeServices'] + unless nodes.empty? + nodes.each do |node| + rule_id = "#{s_id}_#{node['IpAddress']}" + access_rules << AccessRule.new({ :id => rule_id, + :port => port, + :instance_group => "#{service['PublicIpAddress']['Name']}", + :protocol => protocol + }) + end #nodes.each do + end #unless nodes empty + end #services.each do + access_rules = filter_on( access_rules, :id, opts ) + access_rules + end + +#-- +# DELETE ACCESS RULE +#-- + def delete_access_rule(credentials, opts=nil) + service_id = opts[:id].to_s.split("_").first # "xxx_111.222.111.222" where xxx is tmark service Id + terremark_client = new_client(credentials) + nodes = terremark_client.get_node_services(service_id).body['NodeServices'] + unless nodes.empty? + nodes.each do |node| + terremark_client.delete_node_service(node['Id']) + end + end + terremark_client.delete_internet_service(service_id) + end + +#-- +# CREATE NEW ACCESS RULE +#-- +#--expects opts['port'], opts['protocol'], opts['instance_group'] + def create_access_rule(credentials, opts={}) + terremark_client = new_client(credentials) + port = opts['port'] + protocol = opts['protocol'] + instance_group = opts['instance_group'] + #need the public ip address for the internet service + ip_id = nil #id of the IP address (from terremark) + service_id=nil + ip_addresses = terremark_client.get_public_ips(terremark_client.default_vdc_id).body['PublicIpAddresses'] + ip_addresses.each do |ip_address| + ip_id = ip_address['id'].to_s + service_id = internet_service_exists(terremark_client, ip_address['name'], port, protocol) + if service_id + break + end + end #ip_addresses.each + unless service_id #in which case we create a new internet service + service_name ||= opts['service_name'] + service_name ||= "svc" + (rand(1000)*Time.now.to_i).to_s.slice(0,7) + service_id = terremark_client.add_internet_service(ip_id, service_name, protocol, port).body['Id'].to_s + end + node_name ||= opts['node_name'] + node_name ||= "nod" + (rand(1000)*Time.now.to_i).to_s.slice(0,7) + private_port = port #use same private port as public one...? + terremark_client.add_node_service(service_id, instance_group, node_name, private_port) + end + +#-- # PRIVATE METHODS: #--
private
#-- +# INTERNET_SERVICE_EXISTS +#-- helper to check if a given public IP/Port combination are defined as an existing internet service +#-- used by the create_access_rule method +def internet_service_exists(terremark_client, public_address, public_port, protocol) + services = terremark_client.get_internet_services(terremark_client.default_vdc_id) + services.body['InternetServices'].each do |current_service| + if ( (current_service['PublicIpAddress']['Name'] == public_address) && (current_service['Port'].to_s == public_port) && + (current_service['Protocol'].upcase == protocol.upcase) ) + return current_service['Id'] + end + end + nil #either return the service id or nil +end + +#-- # CONVERT IMAGE #-- #gets a vapp_template from a catalog and makes it a Image @@ -204,10 +297,13 @@ end #-- # CONVERT INSTANCE #-- - def convert_instance(vapp, terremark_client, account_name) + def convert_instance(vapp, terremark_client, opts={}) vapp_private_ip = vapp.body['IpAddress'] vapp_public_ip = terremark_client.get_public_ips(terremark_client.default_vdc_id).body['PublicIpAddresses'].first['name']#get_public_address(terremark_client, vapp_private_ip) - vapp_status = vapp.body['status'] + unless opts['vapp_status'] + vapp_status = vapp.body['status'] + end + vapp_status ||= opts['vapp_status'] current_state = VAPP_STATE_MAP[vapp_status] #status == 0->BEING_CREATED 2->OFF 4->ON profile = InstanceProfile.new("default") name = vapp.body['name'] @@ -226,7 +322,7 @@ end end Instance.new( { :id => vapp.body['href'].split('/').last, - :owner_id => "#{account_name}", + :owner_id => "#{opts['account_name']}", #:image_id => "n/a", #cant get this... see https://community.vcloudexpress.terremark.com/en-us/discussion_forums/f/60/t... :name => name, :realm_id => "US-Miami",