Class: MCollective::Util::Playbook
- Inherits:
-
Object
- Object
- MCollective::Util::Playbook
- Includes:
- TemplateUtil
- Defined in:
- lib/mcollective/util/playbook.rb,
lib/mcollective/util/playbook/uses.rb,
lib/mcollective/util/playbook/nodes.rb,
lib/mcollective/util/playbook/tasks.rb,
lib/mcollective/util/playbook/inputs.rb,
lib/mcollective/util/playbook/report.rb,
lib/mcollective/util/playbook/tasks/base.rb,
lib/mcollective/util/playbook/data_stores.rb,
lib/mcollective/util/playbook/task_result.rb,
lib/mcollective/util/playbook/puppet_logger.rb,
lib/mcollective/util/playbook/template_util.rb,
lib/mcollective/util/playbook/nodes/pql_nodes.rb,
lib/mcollective/util/playbook/playbook_logger.rb,
lib/mcollective/util/playbook/data_stores/base.rb,
lib/mcollective/util/playbook/nodes/yaml_nodes.rb,
lib/mcollective/util/playbook/tasks/shell_task.rb,
lib/mcollective/util/playbook/tasks/slack_task.rb,
lib/mcollective/util/playbook/nodes/shell_nodes.rb,
lib/mcollective/util/playbook/tasks/webhook_task.rb,
lib/mcollective/util/playbook/nodes/terraform_nodes.rb,
lib/mcollective/util/playbook/tasks/mcollective_task.rb,
lib/mcollective/util/playbook/nodes/mcollective_nodes.rb,
lib/mcollective/util/playbook/tasks/graphite_event_task.rb,
lib/mcollective/util/playbook/data_stores/etcd_data_store.rb,
lib/mcollective/util/playbook/data_stores/file_data_store.rb,
lib/mcollective/util/playbook/data_stores/shell_data_store.rb,
lib/mcollective/util/playbook/data_stores/consul_data_store.rb,
lib/mcollective/util/playbook/data_stores/environment_data_store.rb
Defined Under Namespace
Modules: TemplateUtil Classes: DataStores, Inputs, Nodes, Playbook_Logger, Puppet_Logger, Report, TaskResult, Tasks, Uses
Instance Attribute Summary collapse
-
#context ⇒ Object
Returns the value of attribute context.
-
#data_stores ⇒ Object
readonly
Returns the value of attribute data_stores.
-
#input_data ⇒ Object
Returns the value of attribute input_data.
-
#logger ⇒ Object
Returns the value of attribute logger.
-
#metadata ⇒ Object
readonly
Returns the value of attribute metadata.
-
#report ⇒ Object
readonly
Returns the value of attribute report.
-
#tasks ⇒ Object
readonly
Returns the value of attribute tasks.
-
#uses ⇒ Object
readonly
Returns the value of attribute uses.
Instance Method Summary collapse
-
#add_cli_options(application, set_required = false) ⇒ Object
Adds the CLI options for an application based on the playbook inputs.
-
#discovered_nodes(nodeset) ⇒ Array<String>
Nodes belonging to a specific node set.
-
#dynamic_inputs ⇒ Array<String>
List of known input names that have dynamic values.
-
#from_hash(data) ⇒ Playbook
Loads the playbook data and prepare the runner.
- #in_context(context) ⇒ Object
-
#initialize(loglevel = nil) ⇒ Playbook
constructor
A new instance of Playbook.
-
#input_value(input) ⇒ Object
Retrieves the value for a specific input.
-
#inputs ⇒ Array<String>
A list of known input keys.
-
#lock_path(lock) ⇒ String
Derives a playbook lock from a given lock.
- #loglevel ⇒ Object
-
#metadata_item(item) ⇒ Object
Retrieves an item from the metadata.
-
#name ⇒ String
Playbook name as declared in metadata.
-
#nodes ⇒ Array<String>
A list of known node sets.
-
#obtain_playbook_locks ⇒ Object
Obtains the playbook level locks.
- #prepare ⇒ Object
-
#prepare_data_stores ⇒ Object
Prepares the data sources from the plabook.
-
#prepare_inputs ⇒ Object
Prepares the inputs from the playbook.
-
#prepare_nodes ⇒ Object
Prepares the ode lists from the Playbook.
-
#prepare_tasks ⇒ Object
Prepares the tasks lists ‘tasks` and `hooks` from the Playbook data.
-
#prepare_uses ⇒ Object
Prepares the uses clauses from the playbook.
-
#previous_task(property) ⇒ Object
Looks up a proeprty of the previous task.
-
#previous_task_result ⇒ TaskResult?
Find the last result from the tasks ran.
-
#release_playbook_locks ⇒ Object
Obtains the playbook level locks.
-
#run!(inputs) ⇒ Hash
Runs the playbook.
-
#save_input_data ⇒ Object
Saves data from any inputs that requested they be written to data stores.
- #seconds_to_human(seconds) ⇒ Object
- #set_logger_level ⇒ Object
-
#static_inputs ⇒ Array<String>
List of known input names that have static values.
-
#task_results ⇒ Array<TaskResult>
All the task results.
-
#validate_agents(agents) ⇒ Object
Validates agent versions on nodes.
-
#validate_configuration! ⇒ Object
Validate the playbook structure.
-
#version ⇒ String
Playbook version as declared in metadata.
Methods included from TemplateUtil
#__template_process_string, #__template_resolve, #t
Constructor Details
#initialize(loglevel = nil) ⇒ Playbook
Returns a new instance of Playbook.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/mcollective/util/playbook.rb', line 19 def initialize(loglevel=nil) @loglevel = loglevel @report = Report.new(self) # @todo dear god this Camel_Snake horror but mcollective requires this # Configures the main MCollective logger with our custom logger @logger = Log.set_logger(Playbook_Logger.new(self)) @nodes = Nodes.new(self) @tasks = Tasks.new(self) @uses = Uses.new(self) @inputs = Inputs.new(self) @data_stores = DataStores.new(self) @playbook = self @playbook_data = {} @input_data = {} @metadata = { "name" => nil, "version" => nil, "author" => nil, "description" => nil, "tags" => [], "on_fail" => "fail", "loglevel" => "info", "run_as" => "choria=deployer" } end |
Instance Attribute Details
#context ⇒ Object
Returns the value of attribute context.
16 17 18 |
# File 'lib/mcollective/util/playbook.rb', line 16 def context @context end |
#data_stores ⇒ Object (readonly)
Returns the value of attribute data_stores.
17 18 19 |
# File 'lib/mcollective/util/playbook.rb', line 17 def data_stores @data_stores end |
#input_data ⇒ Object
Returns the value of attribute input_data.
16 17 18 |
# File 'lib/mcollective/util/playbook.rb', line 16 def input_data @input_data end |
#logger ⇒ Object
Returns the value of attribute logger.
17 18 19 |
# File 'lib/mcollective/util/playbook.rb', line 17 def logger @logger end |
#metadata ⇒ Object (readonly)
Returns the value of attribute metadata.
17 18 19 |
# File 'lib/mcollective/util/playbook.rb', line 17 def @metadata end |
#report ⇒ Object (readonly)
Returns the value of attribute report.
17 18 19 |
# File 'lib/mcollective/util/playbook.rb', line 17 def report @report end |
#tasks ⇒ Object (readonly)
Returns the value of attribute tasks.
17 18 19 |
# File 'lib/mcollective/util/playbook.rb', line 17 def tasks @tasks end |
#uses ⇒ Object (readonly)
Returns the value of attribute uses.
17 18 19 |
# File 'lib/mcollective/util/playbook.rb', line 17 def uses @uses end |
Instance Method Details
#add_cli_options(application, set_required = false) ⇒ Object
Adds the CLI options for an application based on the playbook inputs
398 399 400 |
# File 'lib/mcollective/util/playbook.rb', line 398 def (application, set_required=false) @inputs.(application, set_required) end |
#discovered_nodes(nodeset) ⇒ Array<String>
Nodes belonging to a specific node set
311 312 313 |
# File 'lib/mcollective/util/playbook.rb', line 311 def discovered_nodes(nodeset) @nodes[nodeset].clone end |
#dynamic_inputs ⇒ Array<String>
List of known input names that have dynamic values
337 338 339 |
# File 'lib/mcollective/util/playbook.rb', line 337 def dynamic_inputs @inputs.dynamic_keys end |
#from_hash(data) ⇒ Playbook
Loads the playbook data and prepare the runner
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/mcollective/util/playbook.rb', line 60 def from_hash(data) in_context("loading") do @playbook_data = data @metadata = { "name" => data["name"], "version" => data["version"], "author" => data["author"], "description" => data["description"], "tags" => data.fetch("tags", []), "on_fail" => data.fetch("on_fail", "fail"), "loglevel" => data.fetch("loglevel", "info"), "run_as" => data["run_as"] } end set_logger_level in_context("inputs") do @inputs.from_hash(data.fetch("inputs", {})) end self end |
#in_context(context) ⇒ Object
402 403 404 405 406 407 408 409 |
# File 'lib/mcollective/util/playbook.rb', line 402 def in_context(context) old_context = @context @context = context yield ensure @context = old_context end |
#input_value(input) ⇒ Object
Retrieves the value for a specific input
323 324 325 |
# File 'lib/mcollective/util/playbook.rb', line 323 def input_value(input) @inputs[input] end |
#inputs ⇒ Array<String>
A list of known input keys
330 331 332 |
# File 'lib/mcollective/util/playbook.rb', line 330 def inputs @inputs.keys end |
#lock_path(lock) ⇒ String
Derives a playbook lock from a given lock
If a lock is in the normal valid format of source/lock then it’s assumed the user gave a full path and knows what she wants otherwise a path will be constructed using the playbook name
167 168 169 |
# File 'lib/mcollective/util/playbook.rb', line 167 def lock_path(lock) lock =~ /^[a-zA-Z0-9\-_]+\/.+$/ ? lock : "%s/choria/locks/playbook/%s" % [lock, name] end |
#loglevel ⇒ Object
239 240 241 |
# File 'lib/mcollective/util/playbook.rb', line 239 def loglevel @loglevel || ("loglevel") || "info" end |
#metadata_item(item) ⇒ Object
Retrieves an item from the metadata
302 303 304 305 306 307 308 |
# File 'lib/mcollective/util/playbook.rb', line 302 def (item) if @metadata.include?(item) @metadata[item] else raise("Unknown playbook metadata %s" % item) end end |
#name ⇒ String
Playbook name as declared in metadata
228 229 230 |
# File 'lib/mcollective/util/playbook.rb', line 228 def name ("name") end |
#nodes ⇒ Array<String>
A list of known node sets
318 319 320 |
# File 'lib/mcollective/util/playbook.rb', line 318 def nodes @nodes.keys end |
#obtain_playbook_locks ⇒ Object
Obtains the playbook level locks
172 173 174 175 176 177 |
# File 'lib/mcollective/util/playbook.rb', line 172 def obtain_playbook_locks Array(@playbook_data["locks"]).each do |lock| Log.info("Obtaining playbook lock %s" % [lock_path(lock)]) @data_stores.lock(lock_path(lock)) end end |
#prepare ⇒ Object
141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/mcollective/util/playbook.rb', line 141 def prepare prepare_inputs prepare_data_stores save_input_data obtain_playbook_locks prepare_uses prepare_nodes prepare_tasks end |
#prepare_data_stores ⇒ Object
Prepares the data sources from the plabook
248 249 250 |
# File 'lib/mcollective/util/playbook.rb', line 248 def prepare_data_stores in_context("pre.stores") { @data_stores.from_hash(t(@playbook_data["data_stores"] || {})).prepare } end |
#prepare_inputs ⇒ Object
same pattern as prepare_uses and nodes
this should be done first, before any uses, nodes or tasks are prepared
Prepares the inputs from the playbook
257 258 259 |
# File 'lib/mcollective/util/playbook.rb', line 257 def prepare_inputs in_context("prep.inputs") { @inputs.prepare(@input_data) } end |
#prepare_nodes ⇒ Object
Prepares the ode lists from the Playbook
271 272 273 |
# File 'lib/mcollective/util/playbook.rb', line 271 def prepare_nodes in_context("prep.nodes") { @nodes.from_hash(t(@playbook_data["nodes"] || {})).prepare } end |
#prepare_tasks ⇒ Object
Prepares the tasks lists ‘tasks` and `hooks` from the Playbook data
278 279 280 281 282 283 284 285 286 287 |
# File 'lib/mcollective/util/playbook.rb', line 278 def prepare_tasks # we lazy template parse these so that they might refer to run time # state via the template system - like for example in a post task you # might want to reference properties of another rpc request in_context("prep.tasks") do @tasks.from_hash(@playbook_data["tasks"] || []) @tasks.from_hash(@playbook_data["hooks"] || {}) @tasks.prepare end end |
#prepare_uses ⇒ Object
Prepares the uses clauses from the playbook
264 265 266 |
# File 'lib/mcollective/util/playbook.rb', line 264 def prepare_uses in_context("prep.uses") { @uses.from_hash(t(@playbook_data["uses"] || {})).prepare } end |
#previous_task(property) ⇒ Object
Looks up a proeprty of the previous task
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
# File 'lib/mcollective/util/playbook.rb', line 352 def previous_task(property) if property == "success" return false unless previous_task_result && previous_task_result.ran previous_task_result.success elsif ["msg", "message"].include?(property) return "No previous task were found" unless previous_task_result return "Previous task did not run" unless previous_task_result.ran previous_task_result.msg elsif property == "data" return [] unless previous_task_result && previous_task_result.ran previous_task_result.data || [] elsif property == "description" return "No previous task were found" unless previous_task_result previous_task_result.task[:description] elsif property == "runtime" return 0 unless previous_task_result && previous_task_result.ran previous_task_result.run_time.round(2) else raise("Cannot retrieve %s for the last task outcome" % property) end end |
#previous_task_result ⇒ TaskResult?
Find the last result from the tasks ran
389 390 391 |
# File 'lib/mcollective/util/playbook.rb', line 389 def previous_task_result task_results.last end |
#release_playbook_locks ⇒ Object
Obtains the playbook level locks
180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/mcollective/util/playbook.rb', line 180 def release_playbook_locks Array(@playbook_data["locks"]).each do |lock| Log.info("Releasing playbook lock %s" % [lock_path(lock)]) begin @data_stores.release(lock_path(lock)) rescue Log.warn("Lock %s could not be released, ignoring" % lock_path(lock)) end end end |
#run!(inputs) ⇒ Hash
Runs the playbook
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/mcollective/util/playbook.rb', line 196 def run!(inputs) success = false validate_configuration! begin start_time = @report.start! @input_data = inputs in_context("pre") { Log.info("Starting playbook %s at %s" % [name, start_time]) } prepare success = in_context("run") { @tasks.run } in_context("post") { Log.info("Done running playbook %s in %s" % [name, seconds_to_human(Integer(@report.elapsed_time))]) } release_playbook_locks rescue msg = "Playbook %s failed: %s: %s" % [name, $!.class, $!.to_s] Log.error(msg) Log.debug($!.backtrace.join("\n\t")) report.finalize(false, msg) end report.finalize(success) end |
#save_input_data ⇒ Object
Saves data from any inputs that requested they be written to data stores
155 156 157 |
# File 'lib/mcollective/util/playbook.rb', line 155 def save_input_data @inputs.save_input_data end |
#seconds_to_human(seconds) ⇒ Object
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 |
# File 'lib/mcollective/util/playbook.rb', line 411 def seconds_to_human(seconds) days = seconds / 86400 seconds -= 86400 * days hours = seconds / 3600 seconds -= 3600 * hours minutes = seconds / 60 seconds -= 60 * minutes if days > 1 "%d days %d hours %d minutes %02d seconds" % [days, hours, minutes, seconds] elsif days == 1 "%d day %d hours %d minutes %02d seconds" % [days, hours, minutes, seconds] elsif hours > 0 "%d hours %d minutes %02d seconds" % [hours, minutes, seconds] elsif minutes > 0 "%d minutes %02d seconds" % [minutes, seconds] else "%02d seconds" % seconds end end |
#set_logger_level ⇒ Object
243 244 245 |
# File 'lib/mcollective/util/playbook.rb', line 243 def set_logger_level @logger.set_level(loglevel.intern) end |
#static_inputs ⇒ Array<String>
List of known input names that have static values
344 345 346 |
# File 'lib/mcollective/util/playbook.rb', line 344 def static_inputs @inputs.static_keys end |
#task_results ⇒ Array<TaskResult>
All the task results
382 383 384 |
# File 'lib/mcollective/util/playbook.rb', line 382 def task_results @tasks.results end |
#validate_agents(agents) ⇒ Object
Validates agent versions on nodes
293 294 295 |
# File 'lib/mcollective/util/playbook.rb', line 293 def validate_agents(agents) @uses.validate_agents(agents) end |
#validate_configuration! ⇒ Object
use JSON Schema
Validate the playbook structure
This is a pretty grim way to do this, ideally AIO would include some JSON Schema tools but they don’t and I don’t want to carray a dependency on that now
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 |
# File 'lib/mcollective/util/playbook.rb', line 92 def validate_configuration! in_context("validation") do failed = false valid_keys = (@metadata.keys + ["uses", "inputs", "locks", "data_stores", "nodes", "tasks", "hooks", "macros", "$schema"]) invalid_keys = @playbook_data.keys - valid_keys unless invalid_keys.empty? Log.error("Invalid playbook data items %s found" % invalid_keys.join(", ")) failed = true end ["name", "version", "author", "description"].each do |item| unless @metadata[item] Log.error("A playbook %s is needed" % item) failed = true end end unless ["debug", "info", "warn", "error", "fatal"].include?(@metadata["loglevel"]) Log.error("Invalid log level %s, valid levels are debug, info, warn, error, fatal" % @metadata["loglevel"]) failed = true end unless PluginManager["security_plugin"].valid_callerid?(@metadata["run_as"]) Log.error("Invalid callerid %s" % @metadata["run_as"]) failed = true end ["uses", "nodes", "hooks", "data_stores", "inputs"].each do |key| next unless @playbook_data.include?(key) next if @playbook_data[key].is_a?(Hash) Log.error("%s should be a hash" % key) failed = true end ["locks", "tasks"].each do |key| next unless @playbook_data.include?(key) next if @playbook_data[key].is_a?(Array) Log.error("%s should be a array" % key) failed = true end raise("Playbook is not in a valid format") if failed end end |
#version ⇒ String
Playbook version as declared in metadata
235 236 237 |
# File 'lib/mcollective/util/playbook.rb', line 235 def version ("version") end |