Class: Ironfan::Provider::OpenStack::SecurityGroup

Inherits:
Resource show all
Defined in:
lib/ironfan/headers.rb,
lib/ironfan/provider/openstack/security_group.rb

Constant Summary collapse

WIDE_OPEN =
Range.new(1,65535)

Instance Attribute Summary

Attributes inherited from Resource

#owner, #users

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Resource

aggregate!, #bogus?, create!, destroy!, forget, forget!, handle, known, #on_correlate, patiently, recall, recall?, receive, register, remember, save!, validate_computer!, validate_resources!

Methods inherited from Builder

ui, #ui

Class Method Details

.ensure_groups(computer) ⇒ Object

Utility


184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/ironfan/provider/openstack/security_group.rb', line 184

def self.ensure_groups computer
  return unless OpenStack.applicable computer  # Ensure the security_groups include those for cluster & facet
  # FIXME: This violates the DSL's immutability; it should be
  #   something calculated from within the DSL construction

  Ironfan.todo("CODE SMELL: violation of DSL immutability: #{caller}")
  server = computer.server
  cluster_name = "#{computer.server.realm_name}-#{computer.server.cluster_name}"
  server.security_group computer.server.realm_name
  realm_group = server.security_group cluster_name
  realm_group.authorized_by_group realm_group.name
  facet_name = "#{computer.server.realm_name}-#{computer.server.cluster_name}-#{computer.server.facet_name}"
  server.security_group facet_name
end

.expected_ids(computer) ⇒ Object


24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/ironfan/provider/openstack/security_group.rb', line 24

def self.expected_ids(computer)
  return unless computer.server
  openstack = computer.server.cloud(:openstack)
  server_groups = computer.server.security_groups
  cloud_groups = openstack.security_groups

  result = []
  [server_groups, cloud_groups].each do |container|
    container.each { |g| result.push(g.name) }
  end
  return result.uniq
end

.load!(cluster = nil) ⇒ Object

Discovery


47
48
49
50
51
# File 'lib/ironfan/provider/openstack/security_group.rb', line 47

def self.load!(cluster=nil)
  OpenStack.connection.security_groups.reject { |raw| raw.blank? }.each do |raw|
    remember SecurityGroup.new(:adaptee => raw)
  end
end

.multiple?Boolean

Returns:

  • (Boolean)

21
# File 'lib/ironfan/provider/openstack/security_group.rb', line 21

def self.multiple?()    true;   end

.prepare!(computers) ⇒ Object

Manipulation


76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/ironfan/provider/openstack/security_group.rb', line 76

def self.prepare!(computers)
  # Create any groups that don't yet exist, and ensure any authorizations
  # that are required for those groups
  cluster_name             = nil
  groups_to_create         = [ ]
  authorizations_to_ensure = [ ]

  computers.each{|comp| ensure_groups(comp) if OpenStack.applicable(comp) } # Add facet and cluster security groups for the computer

  # First, deduce the list of all groups to which at least one instance belongs
  # We'll use this later to decide whether to create groups, or authorize access,
  # using a VPC security group or an Openstack security group.
  groups_that_should_exist = [] #computers.map{|comp| expected_ids(comp) }.flatten.compact.sort.uniq
  groups_to_create << groups_that_should_exist

  computers.select { |computer| OpenStack.applicable computer }.each do |computer|
    cloud           = computer.server.cloud(:openstack)
    cluster_name    = computer.server.cluster_name

    # Iterate over all of the security group information, keeping track of
    # any groups that must exist and any authorizations that must be ensured
    [computer.server.security_groups, cloud.security_groups].each do |container|
      
      container.values.each do |dsl_group|

        groups_to_create << dsl_group.name
        
        groups_to_create << dsl_group.group_authorized

        groups_to_create << dsl_group.group_authorized_by

        authorizations_to_ensure << dsl_group.group_authorized.map do |other_group|
          {
            :grantor      => dsl_group.name,
            :grantee      => other_group,
            :grantee_type => :group,
            :range        => WIDE_OPEN,
          }
        end

        authorizations_to_ensure << dsl_group.group_authorized_by.map do |other_group|
          {
            :grantor      => other_group,
            :grantee      => dsl_group.name,
            :grantee_type => :group,
            :range        => WIDE_OPEN,
          }
        end

        authorizations_to_ensure << dsl_group.range_authorizations.map do |range_auth|
          range, cidr, protocol = range_auth
          {
            :grantor      => dsl_group.name,
            :grantee      => { :cidr_ip => cidr, :ip_protocol => protocol },
            :grantee_type => :cidr,
            :range        => range,
          }
        end
      end
    end
  end
  groups_to_create         = groups_to_create.flatten.uniq.reject { |group| recall? group.to_s }.sort
  authorizations_to_ensure = authorizations_to_ensure.flatten.uniq.sort { |a,b| a[:grantor] <=> b[:grantor] }

  Ironfan.step(cluster_name, "creating security groups", :blue) unless groups_to_create.empty?
  groups_to_create.each do |group|
    if group =~ /\//
      Ironfan.step(group, "  assuming that owner/group pair #{group} already exists", :blue)
    else
      Ironfan.step(group, "  creating #{group} security group", :blue)
      begin
        tokens    = group.to_s.split(':')
        group_id  = tokens.pop
        OpenStack.connection.create_security_group(group_id,"Ironfan created group #{group_id}")
      rescue Fog::Compute::OpenStack::Error => e # InvalidPermission.Duplicate
        Chef::Log.info("ignoring security group error: #{e}")
      end
    end
  end

  # Re-load everything so that we have a @@known list of security groups to manipulate
  load! unless groups_to_create.empty?

  # Now make sure that all required authorizations are present
  Ironfan.step(cluster_name, "ensuring security group permissions", :blue) unless authorizations_to_ensure.empty?
  authorizations_to_ensure.each do |auth|
    grantor_fog = recall(auth[:grantor])
    if :group == auth[:grantee_type]
      if fog_grantee = recall(auth[:grantee])
        options = { :group => fog_grantee.group_id }
      elsif auth[:grantee] =~ /\//
        options = { :group_alias => auth[:grantee] }
      else
        raise "Don't know what to do with authorization grantee #{auth[:grantee]}"
      end
      message = "  ensuring access from #{auth[:grantee]} to #{auth[:grantor]}"
    else
      options = auth[:grantee]
      message = "  ensuring #{auth[:grantee][:ip_protocol]} access from #{auth[:grantee][:cidr_ip]} to #{auth[:range]}"
    end
    Ironfan.step(auth[:grantor], message, :blue)
    safely_authorize(grantor_fog, auth[:range], options)
  end
end

.resource_typeObject


22
# File 'lib/ironfan/provider/openstack/security_group.rb', line 22

def self.resource_type()        :security_group;   end

.safely_authorize(fog_group, range, options) ⇒ Object

Try an authorization, ignoring duplicates (this is easier than correlating). Do so for both TCP and UDP, unless only one is specified


201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/ironfan/provider/openstack/security_group.rb', line 201

def self.safely_authorize(fog_group,range,options)
  if options[:group_alias]
    owner, group = options[:group_alias].split(/\//)
    self.patiently(fog_group.name, Fog::Compute::OpenStack::Error, :ignore => Proc.new { |e| e.message =~ /This rule already exists in group/ }) do
      OpenStack.connection.authorize_security_group_ingress(
        'GroupName'                   => fog_group.name,
        'SourceSecurityGroupName'     => group,
        'SourceSecurityGroupOwnerId'  => owner
      )
    end
  elsif options[:ip_protocol]
    self.patiently(fog_group.name, Excon::Errors::HTTPStatusError, :ignore => Proc.new { |e| e.message =~ /This rule already exists in group/ }) do
      fog_group.create_security_group_rule(range.min, range.max, options[:ip_protocol], options[:cidr_ip], options[:group])
    end 
  else
    safely_authorize(fog_group,range,options.merge(:ip_protocol => 'tcp'))
    safely_authorize(fog_group,range,options.merge(:ip_protocol => 'udp'))
    safely_authorize(fog_group,Range.new(-1,-1),options.merge(:ip_protocol => 'icmp')) if(range == WIDE_OPEN)
    return
  end
end

.shared?Boolean

Returns:

  • (Boolean)

20
# File 'lib/ironfan/provider/openstack/security_group.rb', line 20

def self.shared?()      true;   end

Instance Method Details

#group_idObject


40
41
42
# File 'lib/ironfan/provider/openstack/security_group.rb', line 40

def group_id
  @adaptee.id
end

#ip_permissionsObject


37
38
# File 'lib/ironfan/provider/openstack/security_group.rb', line 37

def ip_permissions
end

#receive_adaptee(obj) ⇒ Object


53
54
55
56
# File 'lib/ironfan/provider/openstack/security_group.rb', line 53

def receive_adaptee(obj)
  obj = OpenStack.connection.security_groups.new(obj) if obj.is_a?(Hash)
  super
end

#to_sObject


58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/ironfan/provider/openstack/security_group.rb', line 58

def to_s
  if ip_permissions.present?
    perm_str = ip_permissions.map{|perm|
      "%s:%s-%s (%s | %s)" % [
        perm['ipProtocol'], perm['fromPort'], perm['toPort'],
        perm['groups'  ].map{|el| el['groupName'] }.join(','),
        perm['ipRanges'].map{|el| el['cidrIp']    }.join(','),
      ]
    }
    return "<%-15s %-12s %-25s %s>" % [ self.class.handle, group_id, name, perm_str]
  else
    return "<%-15s %-12s %s>" % [ self.class.handle, group_id, name ]
  end
end