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 2011/06/10 16:43:54 UTC

[PATCH 1/3] Implements 'firewalls' - ec2 security groups

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


Signed-off-by: marios <ma...@redhat.com>
---
 server/Gemfile                                   |    3 +-
 server/deltacloud.rb                             |    2 +
 server/lib/deltacloud/base_driver/base_driver.rb |   14 ++-
 server/lib/deltacloud/base_driver/features.rb    |    8 +-
 server/lib/deltacloud/drivers/ec2/ec2_driver.rb  |  149 +++++++++++++++++++++-
 server/lib/deltacloud/models/bucket.rb           |    2 +-
 server/lib/deltacloud/models/firewall.rb         |   22 +++
 server/lib/deltacloud/models/firewall_rule.rb    |   23 ++++
 server/public/javascripts/application.js         |   63 +++++++--
 server/server.rb                                 |  112 ++++++++++++++++
 server/views/firewalls/index.html.haml           |   25 ++++
 server/views/firewalls/index.xml.haml            |   23 ++++
 server/views/firewalls/new.html.haml             |   11 ++
 server/views/firewalls/new_rule.html.haml        |   26 ++++
 server/views/firewalls/show.html.haml            |   44 +++++++
 server/views/firewalls/show.xml.haml             |   21 +++
 16 files changed, 522 insertions(+), 26 deletions(-)
 create mode 100644 server/lib/deltacloud/models/firewall.rb
 create mode 100644 server/lib/deltacloud/models/firewall_rule.rb
 create mode 100644 server/views/firewalls/index.html.haml
 create mode 100644 server/views/firewalls/index.xml.haml
 create mode 100644 server/views/firewalls/new.html.haml
 create mode 100644 server/views/firewalls/new_rule.html.haml
 create mode 100644 server/views/firewalls/show.html.haml
 create mode 100644 server/views/firewalls/show.xml.haml

diff --git a/server/Gemfile b/server/Gemfile
index 3ab3d5c..ee5f805 100644
--- a/server/Gemfile
+++ b/server/Gemfile
@@ -15,7 +15,8 @@ group :azure do
 end
 
 group :ec2 do
-  gem 'aws'
+  gem 'aws', ">= 2.5.4"
+
 end
 
 group :gogrid do
diff --git a/server/deltacloud.rb b/server/deltacloud.rb
index 7caf34f..5628e31 100644
--- a/server/deltacloud.rb
+++ b/server/deltacloud.rb
@@ -36,6 +36,8 @@ require 'deltacloud/models/storage_volume'
 require 'deltacloud/models/bucket'
 require 'deltacloud/models/blob'
 require 'deltacloud/models/load_balancer'
+require 'deltacloud/models/firewall'
+require 'deltacloud/models/firewall_rule'
 
 require 'deltacloud/validation'
 require 'deltacloud/helpers'
diff --git a/server/lib/deltacloud/base_driver/base_driver.rb b/server/lib/deltacloud/base_driver/base_driver.rb
index 2c780d9..7f8bd65 100644
--- a/server/lib/deltacloud/base_driver/base_driver.rb
+++ b/server/lib/deltacloud/base_driver/base_driver.rb
@@ -149,7 +149,12 @@ module Deltacloud
     # def keys(credentials, opts)
     # def create_key(credentials, opts)
     # def destroy_key(credentials, opts)
-
+    #
+    # def firewalls(credentials, opts)
+    # def create_firewall(credentials, opts)
+    # def delete_firewall(credentials, opts)
+    # def create_firewall_rule(credentials, opts)
+    # def delete_firewall_rule(credentials, opts)
     def realm(credentials, opts)
       realms = realms(credentials, opts).first if has_capability?(:realms)
     end
@@ -183,8 +188,12 @@ module Deltacloud
       keys(credentials, opts).first if has_capability?(:keys)
     end
 
+    def firewall(credentials, opts={})
+      firewalls(credentials, opts).first if has_capability?(:firewalls)
+    end
+
     MEMBER_SHOW_METHODS =
-      [ :realm, :image, :instance, :storage_volume, :bucket, :blob, :key ]
+      [ :realm, :image, :instance, :storage_volume, :bucket, :blob, :key, :firewall ]
 
     def has_capability?(capability)
       if MEMBER_SHOW_METHODS.include?(capability.to_sym)
@@ -194,7 +203,6 @@ module Deltacloud
       end
     end
 
-
     def filter_on(collection, attribute, opts)
       return collection if opts.nil?
       return collection if opts[attribute].nil?
diff --git a/server/lib/deltacloud/base_driver/features.rb b/server/lib/deltacloud/base_driver/features.rb
index b5bc3ee..4a2f1f8 100644
--- a/server/lib/deltacloud/base_driver/features.rb
+++ b/server/lib/deltacloud/base_driver/features.rb
@@ -187,11 +187,11 @@ module Deltacloud
       end
     end
 
-    declare_feature :instances, :security_group do
-      description "Put instance in one or more security groups on launch"
+    declare_feature :instances, :firewall do
+      description "Put instance in one or more firewalls (security groups) on launch"
       operation :create do
-        param :security_group, :array, :optional, [],
-        "Array of security group names"
+        param :firewalls, :array, :optional, nil, "Array of firewall ID strings"
+        "Array of firewall (security group) id"
       end
     end
 
diff --git a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
index 4edd989..fbf39f1 100644
--- a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
+++ b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
@@ -33,12 +33,13 @@ module Deltacloud
       class EC2Driver < Deltacloud::BaseDriver
 
         def supported_collections
-          DEFAULT_COLLECTIONS + [ :keys, :buckets, :load_balancers, :addresses ]
+
+          DEFAULT_COLLECTIONS + [ :keys, :buckets, :load_balancers, :addresses, :firewalls ]
         end
 
         feature :instances, :user_data
         feature :instances, :authentication_key
-        feature :instances, :security_group
+        feature :instances, :firewall
         feature :instances, :instance_count
         feature :images, :owner_id
         feature :buckets, :bucket_location
@@ -201,7 +202,7 @@ module Deltacloud
           instance_options.merge!(:key_name => opts[:keyname]) if opts[:keyname]
           instance_options.merge!(:availability_zone => opts[:realm_id]) if opts[:realm_id]
           instance_options.merge!(:instance_type => opts[:hwp_id]) if opts[:hwp_id] && opts[:hwp_id].length > 0
-          instance_options.merge!(:group_ids => opts[:security_group]) if opts[:security_group]
+          instance_options.merge!(:group_ids => opts[:firewalls]) if opts[:firewalls]
           instance_options.merge!(
             :min_count => opts[:instance_count],
             :max_count => opts[:instance_count]
@@ -571,6 +572,73 @@ module Deltacloud
           end
         end
 
+#--
+#FIREWALLS - ec2 security groups
+#--
+      def firewalls(credentials, opts={})
+          ec2 = new_client(credentials)
+          the_firewalls = []
+          groups = []
+          safely do
+            if opts[:id]
+              groups = ec2.describe_security_groups([opts[:id]])
+            else
+              groups = ec2.describe_security_groups()
+            end
+          end
+          groups.each do |security_group|
+            the_firewalls << convert_security_group(security_group)
+          end
+          the_firewalls
+      end
+
+#--
+#Create firewall
+#--
+        def create_firewall(credentials, opts={})
+          ec2 = new_client(credentials)
+          safely do
+            ec2.create_security_group(opts["name"], opts["description"])
+          end
+          Firewall.new( { :id=>opts["name"], :name=>opts["name"],
+                          :description => opts["description"], :owner_id => "", :rules => [] } )
+        end
+
+#--
+#Delete firewall
+#--
+        def delete_firewall(credentials, opts={})
+          ec2 = new_client(credentials)
+          safely do
+            ec2.delete_security_group(opts["id"])
+          end
+        end
+#--
+#Create firewall rule
+#--
+        def create_firewall_rule(credentials, opts={})
+          ec2 = new_client(credentials)
+          groups = []
+          opts['groups'].each do |k,v|
+            groups << {"group_name" => k, "owner" =>v}
+          end
+          safely do
+            ec2.manage_security_group_ingress(opts['id'], opts['from_port'], opts['to_port'], opts['protocol'],
+              "authorize", opts['addresses'], groups)
+          end
+        end
+#--
+#Delete firewall rule
+#--
+        def delete_firewall_rule(credentials, opts={})
+          ec2 = new_client(credentials)
+          firewall = opts[:id]
+          protocol, from_port, to_port, addresses, groups = firewall_rule_params(opts[:rule_id])
+          safely do
+            ec2.manage_security_group_ingress(firewall, from_port, to_port, protocol, "revoke", addresses, groups)
+          end
+        end
+
         def valid_credentials?(credentials)
           retval = true
           begin
@@ -764,6 +832,81 @@ module Deltacloud
           balancer
         end
 
+        #generate uid from firewall rule parameters (amazon doesn't do this for us
+        def firewall_rule_id(user_id, protocol, from_port, to_port, sources)
+          sources_string = ""
+          sources.each do |source|
+            sources_string<<"@"
+            source.each_pair do |key,value|
+              sources_string<< "#{value},"
+            end
+            sources_string.chomp!(",")
+          end
+         #sources_string is @group,297467797945,test@address,ipv4,10.1.1.1,24 etc
+         id_string = "#{user_id}~#{protocol}~#{from_port}~#{to_port}~#{sources_string}"
+        end
+
+        #extract params from uid
+        def firewall_rule_params(id)
+          #user_id~protocol~from_port~to_port~sources_string
+          params = id.split("~")
+          protocol = params[1]
+          from_port = params[2]
+          to_port = params[3]
+          sources = params[4].split("@")
+          sources.shift #first match is ""
+          addresses = []
+          groups = []
+          #@group,297467797945,test@address,ipv4,10.1.1.1,24@address,ipv4,192.168.1.1,24
+          sources.each do |source|
+            current = source.split(",")
+            type = current[0]
+            case type
+              when 'group'
+                #group,297467797945,test
+                owner = current[1]
+                name = current[2]
+                groups << {'group_name' => name, 'owner' => owner}
+              when 'address'
+                #address,ipv4,10.1.1.1,24
+                address = current[2]
+                address<<"/#{current[3]}"
+                addresses << address
+            end
+          end
+          return protocol, from_port, to_port, addresses, groups
+        end
+
+        #Convert ec2 security group to server/lib/deltacloud/models/firewall
+        def convert_security_group(security_group)
+          rules = []
+          security_group[:aws_perms].each do |perm|
+            sources = []
+            perm[:groups].each do |group|
+              sources << {:type => "group", :name => group[:group_name], :owner => group[:owner]}
+            end
+            perm[:ip_ranges].each do |ip|
+              sources << {:type => "address", :family=>"ipv4",
+                          :address=>ip[:cidr_ip].split("/").first,
+                          :prefix=>ip[:cidr_ip].split("/").last}
+            end
+            rule_id = firewall_rule_id(security_group[:aws_owner], perm[:protocol],
+                                       perm[:from_port] , perm[:to_port], sources)
+            rules << FirewallRule.new({:id => rule_id,
+                                        :allow_protocol => perm[:protocol],
+                                        :port_from => perm[:from_port],
+                                        :port_to => perm[:to_port],
+                                        :direction => 'ingress',
+                                        :sources => sources})
+          end
+          Firewall.new(  {  :id => security_group[:aws_group_name],
+                            :name => security_group[:aws_group_name],
+                            :description => security_group[:aws_description],
+                            :owner_id => security_group[:aws_owner],
+                            :rules => rules
+                      }  )
+        end
+
         def convert_state(ec2_state)
           case ec2_state
             when "terminated"
diff --git a/server/lib/deltacloud/models/bucket.rb b/server/lib/deltacloud/models/bucket.rb
index e6b2b34..bef36ac 100644
--- a/server/lib/deltacloud/models/bucket.rb
+++ b/server/lib/deltacloud/models/bucket.rb
@@ -24,7 +24,7 @@ class Bucket < BaseModel
 
   def to_hash
     h = self.to_hash_original
-    h[:blob_list] = self.blob_list.collect { |blob| { :id => blob, 
+    h[:blob_list] = self.blob_list.collect { |blob| { :id => blob,
       :href => "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/buckets/#{self.id}/#{blob.id}"}}
     return h
   end
diff --git a/server/lib/deltacloud/models/firewall.rb b/server/lib/deltacloud/models/firewall.rb
new file mode 100644
index 0000000..dc0ae3d
--- /dev/null
+++ b/server/lib/deltacloud/models/firewall.rb
@@ -0,0 +1,22 @@
+#
+# 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.
+
+class Firewall < BaseModel
+  attr_accessor :name
+  attr_accessor :description
+  attr_accessor :owner_id
+  attr_accessor :rules
+end
\ No newline at end of file
diff --git a/server/lib/deltacloud/models/firewall_rule.rb b/server/lib/deltacloud/models/firewall_rule.rb
new file mode 100644
index 0000000..3959eb6
--- /dev/null
+++ b/server/lib/deltacloud/models/firewall_rule.rb
@@ -0,0 +1,23 @@
+#
+# 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.
+
+class FirewallRule < BaseModel
+  attr_accessor :allow_protocol # tcp/udp/icmp
+  attr_accessor :port_from
+  attr_accessor :port_to
+  attr_accessor :sources
+  attr_accessor :direction #ingress egress
+end
diff --git a/server/public/javascripts/application.js b/server/public/javascripts/application.js
index 95c9bc2..703879c 100644
--- a/server/public/javascripts/application.js
+++ b/server/public/javascripts/application.js
@@ -19,10 +19,10 @@ $(document).ready(function() {
 
 function more_fields()
 {
-	//increment the hidden input that captures how many meta_data are passed
-	var meta_params = document.getElementsByName('meta_params')
+  //increment the hidden input that captures how many meta_data are passed
+  var meta_params = document.getElementsByName('meta_params')
     current_number_params = eval(meta_params[0].value)+1
-	meta_params[0].value = current_number_params
+  meta_params[0].value = current_number_params
     var new_meta = document.getElementById('metadata_holder').cloneNode(true);
     new_meta.id = 'metadata_holder' + current_number_params;
     new_meta.style.display = 'block';
@@ -39,15 +39,50 @@ function more_fields()
 function less_fields()
 {
     var meta_params = document.getElementsByName('meta_params')
-	current_val = eval(meta_params[0].value)
-	if (current_val == 0)
-	{
-		return;
-	}
-	else
-	{
-		var theDiv = document.getElementById('metadata_holder'+current_val)
-		theDiv.parentNode.removeChild(theDiv)
-		meta_params[0].value = eval(current_val)-1
-	}
+  current_val = eval(meta_params[0].value)
+  if (current_val == 0)
+  {
+    return;
+  }
+  else
+  {
+    var theDiv = document.getElementById('metadata_holder'+current_val)
+    theDiv.parentNode.removeChild(theDiv)
+    meta_params[0].value = eval(current_val)-1
+  }
+}
+
+var addresses = 0;
+var groups = 0;
+function make_fields(type)
+{
+  form = document.getElementById("new_rule_form")
+  button = document.getElementById("submit_button")
+  if(type == "address")
+  {
+    name = "ip_address" + eval(++addresses)
+    create_rule_source_field(name, "Address " + eval(addresses) + " [use CIDR notation 0.0.0.0/0]", form, button)
+  }
+  else if(type == "group")
+  {
+    name = "group" + eval(++groups)
+    create_rule_source_field(name, "Name of group " + eval(groups), form, button)
+    name = "group" + eval(groups) + "owner"
+    create_rule_source_field(name, "Group " + eval(groups) + " owner (required)", form, button)
+  }
+}
+
+function create_rule_source_field(name, label, form, button)
+{
+    element = document.createElement("INPUT")
+    element.type = "input"
+    element.size = 35
+    element.name = name
+    text = document.createTextNode(label)
+    form.insertBefore(element, button)
+    form.insertBefore(text, element)
+    form.insertBefore(document.createElement('BR'), element)
+    form.insertBefore(document.createElement('BR'), button)
+    form.insertBefore(document.createElement('BR'), button)
+    form.insertBefore(document.createElement('BR'), button)
 }
diff --git a/server/server.rb b/server/server.rb
index ba87c67..ae23263 100644
--- a/server/server.rb
+++ b/server/server.rb
@@ -948,3 +948,115 @@ collection :addresses do
   end
 
 end
+
+#html for creating a new firewall
+get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/firewalls/new" do
+  respond_to do |format|
+    format.html { haml :"firewalls/new" }
+  end
+end
+
+#html for creating a new firewall rule
+get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/firewalls/:firewall/new_rule" do
+  @firewall_name = params[:firewall]
+  respond_to do |format|
+    format.html {haml :"firewalls/new_rule" }
+  end
+end
+
+#FIREWALLS
+collection :firewalls do
+  description "Allow user to define firewall rules for an instance (ec2 security groups) eg expose ssh access [port 22, tcp]."
+  operation :index do
+    description 'List all firewalls'
+    with_capability :firewalls
+    control { filter_all(:firewalls) }
+  end
+
+  operation :show do
+    description 'Show details for a specific firewall - list all rules'
+    with_capability :firewall
+    param :id,            :string,    :required
+    control { show(:firewall) }
+  end
+
+  operation :create do
+    description 'Create a new firewall'
+    with_capability :create_firewall
+    param :name,          :string,    :required
+    param :description,   :string,    :required
+    control do
+      @firewall = driver.create_firewall(credentials, params )
+      respond_to do |format|
+        format.xml do
+          response.status = 201  # Created
+          haml :"firewalls/show"
+        end
+        format.html {haml :"firewalls/show"}
+        format.json {convert_to_json(:firewall, @firewall)}
+      end
+    end
+  end
+
+  operation :destroy do
+    description 'Delete a specified firewall - error if firewall has rules'
+    with_capability :delete_firewall
+    param :id,            :string,    :required
+    control do
+      driver.delete_firewall(credentials, params)
+      respond_to do |format|
+        format.xml { 204 }
+        format.json {  204 }
+        format.html {  redirect(firewalls_url) }
+      end
+    end
+  end
+
+#create a new firewall rule - POST /api/firewalls/:firewall/rules
+  operation :rules, :method => :post, :member => true do
+    description 'Create a new firewall rule for the specified firewall'
+    param :firewall,  :required, :string, [],  "Name of firewall in which to apply this rule"
+    param :protocol,  :required, :string, ['tcp','udp','icmp'], "Transport layer protocol for the rule"
+    param :from_port, :required, :string, [], "Start of port range for the rule"
+    param :to_port,   :required, :string, [], "End of port range for the rule"
+    with_capability :create_firewall_rule
+    control do
+      #source IPs from params
+      addresses =  params.inject([]){|result,current| result << current.last unless current.grep(/^ip[-_]address/i).empty?; result}
+      #source groups from params
+      groups = {}
+      max_groups  = params.select{|k,v| k=~/^group/}.size/2
+      for i in (1..max_groups) do
+        groups.merge!({params["group#{i}"]=>params["group#{i}owner"]})
+      end
+      params.merge!( {'addresses' => addresses} ) ; params.merge!( {'groups' => groups} )
+      driver.create_firewall_rule(credentials, params)
+      @firewall = driver.firewall(credentials, {:id => params[:firewall]})
+      respond_to do |format|
+        format.html {haml :"firewalls/show"}
+        format.xml do
+          response.status = 201 #created
+          haml :"firewall/show"
+        end
+        format.json {convert_to_json(:firewall, @firewall)}
+      end
+    end
+  end
+
+#delete a firewall rule DELETE /api/firewalls/:firewall/rule - with param rule_id
+  operation :rule, :method => :delete, :member => true do
+    description 'Delete the specified firewall rule from the given firewall'
+    param :firewall, :required, :string
+    param :rule_id,  :required, :string
+    with_capability :delete_firewall_rule
+    control do
+      driver.delete_firewall_rule(credentials, params)
+      respond_to do |format|
+        format.html {redirect firewall_url(params[:id])}
+        format.xml {204}
+        format.json {204}
+      end
+    end
+  end
+
+end #firewalls
diff --git a/server/views/firewalls/index.html.haml b/server/views/firewalls/index.html.haml
new file mode 100644
index 0000000..3312a32
--- /dev/null
+++ b/server/views/firewalls/index.html.haml
@@ -0,0 +1,25 @@
+%h1 Firewalls
+%br
+%p
+  =link_to 'Create new firewall', "/api/firewalls/new"
+%table.display
+  %thead
+    %tr
+      %th Id
+      %th Name
+      %th Description
+      %th Owner ID
+      %th Rules
+  %tbody
+    - @firewalls.each do |firewall|
+      %tr
+        %td
+          = link_to firewall.id, firewall_url(firewall.id)
+        %td
+          = firewall.name
+        %td
+          = firewall.description
+        %td
+          = firewall.owner_id
+        %td
+          = link_to 'view rules', firewall_url(firewall.id)
diff --git a/server/views/firewalls/index.xml.haml b/server/views/firewalls/index.xml.haml
new file mode 100644
index 0000000..f027785
--- /dev/null
+++ b/server/views/firewalls/index.xml.haml
@@ -0,0 +1,23 @@
+!!! XML
+%firewalls
+  - @firewalls.each do |firewall|
+    %firewall{:href => firewall_url(firewall.id), :id => firewall.id}
+      - firewall.attributes.select{ |attr| attr != :id && attr!= :rules}.each do |attribute|
+        - haml_tag("#{attribute}".tr('-', '_'), :<) do
+          - if [:name, :description].include?(attribute)
+            =cdata do
+              - haml_concat firewall.send(attribute)
+          - else
+            - haml_concat firewall.send(attribute)
+      %rules
+        - firewall.rules.each do |rule|
+          %rule{:id => rule.id}
+            - rule.attributes.select{|attr| attr != :sources && attr != :id}.each do |rule_attrib|
+              - haml_tag("#{rule_attrib}".tr('-', '_'), :<) do
+                - haml_concat rule.send(rule_attrib)
+            %sources
+              - rule.sources.each do |source|
+                - if source[:type] == "group"
+                  %source{:name => source[:name], :type=> source[:type], :owner=> source[:owner]}
+                - else
+                  %source{:prefix => source[:prefix], :address=> source[:address], :family=>source[:family], :type => source[:type]}
\ No newline at end of file
diff --git a/server/views/firewalls/new.html.haml b/server/views/firewalls/new.html.haml
new file mode 100644
index 0000000..4a230a6
--- /dev/null
+++ b/server/views/firewalls/new.html.haml
@@ -0,0 +1,11 @@
+%h1 New Firewall
+
+%form{:action => firewalls_url, :method => :post}
+  %label
+    Firewall Name
+    %input{:name => 'name', :size => 25}/
+    %br
+  %label
+    Firewall Description
+    %input{:name => 'description', :size => 100}/
+  %input{:type => :submit, :name => "commit", :value=>"create"}
\ No newline at end of file
diff --git a/server/views/firewalls/new_rule.html.haml b/server/views/firewalls/new_rule.html.haml
new file mode 100644
index 0000000..b25206a
--- /dev/null
+++ b/server/views/firewalls/new_rule.html.haml
@@ -0,0 +1,26 @@
+%h1 New Firewall Rule
+
+%form{ :action => "#{firewall_url(@firewall_name)}/rules", :id => "new_rule_form", :method => :post, :enctype => 'multipart/form-data'}
+  %label
+    Protocol:
+    %br
+    %input{ :name => 'protocol', :size => 10}/
+    %br
+    %br
+  %label
+    From port:
+    %br
+    %input{ :name => 'from_port', :size => 10}/
+    %br
+    %br
+    To port:
+    %br
+    %input{ :name => 'to_port', :size => 10}/
+    %br
+    %br
+  %a{ :href => "javascript:;", :onclick => "make_fields('address');"} Add source IP address
+  %br
+  %a{ :href => "javascript:;", :onclick => "make_fields('group');"} Add source group
+  %br
+  %br
+  %input{ :type => :submit, :id => "submit_button", :name => "commit", :value => "create"}/
diff --git a/server/views/firewalls/show.html.haml b/server/views/firewalls/show.html.haml
new file mode 100644
index 0000000..b77aaa4
--- /dev/null
+++ b/server/views/firewalls/show.html.haml
@@ -0,0 +1,44 @@
+%h1 Firewall
+%h2
+  = @firewall.id
+%dl
+  %di
+    %dt Name
+    %dd
+      = @firewall.name
+    %dt Owner
+    %dd
+      = @firewall.owner_id
+    %dt Description
+    %dd
+      = @firewall.description
+
+%h2
+  Rules
+  %br
+  %p
+    =link_to 'Create a new rule', "/api/firewalls/#{@firewall.name}/new_rule"
+%dl
+  - @firewall.rules.each do |rule|
+    %di
+      Rule
+      - rule.attributes.select{|attr| attr != :sources}.each do |attrib|
+        %dt #{attrib}
+        %dd
+          = rule.send(attrib)
+      %dt sources
+      %dd
+        - rule.sources.each do |source|
+          - if source[:type] == "group"
+            type: #{source[:type]}, name: #{source[:name]}, owner: #{source[:owner]}
+            %br
+          - else
+            type: #{source[:type]}, family: #{source[:family]}, address: #{source[:address]}, prefix: #{source[:prefix]}
+            %br
+      %dd
+        %form{ :action => "#{firewall_url(@firewall.name)}/rule", :method => :post}
+          %input{:type => "hidden", :name => "_method", :value => "delete"}
+          %input{:type => "hidden", :name => "rule_id", :value => rule.id}
+          %input{:type => :submit, :value => "Delete Rule"}
+  %dd
+    = link_to_action 'Delete Firewall', destroy_firewall_url(@firewall.name), :delete
diff --git a/server/views/firewalls/show.xml.haml b/server/views/firewalls/show.xml.haml
new file mode 100644
index 0000000..9d1fc48
--- /dev/null
+++ b/server/views/firewalls/show.xml.haml
@@ -0,0 +1,21 @@
+!!! XML
+%firewall{:href => firewall_url(@firewall.id), :id => @firewall.id}
+  - @firewall.attributes.select{ |attr| attr != :id && attr!= :rules}.each do |attribute|
+    - haml_tag("#{attribute}".tr('-', '_'), :<) do
+      - if [:name, :description].include?(attribute)
+        =cdata do
+          - haml_concat @firewall.send(attribute)
+      - else
+        - haml_concat @firewall.send(attribute)
+  %rules
+    - @firewall.rules.each do |rule|
+      %rule{:id => rule.id}
+        - rule.attributes.select{|attr| attr != :sources && attr != :id}.each do |rule_attrib|
+          - haml_tag("#{rule_attrib}".tr('-', '_'), :<) do
+            - haml_concat rule.send(rule_attrib)
+        %sources
+          - rule.sources.each do |source|
+            - if source[:type] == "group"
+              %source{:name => source[:name], :type=> source[:type], :owner=>source[:owner]}
+            - else
+              %source{:prefix => source[:prefix], :address=> source[:address], :family=>source[:family], :type => source[:type]}
\ No newline at end of file
-- 
1.7.3.4


Re: [PATCH 1/3] Implements 'firewalls' - ec2 security groups

Posted by Michal Fojtik <mf...@redhat.com>.
On Jun 10, 2011, at 4:43 PM, marios@redhat.com wrote:

ACK.

A few minor indentation/whitespace doubts inline.

  -- Michal


> From: marios <ma...@redhat.com>
> 
> 
> Signed-off-by: marios <ma...@redhat.com>
> ---
> server/Gemfile                                   |    3 +-
> server/deltacloud.rb                             |    2 +
> server/lib/deltacloud/base_driver/base_driver.rb |   14 ++-
> server/lib/deltacloud/base_driver/features.rb    |    8 +-
> server/lib/deltacloud/drivers/ec2/ec2_driver.rb  |  149 +++++++++++++++++++++-
> server/lib/deltacloud/models/bucket.rb           |    2 +-
> server/lib/deltacloud/models/firewall.rb         |   22 +++
> server/lib/deltacloud/models/firewall_rule.rb    |   23 ++++
> server/public/javascripts/application.js         |   63 +++++++--
> server/server.rb                                 |  112 ++++++++++++++++
> server/views/firewalls/index.html.haml           |   25 ++++
> server/views/firewalls/index.xml.haml            |   23 ++++
> server/views/firewalls/new.html.haml             |   11 ++
> server/views/firewalls/new_rule.html.haml        |   26 ++++
> server/views/firewalls/show.html.haml            |   44 +++++++
> server/views/firewalls/show.xml.haml             |   21 +++
> 16 files changed, 522 insertions(+), 26 deletions(-)
> create mode 100644 server/lib/deltacloud/models/firewall.rb
> create mode 100644 server/lib/deltacloud/models/firewall_rule.rb
> create mode 100644 server/views/firewalls/index.html.haml
> create mode 100644 server/views/firewalls/index.xml.haml
> create mode 100644 server/views/firewalls/new.html.haml
> create mode 100644 server/views/firewalls/new_rule.html.haml
> create mode 100644 server/views/firewalls/show.html.haml
> create mode 100644 server/views/firewalls/show.xml.haml
> 
> diff --git a/server/Gemfile b/server/Gemfile
> index 3ab3d5c..ee5f805 100644
> --- a/server/Gemfile
> +++ b/server/Gemfile
> @@ -15,7 +15,8 @@ group :azure do
> end
> 
> group :ec2 do
> -  gem 'aws'
> +  gem 'aws', ">= 2.5.4"
> +
> end
> 
> group :gogrid do
> diff --git a/server/deltacloud.rb b/server/deltacloud.rb
> index 7caf34f..5628e31 100644
> --- a/server/deltacloud.rb
> +++ b/server/deltacloud.rb
> @@ -36,6 +36,8 @@ require 'deltacloud/models/storage_volume'
> require 'deltacloud/models/bucket'
> require 'deltacloud/models/blob'
> require 'deltacloud/models/load_balancer'
> +require 'deltacloud/models/firewall'
> +require 'deltacloud/models/firewall_rule'
> 
> require 'deltacloud/validation'
> require 'deltacloud/helpers'
> diff --git a/server/lib/deltacloud/base_driver/base_driver.rb b/server/lib/deltacloud/base_driver/base_driver.rb
> index 2c780d9..7f8bd65 100644
> --- a/server/lib/deltacloud/base_driver/base_driver.rb
> +++ b/server/lib/deltacloud/base_driver/base_driver.rb
> @@ -149,7 +149,12 @@ module Deltacloud
>     # def keys(credentials, opts)
>     # def create_key(credentials, opts)
>     # def destroy_key(credentials, opts)
> -
> +    #
> +    # def firewalls(credentials, opts)
> +    # def create_firewall(credentials, opts)
> +    # def delete_firewall(credentials, opts)
> +    # def create_firewall_rule(credentials, opts)
> +    # def delete_firewall_rule(credentials, opts)

I don't think this change is necessary.


>     def realm(credentials, opts)
>       realms = realms(credentials, opts).first if has_capability?(:realms)
>     end
> @@ -183,8 +188,12 @@ module Deltacloud
>       keys(credentials, opts).first if has_capability?(:keys)
>     end
> 
> +    def firewall(credentials, opts={})
> +      firewalls(credentials, opts).first if has_capability?(:firewalls)
> +    end
> +
>     MEMBER_SHOW_METHODS =
> -      [ :realm, :image, :instance, :storage_volume, :bucket, :blob, :key ]
> +      [ :realm, :image, :instance, :storage_volume, :bucket, :blob, :key, :firewall ]
> 
>     def has_capability?(capability)
>       if MEMBER_SHOW_METHODS.include?(capability.to_sym)
> @@ -194,7 +203,6 @@ module Deltacloud
>       end
>     end
> 
> -
>     def filter_on(collection, attribute, opts)
>       return collection if opts.nil?
>       return collection if opts[attribute].nil?
> diff --git a/server/lib/deltacloud/base_driver/features.rb b/server/lib/deltacloud/base_driver/features.rb
> index b5bc3ee..4a2f1f8 100644
> --- a/server/lib/deltacloud/base_driver/features.rb
> +++ b/server/lib/deltacloud/base_driver/features.rb
> @@ -187,11 +187,11 @@ module Deltacloud
>       end
>     end
> 
> -    declare_feature :instances, :security_group do
> -      description "Put instance in one or more security groups on launch"
> +    declare_feature :instances, :firewall do
> +      description "Put instance in one or more firewalls (security groups) on launch"
>       operation :create do
> -        param :security_group, :array, :optional, [],
> -        "Array of security group names"
> +        param :firewalls, :array, :optional, nil, "Array of firewall ID strings"
> +        "Array of firewall (security group) id"
>       end
>     end
> 
> diff --git a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
> index 4edd989..fbf39f1 100644
> --- a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
> +++ b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
> @@ -33,12 +33,13 @@ module Deltacloud
>       class EC2Driver < Deltacloud::BaseDriver
> 
>         def supported_collections
> -          DEFAULT_COLLECTIONS + [ :keys, :buckets, :load_balancers, :addresses ]
> +
> +          DEFAULT_COLLECTIONS + [ :keys, :buckets, :load_balancers, :addresses, :firewalls ]
>         end
> 
>         feature :instances, :user_data
>         feature :instances, :authentication_key
> -        feature :instances, :security_group
> +        feature :instances, :firewall
>         feature :instances, :instance_count
>         feature :images, :owner_id
>         feature :buckets, :bucket_location
> @@ -201,7 +202,7 @@ module Deltacloud
>           instance_options.merge!(:key_name => opts[:keyname]) if opts[:keyname]
>           instance_options.merge!(:availability_zone => opts[:realm_id]) if opts[:realm_id]
>           instance_options.merge!(:instance_type => opts[:hwp_id]) if opts[:hwp_id] && opts[:hwp_id].length > 0
> -          instance_options.merge!(:group_ids => opts[:security_group]) if opts[:security_group]
> +          instance_options.merge!(:group_ids => opts[:firewalls]) if opts[:firewalls]
>           instance_options.merge!(
>             :min_count => opts[:instance_count],
>             :max_count => opts[:instance_count]
> @@ -571,6 +572,73 @@ module Deltacloud
>           end
>         end
> 
> +#--
> +#FIREWALLS - ec2 security groups
> +#--
> +      def firewalls(credentials, opts={})
> +          ec2 = new_client(credentials)
> +          the_firewalls = []
> +          groups = []
> +          safely do
> +            if opts[:id]
> +              groups = ec2.describe_security_groups([opts[:id]])
> +            else
> +              groups = ec2.describe_security_groups()
> +            end
> +          end
> +          groups.each do |security_group|
> +            the_firewalls << convert_security_group(security_group)
> +          end
> +          the_firewalls
> +      end

Here you're using 4 spaces for indentation.

> +
> +#--
> +#Create firewall
> +#--
> +        def create_firewall(credentials, opts={})
> +          ec2 = new_client(credentials)

Here you're using 2 spaces. I would prefer to use just 2 spaces ;-)

> +          safely do
> +            ec2.create_security_group(opts["name"], opts["description"])
> +          end
> +          Firewall.new( { :id=>opts["name"], :name=>opts["name"],
> +                          :description => opts["description"], :owner_id => "", :rules => [] } )
> +        end
> +
> +#--
> +#Delete firewall
> +#--
> +        def delete_firewall(credentials, opts={})
> +          ec2 = new_client(credentials)
> +          safely do
> +            ec2.delete_security_group(opts["id"])
> +          end
> +        end
> +#--
> +#Create firewall rule
> +#--
> +        def create_firewall_rule(credentials, opts={})
> +          ec2 = new_client(credentials)
> +          groups = []
> +          opts['groups'].each do |k,v|
> +            groups << {"group_name" => k, "owner" =>v}
> +          end
> +          safely do
> +            ec2.manage_security_group_ingress(opts['id'], opts['from_port'], opts['to_port'], opts['protocol'],
> +              "authorize", opts['addresses'], groups)
> +          end
> +        end
> +#--
> +#Delete firewall rule
> +#--
> +        def delete_firewall_rule(credentials, opts={})
> +          ec2 = new_client(credentials)
> +          firewall = opts[:id]
> +          protocol, from_port, to_port, addresses, groups = firewall_rule_params(opts[:rule_id])
> +          safely do
> +            ec2.manage_security_group_ingress(firewall, from_port, to_port, protocol, "revoke", addresses, groups)
> +          end
> +        end
> +
>         def valid_credentials?(credentials)
>           retval = true
>           begin
> @@ -764,6 +832,81 @@ module Deltacloud
>           balancer
>         end
> 
> +        #generate uid from firewall rule parameters (amazon doesn't do this for us
> +        def firewall_rule_id(user_id, protocol, from_port, to_port, sources)
> +          sources_string = ""
> +          sources.each do |source|
> +            sources_string<<"@"
> +            source.each_pair do |key,value|
> +              sources_string<< "#{value},"
> +            end
> +            sources_string.chomp!(",")
> +          end
> +         #sources_string is @group,297467797945,test@address,ipv4,10.1.1.1,24 etc
> +         id_string = "#{user_id}~#{protocol}~#{from_port}~#{to_port}~#{sources_string}"
> +        end
> +
> +        #extract params from uid
> +        def firewall_rule_params(id)
> +          #user_id~protocol~from_port~to_port~sources_string
> +          params = id.split("~")
> +          protocol = params[1]
> +          from_port = params[2]
> +          to_port = params[3]
> +          sources = params[4].split("@")
> +          sources.shift #first match is ""
> +          addresses = []
> +          groups = []
> +          #@group,297467797945,test@address,ipv4,10.1.1.1,24@address,ipv4,192.168.1.1,24
> +          sources.each do |source|
> +            current = source.split(",")
> +            type = current[0]
> +            case type
> +              when 'group'
> +                #group,297467797945,test
> +                owner = current[1]
> +                name = current[2]
> +                groups << {'group_name' => name, 'owner' => owner}
> +              when 'address'
> +                #address,ipv4,10.1.1.1,24
> +                address = current[2]
> +                address<<"/#{current[3]}"
> +                addresses << address
> +            end
> +          end
> +          return protocol, from_port, to_port, addresses, groups
> +        end
> +
> +        #Convert ec2 security group to server/lib/deltacloud/models/firewall
> +        def convert_security_group(security_group)
> +          rules = []
> +          security_group[:aws_perms].each do |perm|
> +            sources = []
> +            perm[:groups].each do |group|
> +              sources << {:type => "group", :name => group[:group_name], :owner => group[:owner]}
> +            end
> +            perm[:ip_ranges].each do |ip|
> +              sources << {:type => "address", :family=>"ipv4",
> +                          :address=>ip[:cidr_ip].split("/").first,
> +                          :prefix=>ip[:cidr_ip].split("/").last}
> +            end
> +            rule_id = firewall_rule_id(security_group[:aws_owner], perm[:protocol],
> +                                       perm[:from_port] , perm[:to_port], sources)
> +            rules << FirewallRule.new({:id => rule_id,
> +                                        :allow_protocol => perm[:protocol],
> +                                        :port_from => perm[:from_port],
> +                                        :port_to => perm[:to_port],
> +                                        :direction => 'ingress',
> +                                        :sources => sources})
> +          end
> +          Firewall.new(  {  :id => security_group[:aws_group_name],
> +                            :name => security_group[:aws_group_name],
> +                            :description => security_group[:aws_description],
> +                            :owner_id => security_group[:aws_owner],
> +                            :rules => rules
> +                      }  )
> +        end
> +
>         def convert_state(ec2_state)
>           case ec2_state
>             when "terminated"
> diff --git a/server/lib/deltacloud/models/bucket.rb b/server/lib/deltacloud/models/bucket.rb
> index e6b2b34..bef36ac 100644
> --- a/server/lib/deltacloud/models/bucket.rb
> +++ b/server/lib/deltacloud/models/bucket.rb
> @@ -24,7 +24,7 @@ class Bucket < BaseModel
> 
>   def to_hash
>     h = self.to_hash_original
> -    h[:blob_list] = self.blob_list.collect { |blob| { :id => blob, 
> +    h[:blob_list] = self.blob_list.collect { |blob| { :id => blob,
>       :href => "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/buckets/#{self.id}/#{blob.id}"}}
>     return h
>   end
> diff --git a/server/lib/deltacloud/models/firewall.rb b/server/lib/deltacloud/models/firewall.rb
> new file mode 100644
> index 0000000..dc0ae3d
> --- /dev/null
> +++ b/server/lib/deltacloud/models/firewall.rb
> @@ -0,0 +1,22 @@
> +#
> +# 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.
> +
> +class Firewall < BaseModel
> +  attr_accessor :name
> +  attr_accessor :description
> +  attr_accessor :owner_id
> +  attr_accessor :rules
> +end
> \ No newline at end of file
> diff --git a/server/lib/deltacloud/models/firewall_rule.rb b/server/lib/deltacloud/models/firewall_rule.rb
> new file mode 100644
> index 0000000..3959eb6
> --- /dev/null
> +++ b/server/lib/deltacloud/models/firewall_rule.rb
> @@ -0,0 +1,23 @@
> +#
> +# 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.
> +
> +class FirewallRule < BaseModel
> +  attr_accessor :allow_protocol # tcp/udp/icmp
> +  attr_accessor :port_from
> +  attr_accessor :port_to
> +  attr_accessor :sources
> +  attr_accessor :direction #ingress egress
> +end
> diff --git a/server/public/javascripts/application.js b/server/public/javascripts/application.js
> index 95c9bc2..703879c 100644
> --- a/server/public/javascripts/application.js
> +++ b/server/public/javascripts/application.js
> @@ -19,10 +19,10 @@ $(document).ready(function() {
> 
> function more_fields()
> {
> -	//increment the hidden input that captures how many meta_data are passed
> -	var meta_params = document.getElementsByName('meta_params')
> +  //increment the hidden input that captures how many meta_data are passed
> +  var meta_params = document.getElementsByName('meta_params')
>     current_number_params = eval(meta_params[0].value)+1
> -	meta_params[0].value = current_number_params
> +  meta_params[0].value = current_number_params
>     var new_meta = document.getElementById('metadata_holder').cloneNode(true);
>     new_meta.id = 'metadata_holder' + current_number_params;
>     new_meta.style.display = 'block';
> @@ -39,15 +39,50 @@ function more_fields()
> function less_fields()
> {
>     var meta_params = document.getElementsByName('meta_params')
> -	current_val = eval(meta_params[0].value)
> -	if (current_val == 0)
> -	{
> -		return;
> -	}
> -	else
> -	{
> -		var theDiv = document.getElementById('metadata_holder'+current_val)
> -		theDiv.parentNode.removeChild(theDiv)
> -		meta_params[0].value = eval(current_val)-1
> -	}
> +  current_val = eval(meta_params[0].value)
> +  if (current_val == 0)
> +  {
> +    return;
> +  }
> +  else
> +  {
> +    var theDiv = document.getElementById('metadata_holder'+current_val)
> +    theDiv.parentNode.removeChild(theDiv)
> +    meta_params[0].value = eval(current_val)-1
> +  }
> +}
> +
> +var addresses = 0;
> +var groups = 0;
> +function make_fields(type)
> +{
> +  form = document.getElementById("new_rule_form")
> +  button = document.getElementById("submit_button")
> +  if(type == "address")
> +  {
> +    name = "ip_address" + eval(++addresses)
> +    create_rule_source_field(name, "Address " + eval(addresses) + " [use CIDR notation 0.0.0.0/0]", form, button)
> +  }
> +  else if(type == "group")
> +  {
> +    name = "group" + eval(++groups)
> +    create_rule_source_field(name, "Name of group " + eval(groups), form, button)
> +    name = "group" + eval(groups) + "owner"
> +    create_rule_source_field(name, "Group " + eval(groups) + " owner (required)", form, button)
> +  }
> +}
> +
> +function create_rule_source_field(name, label, form, button)
> +{
> +    element = document.createElement("INPUT")
> +    element.type = "input"
> +    element.size = 35
> +    element.name = name
> +    text = document.createTextNode(label)
> +    form.insertBefore(element, button)
> +    form.insertBefore(text, element)
> +    form.insertBefore(document.createElement('BR'), element)
> +    form.insertBefore(document.createElement('BR'), button)
> +    form.insertBefore(document.createElement('BR'), button)
> +    form.insertBefore(document.createElement('BR'), button)
> }
> diff --git a/server/server.rb b/server/server.rb
> index ba87c67..ae23263 100644
> --- a/server/server.rb
> +++ b/server/server.rb
> @@ -948,3 +948,115 @@ collection :addresses do
>   end
> 
> end
> +
> +#html for creating a new firewall
> +get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/firewalls/new" do
> +  respond_to do |format|
> +    format.html { haml :"firewalls/new" }
> +  end
> +end
> +
> +#html for creating a new firewall rule
> +get "#{Sinatra::UrlForHelper::DEFAULT_URI_PREFIX}/firewalls/:firewall/new_rule" do
> +  @firewall_name = params[:firewall]
> +  respond_to do |format|
> +    format.html {haml :"firewalls/new_rule" }
> +  end
> +end
> +
> +#FIREWALLS
> +collection :firewalls do
> +  description "Allow user to define firewall rules for an instance (ec2 security groups) eg expose ssh access [port 22, tcp]."
> +  operation :index do
> +    description 'List all firewalls'
> +    with_capability :firewalls
> +    control { filter_all(:firewalls) }
> +  end
> +
> +  operation :show do
> +    description 'Show details for a specific firewall - list all rules'
> +    with_capability :firewall
> +    param :id,            :string,    :required
> +    control { show(:firewall) }
> +  end
> +
> +  operation :create do
> +    description 'Create a new firewall'
> +    with_capability :create_firewall
> +    param :name,          :string,    :required
> +    param :description,   :string,    :required
> +    control do
> +      @firewall = driver.create_firewall(credentials, params )
> +      respond_to do |format|
> +        format.xml do
> +          response.status = 201  # Created
> +          haml :"firewalls/show"
> +        end
> +        format.html {haml :"firewalls/show"}
> +        format.json {convert_to_json(:firewall, @firewall)}
> +      end
> +    end
> +  end
> +
> +  operation :destroy do
> +    description 'Delete a specified firewall - error if firewall has rules'
> +    with_capability :delete_firewall
> +    param :id,            :string,    :required
> +    control do
> +      driver.delete_firewall(credentials, params)
> +      respond_to do |format|
> +        format.xml { 204 }
> +        format.json {  204 }
> +        format.html {  redirect(firewalls_url) }
> +      end
> +    end
> +  end
> +
> +#create a new firewall rule - POST /api/firewalls/:firewall/rules
> +  operation :rules, :method => :post, :member => true do
> +    description 'Create a new firewall rule for the specified firewall'
> +    param :firewall,  :required, :string, [],  "Name of firewall in which to apply this rule"
> +    param :protocol,  :required, :string, ['tcp','udp','icmp'], "Transport layer protocol for the rule"
> +    param :from_port, :required, :string, [], "Start of port range for the rule"
> +    param :to_port,   :required, :string, [], "End of port range for the rule"
> +    with_capability :create_firewall_rule
> +    control do
> +      #source IPs from params
> +      addresses =  params.inject([]){|result,current| result << current.last unless current.grep(/^ip[-_]address/i).empty?; result}
> +      #source groups from params
> +      groups = {}
> +      max_groups  = params.select{|k,v| k=~/^group/}.size/2
> +      for i in (1..max_groups) do
> +        groups.merge!({params["group#{i}"]=>params["group#{i}owner"]})
> +      end
> +      params.merge!( {'addresses' => addresses} ) ; params.merge!( {'groups' => groups} )
> +      driver.create_firewall_rule(credentials, params)
> +      @firewall = driver.firewall(credentials, {:id => params[:firewall]})
> +      respond_to do |format|
> +        format.html {haml :"firewalls/show"}
> +        format.xml do
> +          response.status = 201 #created
> +          haml :"firewall/show"
> +        end
> +        format.json {convert_to_json(:firewall, @firewall)}
> +      end
> +    end
> +  end
> +
> +#delete a firewall rule DELETE /api/firewalls/:firewall/rule - with param rule_id
> +  operation :rule, :method => :delete, :member => true do
> +    description 'Delete the specified firewall rule from the given firewall'
> +    param :firewall, :required, :string
> +    param :rule_id,  :required, :string
> +    with_capability :delete_firewall_rule
> +    control do
> +      driver.delete_firewall_rule(credentials, params)
> +      respond_to do |format|
> +        format.html {redirect firewall_url(params[:id])}
> +        format.xml {204}
> +        format.json {204}
> +      end
> +    end
> +  end
> +
> +end #firewalls
> diff --git a/server/views/firewalls/index.html.haml b/server/views/firewalls/index.html.haml
> new file mode 100644
> index 0000000..3312a32
> --- /dev/null
> +++ b/server/views/firewalls/index.html.haml
> @@ -0,0 +1,25 @@
> +%h1 Firewalls
> +%br
> +%p
> +  =link_to 'Create new firewall', "/api/firewalls/new"
> +%table.display
> +  %thead
> +    %tr
> +      %th Id
> +      %th Name
> +      %th Description
> +      %th Owner ID
> +      %th Rules
> +  %tbody
> +    - @firewalls.each do |firewall|
> +      %tr
> +        %td
> +          = link_to firewall.id, firewall_url(firewall.id)
> +        %td
> +          = firewall.name
> +        %td
> +          = firewall.description
> +        %td
> +          = firewall.owner_id
> +        %td
> +          = link_to 'view rules', firewall_url(firewall.id)
> diff --git a/server/views/firewalls/index.xml.haml b/server/views/firewalls/index.xml.haml
> new file mode 100644
> index 0000000..f027785
> --- /dev/null
> +++ b/server/views/firewalls/index.xml.haml
> @@ -0,0 +1,23 @@
> +!!! XML
> +%firewalls
> +  - @firewalls.each do |firewall|
> +    %firewall{:href => firewall_url(firewall.id), :id => firewall.id}
> +      - firewall.attributes.select{ |attr| attr != :id && attr!= :rules}.each do |attribute|
> +        - haml_tag("#{attribute}".tr('-', '_'), :<) do
> +          - if [:name, :description].include?(attribute)
> +            =cdata do
> +              - haml_concat firewall.send(attribute)
> +          - else
> +            - haml_concat firewall.send(attribute)
> +      %rules
> +        - firewall.rules.each do |rule|
> +          %rule{:id => rule.id}
> +            - rule.attributes.select{|attr| attr != :sources && attr != :id}.each do |rule_attrib|
> +              - haml_tag("#{rule_attrib}".tr('-', '_'), :<) do
> +                - haml_concat rule.send(rule_attrib)
> +            %sources
> +              - rule.sources.each do |source|
> +                - if source[:type] == "group"
> +                  %source{:name => source[:name], :type=> source[:type], :owner=> source[:owner]}
> +                - else
> +                  %source{:prefix => source[:prefix], :address=> source[:address], :family=>source[:family], :type => source[:type]}
> \ No newline at end of file
> diff --git a/server/views/firewalls/new.html.haml b/server/views/firewalls/new.html.haml
> new file mode 100644
> index 0000000..4a230a6
> --- /dev/null
> +++ b/server/views/firewalls/new.html.haml
> @@ -0,0 +1,11 @@
> +%h1 New Firewall
> +
> +%form{:action => firewalls_url, :method => :post}
> +  %label
> +    Firewall Name
> +    %input{:name => 'name', :size => 25}/
> +    %br
> +  %label
> +    Firewall Description
> +    %input{:name => 'description', :size => 100}/
> +  %input{:type => :submit, :name => "commit", :value=>"create"}
> \ No newline at end of file
> diff --git a/server/views/firewalls/new_rule.html.haml b/server/views/firewalls/new_rule.html.haml
> new file mode 100644
> index 0000000..b25206a
> --- /dev/null
> +++ b/server/views/firewalls/new_rule.html.haml
> @@ -0,0 +1,26 @@
> +%h1 New Firewall Rule
> +
> +%form{ :action => "#{firewall_url(@firewall_name)}/rules", :id => "new_rule_form", :method => :post, :enctype => 'multipart/form-data'}
> +  %label
> +    Protocol:
> +    %br
> +    %input{ :name => 'protocol', :size => 10}/
> +    %br
> +    %br
> +  %label
> +    From port:
> +    %br
> +    %input{ :name => 'from_port', :size => 10}/
> +    %br
> +    %br
> +    To port:
> +    %br
> +    %input{ :name => 'to_port', :size => 10}/
> +    %br
> +    %br
> +  %a{ :href => "javascript:;", :onclick => "make_fields('address');"} Add source IP address
> +  %br
> +  %a{ :href => "javascript:;", :onclick => "make_fields('group');"} Add source group
> +  %br
> +  %br
> +  %input{ :type => :submit, :id => "submit_button", :name => "commit", :value => "create"}/
> diff --git a/server/views/firewalls/show.html.haml b/server/views/firewalls/show.html.haml
> new file mode 100644
> index 0000000..b77aaa4
> --- /dev/null
> +++ b/server/views/firewalls/show.html.haml
> @@ -0,0 +1,44 @@
> +%h1 Firewall
> +%h2
> +  = @firewall.id
> +%dl
> +  %di
> +    %dt Name
> +    %dd
> +      = @firewall.name
> +    %dt Owner
> +    %dd
> +      = @firewall.owner_id
> +    %dt Description
> +    %dd
> +      = @firewall.description
> +
> +%h2
> +  Rules
> +  %br
> +  %p
> +    =link_to 'Create a new rule', "/api/firewalls/#{@firewall.name}/new_rule"
> +%dl
> +  - @firewall.rules.each do |rule|
> +    %di
> +      Rule
> +      - rule.attributes.select{|attr| attr != :sources}.each do |attrib|
> +        %dt #{attrib}
> +        %dd
> +          = rule.send(attrib)
> +      %dt sources
> +      %dd
> +        - rule.sources.each do |source|
> +          - if source[:type] == "group"
> +            type: #{source[:type]}, name: #{source[:name]}, owner: #{source[:owner]}
> +            %br
> +          - else
> +            type: #{source[:type]}, family: #{source[:family]}, address: #{source[:address]}, prefix: #{source[:prefix]}
> +            %br
> +      %dd
> +        %form{ :action => "#{firewall_url(@firewall.name)}/rule", :method => :post}
> +          %input{:type => "hidden", :name => "_method", :value => "delete"}
> +          %input{:type => "hidden", :name => "rule_id", :value => rule.id}
> +          %input{:type => :submit, :value => "Delete Rule"}
> +  %dd
> +    = link_to_action 'Delete Firewall', destroy_firewall_url(@firewall.name), :delete
> diff --git a/server/views/firewalls/show.xml.haml b/server/views/firewalls/show.xml.haml
> new file mode 100644
> index 0000000..9d1fc48
> --- /dev/null
> +++ b/server/views/firewalls/show.xml.haml
> @@ -0,0 +1,21 @@
> +!!! XML
> +%firewall{:href => firewall_url(@firewall.id), :id => @firewall.id}
> +  - @firewall.attributes.select{ |attr| attr != :id && attr!= :rules}.each do |attribute|
> +    - haml_tag("#{attribute}".tr('-', '_'), :<) do
> +      - if [:name, :description].include?(attribute)
> +        =cdata do
> +          - haml_concat @firewall.send(attribute)
> +      - else
> +        - haml_concat @firewall.send(attribute)
> +  %rules
> +    - @firewall.rules.each do |rule|
> +      %rule{:id => rule.id}
> +        - rule.attributes.select{|attr| attr != :sources && attr != :id}.each do |rule_attrib|
> +          - haml_tag("#{rule_attrib}".tr('-', '_'), :<) do
> +            - haml_concat rule.send(rule_attrib)
> +        %sources
> +          - rule.sources.each do |source|
> +            - if source[:type] == "group"
> +              %source{:name => source[:name], :type=> source[:type], :owner=>source[:owner]}
> +            - else
> +              %source{:prefix => source[:prefix], :address=> source[:address], :family=>source[:family], :type => source[:type]}
> \ No newline at end of file
> -- 
> 1.7.3.4
> 

------------------------------------------------------
Michal Fojtik, mfojtik@redhat.com
Deltacloud API: http://deltacloud.org