module ClusterChef class Facet < ClusterChef::ComputeBuilder attr_reader :cluster has_keys :instances def initialize cluster, facet_name, attrs={} super(facet_name.to_sym, attrs) @cluster = cluster @servers = Mash.new @chef_roles = [] @settings[:instances] ||= 1 create_facet_role create_facet_security_group unless attrs[:no_security_group] end def cluster_name cluster.name end def facet_name name end # The auto-generated role for this facet. # Instance-evals the given block in the context of that role, # # @example # facet_role do # override_attributes({ # :time_machine => { :transition_speed => 88 }, # }) # end # # @return [Chef::Role] The auto-generated role for this facet. def facet_role(&block) @facet_role.instance_eval( &block ) if block_given? @facet_role end def assign_volume_ids(volume_name, *volume_ids) volume_ids.flatten.zip(servers).each do |volume_id, server| server.volume(volume_name){ volume_id(volume_id) } if server end end # # Retrieve or define the given server # # @param [Integer] idx -- the index of the desired server # @param [Hash] attrs -- attributes to configure on the object # @yield a block to execute in the context of the object # # @return [ClusterChef::Facet] # def server(idx, attrs={}, &block) idx = idx.to_i @servers[idx] ||= ClusterChef::Server.new(self, idx) @servers[idx].configure(attrs, &block) @servers[idx] end # if the server has been added to this facet or is in range def has_server? idx (idx.to_i < instances) || @servers.include?(idx.to_i) end # # Slicing # # All servers in this facet # # @return [ClusterChef::ServerSlice] slice containing all servers def servers slice(indexes) end # # A slice of servers from this facet, in index order # # If +slice_indexes+ is nil, returns all servers. # Otherwise, takes slice (given by +*args+) from the requested facet. # # @param [Array, String] slice_indexes -- servers in that facet (or nil for all in facet). # # @return [ClusterChef::ServerSlice] the requested slice def slice(slice_indexes=nil) slice_indexes = self.indexes if slice_indexes.blank? slice_indexes = indexes_from_intervals(slice_indexes) if slice_indexes.is_a?(String) svrs = Array(slice_indexes).map(&:to_i).sort!.select{|idx| has_server?(idx) }.map{|idx| server(idx) } ClusterChef::ServerSlice.new(self.cluster, svrs) end # all valid server indexes def valid_indexes (0 ... instances).to_a # note the '...' end # indexes in the 0...instances range plus bogus ones that showed up # (probably from chef or fog) def indexes [@servers.keys, valid_indexes].flatten.compact.uniq.sort end # # Resolve: # def resolve! servers.each(&:resolve!) end protected def create_facet_security_group cloud.security_group("#{cluster_name}-#{facet_name}") end # Creates a chef role named for the facet def create_facet_role @facet_role_name = "#{cluster_name}_#{facet_name}" @facet_role = new_chef_role(@facet_role_name, cluster, self) role(@facet_role_name, :last) end # # Given a string enumerating indexes to select returns a flat array of # indexes. The indexes will be unique but in an arbitrary order. # # @example # facet = ClusterChef::Facet.new('foo', 'bar') # facet.indexes_from_intervals('1,2-3,8-9,7') # [1, 2, 3, 8, 9, 7] # facet.indexes_from_intervals('1,3-5,4,7') # [1, 3, 4, 5, 7] # def indexes_from_intervals intervals intervals.split(",").map do |term| if term =~ /^(\d+)-(\d+)$/ then ($1.to_i .. $2.to_i).to_a elsif term =~ /^(\d+)$/ then $1.to_i else ui.warn("Bad interval: #{term}") ; nil end end.flatten.compact.uniq end end end