You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@deltacloud.apache.org by ma...@redhat.com on 2013/03/20 18:02:53 UTC

[PATCH 4/4] Network API rev 2 (RFC) - Drivers - Openstack and EC2

From: marios <ma...@redhat.com>


Signed-off-by: marios <ma...@redhat.com>
---
 server/lib/deltacloud/drivers/base_driver.rb       |   7 +
 server/lib/deltacloud/drivers/ec2/ec2_driver.rb    | 105 ++++++++--
 .../drivers/openstack/openstack_driver.rb          | 216 ++++++++++++++++++++-
 3 files changed, 309 insertions(+), 19 deletions(-)

diff --git a/server/lib/deltacloud/drivers/base_driver.rb b/server/lib/deltacloud/drivers/base_driver.rb
index 85a2148..3d14611 100644
--- a/server/lib/deltacloud/drivers/base_driver.rb
+++ b/server/lib/deltacloud/drivers/base_driver.rb
@@ -259,6 +259,13 @@ module Deltacloud
       addresses(credentials, opts).first if has_capability?(:addresses)
     end
 
+    def network(credentials, opts={})
+      networks(credentials, opts).first if has_capability?(:networks)
+    end
+
+    def subnet(credentials, opts={})
+      subnets(credentials, opts).first if has_capability?(:subnets)
+    end
 
     MEMBER_SHOW_METHODS = [ :realm, :image, :instance, :storage_volume, :bucket, :blob,
                             :key, :firewall ] unless defined?(MEMBER_SHOW_METHODS)
diff --git a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
index 62c72cc..88016eb 100644
--- a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
+++ b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
@@ -268,14 +268,8 @@ module Deltacloud
           if opts[:metrics] and !opts[:metrics].empty?
             instance_options[:monitoring_enabled] = true
           end
-          if opts[:realm_id]
-            az, sn = opts[:realm_id].split(":")
-            if sn
-              instance_options[:subnet_id] = sn
-            else
-              instance_options[:availability_zone] = az
-            end
-          end
+          instance_options[:availability_zone] = opts[:realm_id] if opts[:realm_id]
+          instance_options[:subnet_id] = opts[:subnet_id] if opts[:subnet_id] #FIXME should we fail if no :network_id ? don't need it but need consistency in API...
           instance_options[:key_name] = opts[:keyname] if opts[:keyname]
           instance_options[:instance_type] = opts[:hwp_id] if opts[:hwp_id] && opts[:hwp_id].length > 0
           firewalls = opts.inject([]){|res, (k,v)| res << v if k =~ /firewalls\d+$/; res}
@@ -835,6 +829,73 @@ module Deltacloud
           end
         end
 
+        #Deltacloud Networks == Amazon VPC
+        def networks(credentials, opts={})
+          ec2 = new_client(credentials)
+          networks = []
+          safely do
+            subnets = subnets(credentials) #get all subnets once
+            ec2.describe_vpcs.each do |vpc|
+              vpc_subnets = subnets.inject([]){|res,cur| res<<cur if cur.network==vpc[:vpc_id]  ;res} #collect subnets for this.network
+              networks << convert_vpc(vpc, vpc_subnets)
+            end
+          end
+          networks = filter_on(networks, :id, opts)
+        end
+
+        def create_network(credentials, opts={})
+          ec2 = new_client(credentials)
+          safely do
+            network = ec2.create_vpc(opts[:address_block]).first
+            convert_vpc(network)
+          end
+        end
+
+        def destroy_network(credentials, network_id)
+          ec2 = new_client(credentials)
+          safely do
+            ec2.delete_vpc(network_id)
+          end
+        end
+
+        def subnets(credentials, opts={})
+          ec2 = new_client(credentials)
+          subnets = []
+          safely do
+            ec2.describe_subnets.each do |sn|
+              subnets << convert_subnet(sn)
+            end
+          end
+          subnets = filter_on(subnets, :id, opts)
+        end
+
+        def create_subnet(credentials, opts={})
+          ec2 = new_client(credentials)
+          safely do
+            subnet = ec2.create_subnet(opts[:network_id], opts[:address_block])
+            convert_subnet(subnet)
+          end
+        end
+
+        def destroy_subnet(credentials, opts={})
+          # this method need to be called
+          # with the subnet_id as parameter
+          # in order to work
+
+          # ec2 = new_client(credentials)
+          # safely do
+          #  ec2.delete_subnet(opts[:vpc_id])
+          # end
+          # Subnet.delete(:name=>opts[:vpc_id])
+          #
+        end
+
+        def ports(credentials, opts={})
+          #
+          # Need more investigations
+          #
+        end
+
         def providers(credentials, opts={})
           ec2 = new_client(credentials)
           @providers ||= ec2.describe_regions.map{|r| Provider.new( {:id=>r, :name=>r,
@@ -966,7 +1027,7 @@ module Deltacloud
           unless instance[:subnet_id].empty?
             realm_id = "#{realm_id}:#{instance[:subnet_id]}"
           end
-         Instance.new(
+          inst_params = {
             :id => instance[:aws_instance_id],
             :name => instance[:aws_image_id],
             :state => convert_state(instance[:aws_state]),
@@ -981,8 +1042,11 @@ module Deltacloud
             :private_addresses => [InstanceAddress.new(instance[:private_dns_name], :type => :hostname)],
             :firewalls => instance[:aws_groups],
             :storage_volumes => instance[:block_device_mappings].map{|vol| {vol.values.first=>vol.keys.first } },
-            :create_image => can_create_image
-          )
+            :create_image => can_create_image }
+          if instance[:vpc_id]
+            inst_params.merge!(:network_bindings => [{:network=>instance[:vpc_id], :subnet=>instance[:subnet_id], :ip_address=> instance[:aws_private_ip_address]}])
+          end
+          Instance.new(inst_params)
         end
 
         def convert_key(key)
@@ -1159,6 +1223,25 @@ module Deltacloud
           end
         end
 
+        def convert_vpc(vpc, subnets=[])
+          addr_blocks = subnets.inject([]){|res,cur| res << cur.address_block  ; res}
+          Network.new({ :id => vpc[:vpc_id],
+                        :name => vpc[:vpc_id],
+                        :state=> vpc[:state],
+                        :subnets => subnets.inject([]){|res,cur| res << cur.id  ;res},
+                        :address_blocks=> (addr_blocks.empty? ? [vpc[:cidr_block]] : addr_blocks)  })
+        end
+
+        def convert_subnet(subnet)
+          Subnet.new({  :id => subnet[:subnet_id],
+                        :name => subnet[:subnet_id],
+                        :network =>subnet[:vpc_id],
+                        :address_block => subnet[:cidr_block],
+                        :state => subnet[:state] })
+        end
+
+
+
         exceptions do
 
           on /root device is not supported for the instance/ do
diff --git a/server/lib/deltacloud/drivers/openstack/openstack_driver.rb b/server/lib/deltacloud/drivers/openstack/openstack_driver.rb
index 343934d..bfcca85 100644
--- a/server/lib/deltacloud/drivers/openstack/openstack_driver.rb
+++ b/server/lib/deltacloud/drivers/openstack/openstack_driver.rb
@@ -162,19 +162,24 @@ module Deltacloud
 
         def instances(credentials, opts={})
           os = new_client(credentials)
-          insts = attachments = []
+          insts = attachments = ports = []
           safely do
+            if quantum = have_quantum?(credentials)
+              ports = quantum.ports
+            end
             if opts[:id]
               begin
                 server = os.get_server(opts[:id])
-                insts << convert_from_server(server, os.connection.authuser, get_attachments(opts[:id], os))
+                net_bind = get_net_bindings_for(server.id, ports)
+                insts << convert_from_server(server, os.connection.authuser, get_attachments(opts[:id], os), net_bind)
               rescue => e
                 raise e unless e.message =~ /The resource could not be found/
                 insts = []
               end
             else
               insts = os.list_servers_detail.collect do |s|
-                convert_from_server(s, os.connection.authuser,get_attachments(s[:id], os))
+                net_bind = get_net_bindings_for(s[:id], ports)
+                convert_from_server(s, os.connection.authuser,get_attachments(s[:id], os), net_bind)
               end
             end
           end
@@ -182,6 +187,30 @@ module Deltacloud
           insts
         end
 
+        #le port:
+        #=> #<OpenStack::Network::Port:0x95a833c @id="d601db9e-c936-4811-904a-bb5a27d105f3", @network_id="c4dfe90e-a7ce-41f7-b9b2-2f9773f42a6b", @name="", @admin_state_up=true, @status="ACTIVE", @mac_address="fa:16:3e:f4:e8:bc", @fixed_ips=[{"subnet_id"=>"f78bfc05-ead0-40a6-8325-a35eb2b535c3", "ip_address"=>"10.0.0.4"}], @device_id="fe4022fa-a77c-4adf-be45-6e069fb3a314", @device_owner="", @tenant_id="7be215d541ea4db4a23b3a84b0882408">
+        def get_net_bindings_for(server_id, ports)
+          return [] if ports.empty?
+          net_bind = []
+          ports.each do |port|
+            if port.device_id == server_id
+              port.fixed_ips.each do |fix_ip|
+                net_bind << {:network=> port.network_id, :subnet=>fix_ip["subnet_id"] , :ip_address=>fix_ip["ip_address"]}
+              end
+            end
+          end
+          net_bind
+        end
+
+        def have_quantum?(credentials)
+          begin
+            quantum = new_client(credentials, "network")
+          rescue => e
+            return nil
+          end
+          quantum
+        end
+
         def create_instance(credentials, image_id, opts)
           os = new_client( credentials, "compute")
           result = nil
@@ -203,7 +232,13 @@ module Deltacloud
           end
           safely do
             server = os.create_server(params)
-            result = convert_from_server(server, os.connection.authuser, get_attachments(server.id, os))
+            net_bind = []
+            if opts[:network_id] && opts[:subnet_id] && (quantum=have_quantum?(credentials)) #place instance into a network
+              port = quantum.create_port(opts[:network_id], {"fixed_ips"=>[{"subnet_id"=>opts[:subnet_id]}], "device_id"=>server.id})
+              net_bind = [{:network=>opts[:network], :subnet=>opts[:subnet_id], :ip_address=>port.fixed_ips.first["ip_address"]}]
+              server.refresh
+            end
+            result = convert_from_server(server, os.connection.authuser, get_attachments(server.id, os), net_bind)
           end
           result
         end
@@ -224,6 +259,13 @@ module Deltacloud
           safely do
             server = os.get_server(instance_id)
             server.delete!
+            if quantum = have_quantum?(credentials) #destroy ports if any
+              quantum.ports.each do |port|
+                if port.device_id == server.id
+                  quantum.delete_port(port.id)
+                end
+              end
+            end
           end
           begin
             server.populate
@@ -473,6 +515,160 @@ module Deltacloud
           end
         end
 
+        def networks(credentials, opts={})
+          os = new_client(credentials, "network")
+          networks = []
+          safely do
+            subnets = os.subnets
+            os.networks.each do |net|
+              addr_blocks = get_address_blocks_for(net.id, subnets)
+              networks << convert_network(net, addr_blocks)
+            end
+          end
+          networks = filter_on(networks, :id, opts)
+        end
+
+        def get_address_blocks_for(network_id, subnets)
+          return [] if subnets.empty?
+          addr_blocks = []
+          subnets.each do |sn|
+            if sn.network_id == network_id
+              addr_blocks << sn.cidr
+            end
+          end
+          addr_blocks
+        end
+
+
+        def convert_network(net, addr_blocks)
+          Network.new({ :id => net.id,
+                        :name => net.name,
+                        :subnets => net.subnets,
+                        :state => (net.admin_state_up ? "UP" : "DOWN"),
+                        :address_blocks => addr_blocks
+                        #NOT USED :address_block, :ports
+          })
+        end
+
+        #require params for openstack: {:name}
+        def create_network(credentials, opts={})
+          os = new_client(credentials, "network")
+          safely do
+            net = os.create_network(opts[:name] || "net_#{Time.now.to_i}")
+            convert_network(net)
+          end
+        end
+
+        def destroy_network(credentials, id)
+          os = new_client(credentials, "network")
+          safely do
+            os.delete_network(id)
+          end
+        end
+
+        #can update name or admin_state_up (true/false)
+        def update_network(credentials, opts={})
+          os = new_client(credentials, "network")
+          safely do
+            os.update_network(opts)
+          end
+        end
+
+        #you can list all subnets - don't need to supply network_id
+        #each subnet returned 'knows' its parent network_id
+        #no opts - could use opts for filtering = e.g. ?name=foo
+        def subnets(credentials, opts={})
+          os = new_client(credentials, "network")
+          subnets = []
+          safely do
+            os.subnets.each do |subnet|
+              subnets << convert_subnet(subnet)
+            end
+          end
+          subnets = filter_on(subnets, :id, opts)
+        end
+
+        def convert_subnet(subnet)
+          Subnet.new({  :id => subnet.id,
+                        :name => subnet.name,
+                        :network => subnet.network_id,
+                        :address_block => subnet.cidr,  #or allocation_pools? start..end
+                        #  :state :type
+                        # in quantum, ports have a 'network_id'
+          })
+        end
+
+        #required params:  :network_id, cidr_block
+        #optional params:  :ip_version, :gateway_ip, :allocation_pools
+        def create_subnet(credentials, opts={})
+          os = new_client(credentials, "network")
+          safely do
+            convert_subnet(os.create_subnet(opts[:network_id], opts[:address_block]))
+          end
+        end
+
+        #can update gateway_ip, name
+        def update_subnet(credentials, opts={})
+          os = new_client(credentials, "network")
+          safely do
+            os.update_subnet()
+          end
+        end
+
+        def destroy_subnet(credentials, subnet_id)
+          os = new_client(credentials, "network")
+          safely do
+            os.delete_subnet(subnet_id)
+          end
+        end
+
+#        def ports(credentials, opts={})
+#          os = new_client(credentials, "network")
+#          ports = []
+#          safely do
+#            os.ports.each do |port|
+#              ports << convert_port(port)
+#            end
+#          end
+#          ports
+#        end
+#
+#        def convert_port(port)
+#          Port.new({  :id => port.id,
+#                      :attachment => port.device_id,
+#                      :network => port.network_id, #network, not subnet
+#                      :mac_address => port.mac_address,
+#                      :state => (port.admin_state_up ? "UP" : "DOWN" ), # true/false
+#                      :ip_address => port.fixed_ips # this is a structure; like  [{"subnet_id": "f45087fa-a673-4c98-ba9e-e21642448997", "ip_address": "10.0.0.5"}] - COULD BE >1 address here...
+#                      # UNUSED/no mapping for :type,  :attachment (can be inferred from IP address(es)?)
+#          })
+#        end
+#
+#        #required params: network_id
+#        #optional params: name, mac_address, state, fixedIP (subnet_id AND/OR IP)
+#        def create_port(credentials, opts={})
+#          os = new_client(credentials, "network")
+#          safely do
+#            convert_port(os.create_port(opts))
+#          end
+#        end
+#
+#
+#        #can update name, device_id, state (what else? not clear from API docs)
+#        def update_port(credentials, opts={})
+#          os = new_client(credentials, "network")
+#          safely do
+#            os.update_port(opts[:id], opts)
+#          end
+#        end
+#
+#        def destroy_port(credentials, port_id)
+#          os = new_client(credentials, "network")
+#          safely do
+#            os.delete_port(port_id)
+#          end
+#        end
+#
 private
         #for v2 authentication credentials.name == "username+tenant_name"
         def new_client(credentials, type="compute", ignore_provider=false)
@@ -495,7 +691,7 @@ private
           connection_params.merge!({:region => region}) if region && !ignore_provider # hack needed for 'def providers'
           safely do
             raise ValidationFailure.new(Exception.new("Error: tried to initialise Openstack connection using" +
-                    " an unknown service_type: #{type}")) unless ["volume", "compute", "object-store"].include? type
+                    " an unknown service_type: #{type}")) unless ["volume", "compute", "object-store", "network"].include? type
             OpenStack::Connection.create(connection_params)
            end
         end
@@ -529,7 +725,7 @@ private
                     })
         end
 
-        def convert_from_server(server, owner, attachments=[])
+        def convert_from_server(server, owner, attachments=[], net_bind=[])
           op = (server.class == Hash)? :fetch : :send
           image = server.send(op, :image)
           flavor = server.send(op, :flavor)
@@ -538,7 +734,7 @@ private
             rescue IndexError
               password = ""
           end
-          inst = Instance.new(
+          inst_params = {
             :id => server.send(op, :id).to_s,
             :realm_id => "default",
             :owner_id => owner,
@@ -555,7 +751,11 @@ private
             :keyname => server.send(op, :key_name),
             :launch_time => server.send(op, :created),
             :storage_volumes => attachments.inject([]){|res, cur| res << {cur[:volumeId] => cur[:device]} ;res}
-          )
+          }
+          unless net_bind.empty?
+            inst_params.merge!(:network_bindings=>net_bind)
+          end
+          inst = Instance.new(inst_params)
           inst.actions = instance_actions_for(inst.state)
           inst.create_image = 'RUNNING'.eql?(inst.state)
           inst
-- 
1.8.1.4