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:49 UTC

Network-API-rev-2-RFC

patches are at http://tracker.deltacloud.org/set/395

write-up at http://mariosandreou.com/deltacloud/cloud_API/2013/03/20/deltacloud-networks-api-rfc.html

I'll be working on RHEV-M next - any comments are very much appreciated!

thanks, marios

[PATCH 1/4] Network API rev 2 (RFC) - Models

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


Signed-off-by: marios <ma...@redhat.com>
---
 server/lib/deltacloud/models.rb          |  2 ++
 server/lib/deltacloud/models/instance.rb |  1 +
 server/lib/deltacloud/models/network.rb  | 25 +++++++++++++++++++++++++
 server/lib/deltacloud/models/subnet.rb   | 27 +++++++++++++++++++++++++++
 4 files changed, 55 insertions(+)
 create mode 100644 server/lib/deltacloud/models/network.rb
 create mode 100644 server/lib/deltacloud/models/subnet.rb

diff --git a/server/lib/deltacloud/models.rb b/server/lib/deltacloud/models.rb
index e6020e6..7dbf3dc 100644
--- a/server/lib/deltacloud/models.rb
+++ b/server/lib/deltacloud/models.rb
@@ -32,3 +32,5 @@ require_relative 'models/realm'
 require_relative 'models/state_machine'
 require_relative 'models/storage_snapshot'
 require_relative 'models/storage_volume'
+require_relative 'models/network'
+require_relative 'models/subnet'
diff --git a/server/lib/deltacloud/models/instance.rb b/server/lib/deltacloud/models/instance.rb
index 6c6b018..e88e5f0 100644
--- a/server/lib/deltacloud/models/instance.rb
+++ b/server/lib/deltacloud/models/instance.rb
@@ -34,6 +34,7 @@ module Deltacloud
     attr_accessor :create_image
     attr_accessor :firewalls
     attr_accessor :storage_volumes
+    attr_accessor :network_bindings
 
     def to_hash(context)
       r = {
diff --git a/server/lib/deltacloud/models/network.rb b/server/lib/deltacloud/models/network.rb
new file mode 100644
index 0000000..e8a9082
--- /dev/null
+++ b/server/lib/deltacloud/models/network.rb
@@ -0,0 +1,25 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you 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 Deltacloud
+class Network < BaseModel
+
+  attr_accessor :name
+  attr_accessor :subnets
+  attr_accessor :address_blocks
+  attr_accessor :state
+
+end
+end
diff --git a/server/lib/deltacloud/models/subnet.rb b/server/lib/deltacloud/models/subnet.rb
new file mode 100644
index 0000000..d91a7a9
--- /dev/null
+++ b/server/lib/deltacloud/models/subnet.rb
@@ -0,0 +1,27 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you 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 Deltacloud
+class Subnet < BaseModel
+
+  attr_accessor :name
+  attr_accessor :network
+  attr_accessor :address_block
+  attr_accessor :state
+  attr_accessor :type
+
+end
+end
-- 
1.8.1.4


[PATCH 3/4] Network API rev 2 (RFC) - Views

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


Signed-off-by: marios <ma...@redhat.com>
---
 server/views/instances/new.html.haml  |  9 +++++++++
 server/views/instances/show.html.haml |  9 +++++++++
 server/views/networks/index.html.haml | 10 ++++++++++
 server/views/networks/index.xml.haml  |  4 ++++
 server/views/networks/new.html.haml   | 10 ++++++++++
 server/views/networks/show.html.haml  | 24 ++++++++++++++++++++++++
 server/views/networks/show.xml.haml   | 15 +++++++++++++++
 server/views/subnets/index.html.haml  | 10 ++++++++++
 server/views/subnets/index.xml.haml   |  4 ++++
 server/views/subnets/new.html.haml    | 14 ++++++++++++++
 server/views/subnets/show.html.haml   | 24 ++++++++++++++++++++++++
 server/views/subnets/show.xml.haml    | 13 +++++++++++++
 12 files changed, 146 insertions(+)
 create mode 100644 server/views/networks/index.html.haml
 create mode 100644 server/views/networks/index.xml.haml
 create mode 100644 server/views/networks/new.html.haml
 create mode 100644 server/views/networks/show.html.haml
 create mode 100644 server/views/networks/show.xml.haml
 create mode 100644 server/views/subnets/index.html.haml
 create mode 100644 server/views/subnets/index.xml.haml
 create mode 100644 server/views/subnets/new.html.haml
 create mode 100644 server/views/subnets/show.html.haml
 create mode 100644 server/views/subnets/show.xml.haml

diff --git a/server/views/instances/new.html.haml b/server/views/instances/new.html.haml
index f6f674e..e22a110 100644
--- a/server/views/instances/new.html.haml
+++ b/server/views/instances/new.html.haml
@@ -84,6 +84,15 @@
               %input{ :type => :text, :id => "path#{i}", :name => "path#{i}", :value => ""} Path #{i}
               %input{ :type => "file", :name => "content#{i}", :size => 50 }
 
+        - if driver.has_capability? :networks
+          %div{ 'data-role' => :fieldcontain }
+            %label{ :for => :network, :class => 'ui-input-text'} Network to Launch Instance into:
+            %select{:name => 'network_id', :'data-native-menu' => "true" }
+              %option{ :value => ''} None
+              - @networks.each do |net|
+                - net.subnets.each do |sn|
+                  %option{ :value => "#{net.id}+#{sn}" } #{net.id}+#{sn}
+
     - if !@hardware_profiles.empty?
       %div{ 'data-role' => :fieldcontain }
         %h3 Instance profile
diff --git a/server/views/instances/show.html.haml b/server/views/instances/show.html.haml
index ae193aa..37446bf 100644
--- a/server/views/instances/show.html.haml
+++ b/server/views/instances/show.html.haml
@@ -56,6 +56,15 @@
       -@instance.storage_volumes.each do |vol|
         %li
           %a{ :href => storage_volume_url("#{vol.keys.first}"), :'data-ajax' => 'false'}=["#{vol.keys.first}", "#{vol.values.first}"].compact.reject{ |e| e.empty? }.join(' <---> ')
+    - if @instance.network_bindings
+      %li{ :'data-role' => 'list-divider'} Network Bindings
+      -@instance.network_bindings.each do |net_bind|
+        %li
+          %a{ :href => network_url("#{net_bind[:network]}"), :'data-ajax' => 'false'}="Network #{net_bind[:network]}"
+        %li
+          %a{ :href => subnet_url("#{net_bind[:subnet]}"), :'data-ajax' => 'false'}="Subnet #{net_bind[:subnet]}"
+        %li="Address #{net_bind[:ip_address]}"
+        %li
     %li{ :'data-role' => 'list-divider'} Actions
     %li
       %div{ :'data-role' => 'controlgroup', :'data-type' => "horizontal" }
diff --git a/server/views/networks/index.html.haml b/server/views/networks/index.html.haml
new file mode 100644
index 0000000..aa0050b
--- /dev/null
+++ b/server/views/networks/index.html.haml
@@ -0,0 +1,10 @@
+=header "Networks" do
+  %a{ :href => url_for('networks/new'), :'data-icon' => :plus, :'data-role' => :button, :class => 'ui-btn-right'} Create new network
+
+%div{ :'data-role' => :content, :'data-theme' => 'c'}
+  %ul{ :'data-role' => :listview , :'data-inset' => :true, :'data-divider-theme' => 'a'}
+    - @elements.each do |net|
+      %li
+        %a{ :href => network_url(net.id), :'data-ajax' => 'false'}
+          %img{ :class => 'ui-link-thumb', :src => '/images/cloud.png'}
+          %h3=net.id
diff --git a/server/views/networks/index.xml.haml b/server/views/networks/index.xml.haml
new file mode 100644
index 0000000..f2eaac6
--- /dev/null
+++ b/server/views/networks/index.xml.haml
@@ -0,0 +1,4 @@
+!!!XML
+%networks
+  - @elements.each do |c|
+    = haml :'networks/show', :locals => { :@network => c, :partial => true }
diff --git a/server/views/networks/new.html.haml b/server/views/networks/new.html.haml
new file mode 100644
index 0000000..0087259
--- /dev/null
+++ b/server/views/networks/new.html.haml
@@ -0,0 +1,10 @@
+=header "Create new network"
+
+%div{ :'data-role' => :content, :'data-theme' => 'c', :class => 'middle-dialog'}
+  %form{ :action => networks_url, :method => :post}
+    %div{ 'data-role' => :fieldcontain }
+      %p
+        %label{ :for => :address_block} CIDR Address block (optional):
+      %p
+        %input{ :type => :text, :id => :address_block, :name => :address_block, :value => '' }
+    %button{ :type => :submit} Create network
diff --git a/server/views/networks/show.html.haml b/server/views/networks/show.html.haml
new file mode 100644
index 0000000..18fef78
--- /dev/null
+++ b/server/views/networks/show.html.haml
@@ -0,0 +1,24 @@
+=header "Network"
+=subheader @network.id
+
+%div{ :'data-role' => :content, :'data-theme' => 'c'}
+  %ul{ :'data-role' => :listview , :'data-inset' => :true, :'data-divider-theme' => 'd'}
+    %li{ :'data-role' => 'list-divider'} Identifier
+    %li
+      %p{ :'data-role' => 'fieldcontain'}=@network.id
+    %li{ :'data-role' => 'list-divider'} Name
+    %li
+      %p{ :'data-role' => 'fieldcontain'}=@network.name
+    %li{ :'data-role' => 'list-divider'} State
+    %li
+      %p{ :'data-role' => 'fieldcontain'}=@network.state
+    %li{ :'data-role' => 'list-divider'} Address Blocks
+    %li
+      %p{ :'data-role' => 'fieldcontain'}=(@network.address_blocks ? @network.address_blocks.join(",") : nil)
+    %li{ :'data-role' => 'list-divider'} Subnets
+    %li
+      %p{ :'data-role' => 'fieldcontain'}= (@network.subnets ? @network.subnets.join(",") : nil)
+    %li{ :'data-role' => 'list-divider'} Actions
+    %li
+      %div{ :'data-role' => 'controlgroup', :'data-type' => "horizontal" }
+        =link_to_action "Destroy", destroy_network_url(@network.id), :delete
diff --git a/server/views/networks/show.xml.haml b/server/views/networks/show.xml.haml
new file mode 100644
index 0000000..0ebb170
--- /dev/null
+++ b/server/views/networks/show.xml.haml
@@ -0,0 +1,15 @@
+- unless defined?(partial)
+  !!! XML
+%network{ :href => network_url(@network.id), :id => @network.id }
+  %name=@network.name
+  %state<
+    =@network.state
+  %address_blocks
+    - @network.address_blocks.each do |addr_block|
+      %address_block=addr_block
+  %subnets
+    - (@network.subnets || []).each do |subnet|
+      %subnet{:href => subnet_url(subnet), :id=>subnet}
+  %actions
+    - if driver.respond_to?(:destroy_network)
+      %link{ :rel => "destroy", :method => "delete", :href => destroy_network_url(@network.id)}
diff --git a/server/views/subnets/index.html.haml b/server/views/subnets/index.html.haml
new file mode 100644
index 0000000..5442c17
--- /dev/null
+++ b/server/views/subnets/index.html.haml
@@ -0,0 +1,10 @@
+=header "Subnets" do
+  %a{ :href => url_for('subnets/new'), :'data-icon' => :plus, :'data-role' => :button, :class => 'ui-btn-right'} Create new subnet
+
+%div{ :'data-role' => :content, :'data-theme' => 'c'}
+  %ul{ :'data-role' => :listview , :'data-inset' => :true, :'data-divider-theme' => 'a'}
+    - @elements.each do |subnet|
+      %li
+        %a{ :href => subnet_url(subnet.id), :'data-ajax' => 'false'}
+          %img{ :class => 'ui-link-thumb', :src => '/images/cloud.png'}
+          %h3=subnet.id
diff --git a/server/views/subnets/index.xml.haml b/server/views/subnets/index.xml.haml
new file mode 100644
index 0000000..17f66b4
--- /dev/null
+++ b/server/views/subnets/index.xml.haml
@@ -0,0 +1,4 @@
+!!!XML
+%subnets
+  - @elements.each do |c|
+    = haml :'subnets/show', :locals => { :@subnet => c, :partial => true }
diff --git a/server/views/subnets/new.html.haml b/server/views/subnets/new.html.haml
new file mode 100644
index 0000000..c542d8e
--- /dev/null
+++ b/server/views/subnets/new.html.haml
@@ -0,0 +1,14 @@
+=header "Create new subnet"
+
+%div{ :'data-role' => :content, :'data-theme' => 'c', :class => 'middle-dialog'}
+  %form{ :action => subnets_url, :method => :post}
+    %div{ 'data-role' => :fieldcontain }
+      %p
+        %label{ :for => :network_id} Network ID:
+      %p
+        %input{ :type => :text, :id => :network_id, :name => :network_id, :value => '' }
+      %p
+        %label{ :for => :address_block} CIDR Address block:
+      %p
+        %input{ :type => :text, :id => :address_block, :name => :address_block, :value => '' }
+    %button{ :type => :submit} Create subnet
diff --git a/server/views/subnets/show.html.haml b/server/views/subnets/show.html.haml
new file mode 100644
index 0000000..e9cfadb
--- /dev/null
+++ b/server/views/subnets/show.html.haml
@@ -0,0 +1,24 @@
+=header "Subnet"
+=subheader @subnet.id
+
+%div{ :'data-role' => :content, :'data-theme' => 'c'}
+  %ul{ :'data-role' => :listview , :'data-inset' => :true, :'data-divider-theme' => 'd'}
+    %li{ :'data-role' => 'list-divider'} Identifier
+    %li
+      %p{ :'data-role' => 'fieldcontain'}=@subnet.id
+    %li{ :'data-role' => 'list-divider'} Name
+    %li
+      %p{ :'data-role' => 'fieldcontain'}=@subnet.name
+    %li{ :'data-role' => 'list-divider'} Network
+    %li
+      %a{ :href => network_url(@subnet.network), :'data-ajax'=>'false' }=@subnet.network
+    %li{ :'data-role' => 'list-divider'} State
+    %li
+      %p{ :'data-role' => 'fieldcontain'}=@subnet.state
+    %li{ :'data-role' => 'list-divider'} Address Block
+    %li
+      %p{ :'data-role' => 'fieldcontain'}=(@subnet.address_block ? @subnet.address_block : nil)
+    %li{ :'data-role' => 'list-divider'} Actions
+    %li
+      %div{ :'data-role' => 'controlgroup', :'data-type' => "horizontal" }
+        =link_to_action "Destroy", destroy_subnet_url(@subnet.id), :delete
diff --git a/server/views/subnets/show.xml.haml b/server/views/subnets/show.xml.haml
new file mode 100644
index 0000000..3c47e59
--- /dev/null
+++ b/server/views/subnets/show.xml.haml
@@ -0,0 +1,13 @@
+- unless defined?(partial)
+  !!! XML
+%subnet{ :href => subnet_url(@subnet.id), :id => @subnet.id }
+  %name=@subnet.name
+  %state<
+    =@subnet.state
+  %address_block
+    =@subnet.address_block
+  %network
+    =@subnet.network
+  %actions
+    - if driver.respond_to?(:destroy_subnet)
+      %link{ :rel => "destroy", :method => "delete", :href => destroy_subnet_url(@subnet.id)}
-- 
1.8.1.4


Re: Network-API-rev-2-RFC

Posted by "marios@redhat.com" <ma...@redhat.com>.
On 20/03/13 19:02, marios@redhat.com wrote:
> patches are at http://tracker.deltacloud.org/set/395
> 

updated patches are at http://tracker.deltacloud.org/set/396 - the
difference is that this new set includes mock driver functionality - the
new set is also independent/stand-alone (doesn't need to be applied on
top of the previous one)

thanks, marios


> write-up at http://mariosandreou.com/deltacloud/cloud_API/2013/03/20/deltacloud-networks-api-rfc.html
> 
> I'll be working on RHEV-M next - any comments are very much appreciated!
> 
> thanks, marios
> 


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

Posted by ma...@redhat.com.
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


[PATCH 2/4] Network API rev 2 (RFC) - Collections (Routes)

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


Signed-off-by: marios <ma...@redhat.com>
---
 server/lib/deltacloud/collections/instances.rb |  5 ++
 server/lib/deltacloud/collections/networks.rb  | 63 +++++++++++++++++++++++++
 server/lib/deltacloud/collections/subnets.rb   | 65 ++++++++++++++++++++++++++
 3 files changed, 133 insertions(+)
 create mode 100644 server/lib/deltacloud/collections/networks.rb
 create mode 100644 server/lib/deltacloud/collections/subnets.rb

diff --git a/server/lib/deltacloud/collections/instances.rb b/server/lib/deltacloud/collections/instances.rb
index ee48f30..ecd6d00 100644
--- a/server/lib/deltacloud/collections/instances.rb
+++ b/server/lib/deltacloud/collections/instances.rb
@@ -29,6 +29,7 @@ module Deltacloud::Collections
       @realms ||= driver.realms(credentials)
       @firewalls = driver.firewalls(credentials) if driver.class.has_feature? :instances, :firewalls
       @keys = driver.keys(credentials) if driver.class.has_feature? :instances, :authentication_key
+      @networks = driver.networks(credentials) if driver.has_capability? :networks
     end
 
     get '/instances/:id/run' do
@@ -48,7 +49,11 @@ module Deltacloud::Collections
         param :realm_id,     :string, :optional
         param :hwp_id,       :string, :optional
         param :keyname,      :string, :optional
+        param :network_id,   :string, :optional
+        param :subnet_id,    :string, :optional
         control do
+          params.merge!({:network_id => params["network_id"].split("+").first}) unless params["network_id"].empty?
+          params.merge!({:subnet_id => params["network_id"].split("+").last}) unless params["network_id"].empty?
           @instance = driver.create_instance(credentials, params[:image_id], params)
           if @instance.kind_of? Array
             @elements = @instance
diff --git a/server/lib/deltacloud/collections/networks.rb b/server/lib/deltacloud/collections/networks.rb
new file mode 100644
index 0000000..34c7fad
--- /dev/null
+++ b/server/lib/deltacloud/collections/networks.rb
@@ -0,0 +1,63 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you 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 Deltacloud::Collections
+  class Networks < Base
+
+    include Deltacloud::Features
+
+    set :capability, lambda { |m| driver.respond_to? m }
+    check_features :for => lambda { |c, f| driver.class.has_feature?(c, f) }
+
+    get '/networks/new' do
+      respond_to do |format|
+        format.html { haml :"networks/new" }
+      end
+    end
+
+    collection :networks do
+
+      standard_show_operation
+      standard_index_operation
+
+      operation :create, :with_capability => :create_network do
+        param :address_block, :string, :optional
+        param :name,          :string, :optional
+        control do
+          @network = driver.create_network(credentials, { :address_block => params[:address_block]})
+          respond_to do |format|
+            format.xml  { haml :"networks/show" }
+            format.html { haml :"networks/show" }
+            format.json { xml_to_json("networks/show")}
+          end
+        end
+      end
+
+      operation :destroy, :with_capability => :destroy_network do
+        control do
+          driver.destroy_network(credentials, params[:id])
+          status 204
+          respond_to do |format|
+            format.xml
+            format.json
+            format.html { redirect(networks_url) }
+          end
+        end
+      end
+
+    end
+
+  end
+end
diff --git a/server/lib/deltacloud/collections/subnets.rb b/server/lib/deltacloud/collections/subnets.rb
new file mode 100644
index 0000000..9d5fd74
--- /dev/null
+++ b/server/lib/deltacloud/collections/subnets.rb
@@ -0,0 +1,65 @@
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you 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 Deltacloud::Collections
+  class Subnets < Base
+
+    include Deltacloud::Features
+
+    set :capability, lambda { |m| driver.respond_to? m }
+    check_features :for => lambda { |c, f| driver.class.has_feature?(c, f) }
+
+    get '/subnets/new' do
+      respond_to do |format|
+        format.html { haml :"subnets/new" }
+      end
+    end
+
+
+    collection :subnets do
+
+      standard_show_operation
+      standard_index_operation
+
+      operation :create, :with_capability => :create_subnet do
+        param :network_id, :string, :required
+        param :address_block,  :string,  :required
+        control do
+          @subnet = driver.create_subnet(credentials, { :network_id => params[:network_id], :address_block => params[:address_block]})
+          respond_to do |format|
+            format.xml  { haml :"subnets/show"}
+            format.html { haml :"subnets/show" }
+            format.json { xml_to_json("subnets/show")}
+          end
+        end
+      end
+
+      operation :destroy, :with_capability => :destroy_subnet do
+        control do
+          driver.destroy_subnet(credentials, params[:id])
+          status 204
+          respond_to do |format|
+            format.xml
+            format.json
+            format.html { redirect(subnets_url) }
+          end
+        end
+      end
+
+    end
+
+  end
+end
-- 
1.8.1.4