Class: MCollective::Util::Playbook::Nodes

Inherits:
Object
  • Object
show all
Defined in:
lib/mcollective/util/playbook/nodes.rb,
lib/mcollective/util/playbook/nodes/pql_nodes.rb,
lib/mcollective/util/playbook/nodes/yaml_nodes.rb,
lib/mcollective/util/playbook/nodes/shell_nodes.rb,
lib/mcollective/util/playbook/nodes/terraform_nodes.rb,
lib/mcollective/util/playbook/nodes/mcollective_nodes.rb

Defined Under Namespace

Classes: McollectiveNodes, PqlNodes, ShellNodes, TerraformNodes, YamlNodes

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(playbook) ⇒ Nodes

Returns a new instance of Nodes.



13
14
15
16
# File 'lib/mcollective/util/playbook/nodes.rb', line 13

def initialize(playbook)
  @playbook = playbook
  @nodes = {}
end

Instance Attribute Details

#nodesObject (readonly)

Returns the value of attribute nodes.



11
12
13
# File 'lib/mcollective/util/playbook/nodes.rb', line 11

def nodes
  @nodes
end

Instance Method Details

#[](nodeset) ⇒ Array<String>

Nodes belonging to a specific node set

Parameters:

  • nodeset (String)

    node set name

Returns:

Raises:

  • (StandardError)

    when node set is unknown



30
31
32
33
34
35
36
# File 'lib/mcollective/util/playbook/nodes.rb', line 30

def [](nodeset)
  if include?(nodeset)
    @nodes[nodeset][:discovered]
  else
    raise("Unknown node set %s" % nodeset)
  end
end

#check_empty(nodes) ⇒ Object

Handles an empty discovered list

Parameters:

  • nodes (String)

    node set name

Raises:

  • (StandardError)

    when empty



153
154
155
# File 'lib/mcollective/util/playbook/nodes.rb', line 153

def check_empty(nodes)
  raise(properties(nodes)["when_empty"] || "Did not discover any nodes for nodeset %s" % nodes) if self[nodes].empty? && !properties(nodes)["empty_ok"]
end

#check_usesObject

Checks if the agents on the nodes matches the desired versions

Raises:

  • (StandardError)

    on error



89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/mcollective/util/playbook/nodes.rb', line 89

def check_uses
  agent_nodes = {}

  @nodes.map do |_, dets|
    dets[:properties].fetch("uses", []).each do |agent|
      agent_nodes[agent] ||= []
      agent_nodes[agent].concat(dets[:discovered])
    end
  end

  @playbook.validate_agents(agent_nodes) unless agent_nodes.empty?
end

#from_hash(data) ⇒ Object



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/mcollective/util/playbook/nodes.rb', line 181

def from_hash(data)
  data.each do |nodes, props|
    resolver = resolver_for(props["type"])
    resolver.from_hash(props)
    resolver.validate_configuration!

    node_props = {
      "at_least" => 1,
      "empty_ok" => false,
      "when_empty" => "Did not discover any nodes for nodeset %s" % nodes
    }.merge(props)

    node_props["at_least"] = 0 if node_props["empty_ok"]

    @nodes[nodes] = {
      :resolver => resolver,
      :discovered => [],
      :properties => node_props
    }
  end

  self
end

#include?(nodes) ⇒ Boolean

Determines if a node set is known

Parameters:

  • nodes (String)

    node set name

Returns:

  • (Boolean)


55
56
57
# File 'lib/mcollective/util/playbook/nodes.rb', line 55

def include?(nodes)
  @nodes.include?(nodes)
end

#keysArray<String>

List of known node set names

Returns:



21
22
23
# File 'lib/mcollective/util/playbook/nodes.rb', line 21

def keys
  @nodes.keys
end

#limit_nodes(nodes) ⇒ Object

TODO:

more intelegent limiting with weighted randoms like mco rpc client

Limits the discovered list for a node set based on the playbook limits

Parameters:

  • nodes (String)

    node set name



161
162
163
164
165
166
167
168
# File 'lib/mcollective/util/playbook/nodes.rb', line 161

def limit_nodes(nodes)
  return if self[nodes].empty?

  if limit = properties(nodes)["limit"]
    Log.debug("Limiting node set %s to %d nodes from %d" % [nodes, limit, @nodes[nodes][:discovered].size])
    @nodes[nodes][:discovered] = @nodes[nodes][:discovered][0..(limit - 1)]
  end
end

#mcollective_taskObject



111
112
113
# File 'lib/mcollective/util/playbook/nodes.rb', line 111

def mcollective_task
  Tasks::McollectiveTask.new(@playbook)
end

#prepareObject



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/mcollective/util/playbook/nodes.rb', line 59

def prepare
  @nodes.each do |node_set, dets|
    @playbook.in_context(node_set) do
      Log.debug("Preparing nodeset %s" % node_set)

      resolve_nodes(node_set)
      check_empty(node_set)
      limit_nodes(node_set)
      validate_nodes(node_set)

      Log.info("Discovered %d node(s) in node set %s" % [dets[:discovered].size, node_set])
    end
  end

  @playbook.in_context("conn.test") { test_nodes }
  @playbook.in_context("ddl.test") { check_uses }
end

#properties(nodes) ⇒ Hash

Properties for a certain node set

Parameters:

  • nodes (String)

    node set name

Returns:

  • (Hash)

Raises:

  • (StandardError)

    when node set is unknown



43
44
45
46
47
48
49
# File 'lib/mcollective/util/playbook/nodes.rb', line 43

def properties(nodes)
  if include?(nodes)
    @nodes[nodes][:properties]
  else
    raise("Unknown node set %s" % nodes)
  end
end

#resolve_nodes(node_set) ⇒ Object

Resolve the node list using the resolver class

Parameters:

  • node_set (String)

    node set name



80
81
82
83
84
# File 'lib/mcollective/util/playbook/nodes.rb', line 80

def resolve_nodes(node_set)
  node_props = @nodes[node_set]
  node_props[:resolver].prepare
  node_props[:discovered] = node_props[:resolver].discover.uniq
end

#resolver_for(type) ⇒ Object

Retrieves a new instance of the resolver for a certain type of discovery

Parameters:

  • type (String)

    finds classes called Nodes::TypeNodes based on this type



173
174
175
176
177
178
179
# File 'lib/mcollective/util/playbook/nodes.rb', line 173

def resolver_for(type)
  klass_name = "%sNodes" % type.capitalize

  Nodes.const_get(klass_name).new
rescue NameError
  raise("Cannot find a handler for Node Set type %s" % type)
end

#should_test?(nodes) ⇒ Boolean

Determines if a nodeset needs connectivity test

Parameters:

  • nodes (String)

    node set name

Returns:

  • (Boolean)

Raises:

  • (StandardError)

    for unknown node sets



107
108
109
# File 'lib/mcollective/util/playbook/nodes.rb', line 107

def should_test?(nodes)
  !!properties(nodes)["test"]
end

#test_nodesObject

TODO:

is this really needed?

Tests a RPC ping to the discovered nodes

Raises:

  • (StandardError)

    on error



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/mcollective/util/playbook/nodes.rb', line 119

def test_nodes
  nodes_to_test = @nodes.map do |nodes, _|
    self[nodes] if should_test?(nodes)
  end.flatten.compact

  return if nodes_to_test.empty?

  Log.info("Checking connectivity for %d nodes" % nodes_to_test.size)

  rpc = mcollective_task
  rpc.from_hash(
    "nodes" => nodes_to_test,
    "action" => "rpcutil.ping",
    "silent" => true
  )
  success, msg, _ = rpc.run

  raise("Connectivity test failed for some nodes: %s" % [msg]) unless success
end

#validate_nodes(nodes) ⇒ Object

Checks that discovered nodes matches stated expectations

Parameters:

  • nodes (String)

    node set name

Raises:

  • (StandardError)

    on error



143
144
145
146
147
# File 'lib/mcollective/util/playbook/nodes.rb', line 143

def validate_nodes(nodes)
  return if properties(nodes)["empty_ok"]

  raise("Node set %s needs at least %d nodes, got %d" % [nodes, properties(nodes)["at_least"], self[nodes].size]) unless self[nodes].size >= properties(nodes)["at_least"]
end