moose-inventory 2.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +2 -0
- data/.gitignore +6 -1
- data/.rubocop.yml +21 -0
- data/BACKLOG.md +638 -9
- data/Gemfile +2 -0
- data/Gemfile.lock +1 -1
- data/README.md +315 -39
- data/Rakefile +2 -0
- data/bin/moose-inventory +2 -1
- data/docs/architecture/architecture-and-trust-boundaries.md +444 -0
- data/docs/compatibility/cli-output-compatibility.md +76 -0
- data/docs/governance/approval-register.md +37 -0
- data/docs/maintenance/database-backup-restore-guidance.md +162 -0
- data/docs/maintenance/package-maintenance-and-agent-boundaries.md +260 -0
- data/docs/process/conformance-gap-analysis-2026-05-28.md +192 -0
- data/docs/product/product-brief.md +161 -0
- data/docs/product/requirements-baseline.md +477 -0
- data/docs/qa/qa-documentation-and-release-gates.md +283 -0
- data/docs/release/package-provenance-hardening.md +126 -0
- data/docs/release/publishing.md +11 -3
- data/docs/release/release-environment-protection.md +78 -0
- data/docs/release/release-readiness.md +23 -4
- data/docs/security/accepted-risk-register.md +84 -0
- data/docs/security/security-privacy-process.md +287 -0
- data/docs/security-audit-2026-05-26-rerun.md +2 -2
- data/docs/security-audit-2026-05-29-snapshot-import-fuzz.md +58 -0
- data/docs/ux/cli-workflow-notes.md +287 -0
- data/examples/ansible/ansible.cfg +3 -0
- data/examples/ansible/inventory/moose_inventory.yml +5 -0
- data/examples/ansible/inventory_plugins/moose_inventory.py +100 -0
- data/examples/ci/README.md +16 -0
- data/examples/ci/github-actions/inventory-review.yml +38 -0
- data/examples/ci/inventory/example-snapshot.yml +19 -0
- data/examples/ci/scripts/validate-inventory-snapshot.sh +30 -0
- data/lib/moose_inventory/cli/application.rb +135 -5
- data/lib/moose_inventory/cli/association_rendering.rb +74 -0
- data/lib/moose_inventory/cli/association_rendering_support.rb +89 -0
- data/lib/moose_inventory/cli/audit.rb +62 -0
- data/lib/moose_inventory/cli/audit_recording.rb +40 -0
- data/lib/moose_inventory/cli/child_relation_rendering.rb +110 -0
- data/lib/moose_inventory/cli/console.rb +135 -0
- data/lib/moose_inventory/cli/db.rb +64 -0
- data/lib/moose_inventory/cli/factory.rb +28 -0
- data/lib/moose_inventory/cli/formatter.rb +8 -12
- data/lib/moose_inventory/cli/group.rb +5 -2
- data/lib/moose_inventory/cli/group_add.rb +11 -9
- data/lib/moose_inventory/cli/group_addchild.rb +23 -65
- data/lib/moose_inventory/cli/group_addhost.rb +16 -67
- data/lib/moose_inventory/cli/group_addvar.rb +27 -47
- data/lib/moose_inventory/cli/group_get.rb +8 -42
- data/lib/moose_inventory/cli/group_list.rb +7 -40
- data/lib/moose_inventory/cli/group_listvars.rb +9 -55
- data/lib/moose_inventory/cli/group_rm.rb +12 -10
- data/lib/moose_inventory/cli/group_rmchild.rb +26 -82
- data/lib/moose_inventory/cli/group_rmhost.rb +18 -53
- data/lib/moose_inventory/cli/group_rmvar.rb +30 -41
- data/lib/moose_inventory/cli/group_tags.rb +33 -0
- data/lib/moose_inventory/cli/helpers.rb +68 -1
- data/lib/moose_inventory/cli/host.rb +6 -3
- data/lib/moose_inventory/cli/host_add.rb +69 -29
- data/lib/moose_inventory/cli/host_addgroup.rb +22 -58
- data/lib/moose_inventory/cli/host_addvar.rb +28 -52
- data/lib/moose_inventory/cli/host_get.rb +9 -37
- data/lib/moose_inventory/cli/host_list.rb +24 -21
- data/lib/moose_inventory/cli/host_listvars.rb +9 -62
- data/lib/moose_inventory/cli/host_rm.rb +60 -42
- data/lib/moose_inventory/cli/host_rmgroup.rb +25 -44
- data/lib/moose_inventory/cli/host_rmvar.rb +31 -45
- data/lib/moose_inventory/cli/host_tags.rb +33 -0
- data/lib/moose_inventory/cli/listvars_support.rb +55 -0
- data/lib/moose_inventory/cli/plan_rendering.rb +50 -0
- data/lib/moose_inventory/cli/relation_transaction_support.rb +51 -0
- data/lib/moose_inventory/cli/tag_support.rb +97 -0
- data/lib/moose_inventory/cli/variable_rendering.rb +67 -0
- data/lib/moose_inventory/config/config.rb +185 -108
- data/lib/moose_inventory/db/db.rb +170 -195
- data/lib/moose_inventory/db/exceptions.rb +6 -3
- data/lib/moose_inventory/db/models.rb +16 -0
- data/lib/moose_inventory/db/schema_migrations.rb +248 -0
- data/lib/moose_inventory/inventory_context.rb +68 -2
- data/lib/moose_inventory/operations/add_associations.rb +20 -16
- data/lib/moose_inventory/operations/add_groups.rb +21 -13
- data/lib/moose_inventory/operations/add_hosts.rb +30 -17
- data/lib/moose_inventory/operations/add_variables.rb +77 -0
- data/lib/moose_inventory/operations/entity_variable_operation_support.rb +46 -0
- data/lib/moose_inventory/operations/group_child_relations.rb +23 -16
- data/lib/moose_inventory/operations/group_cleanup.rb +23 -8
- data/lib/moose_inventory/operations/import_inventory_snapshot.rb +41 -0
- data/lib/moose_inventory/operations/inventory_doctor.rb +172 -0
- data/lib/moose_inventory/operations/inventory_snapshot.rb +60 -0
- data/lib/moose_inventory/operations/inventory_snapshot_applier.rb +112 -0
- data/lib/moose_inventory/operations/inventory_snapshot_preview.rb +174 -0
- data/lib/moose_inventory/operations/inventory_snapshot_validator.rb +174 -0
- data/lib/moose_inventory/operations/operation_event_support.rb +27 -0
- data/lib/moose_inventory/operations/query_inventory/base_query.rb +24 -0
- data/lib/moose_inventory/operations/query_inventory/group_queries.rb +86 -0
- data/lib/moose_inventory/operations/query_inventory/host_queries.rb +106 -0
- data/lib/moose_inventory/operations/query_inventory.rb +47 -0
- data/lib/moose_inventory/operations/remove_associations.rb +30 -18
- data/lib/moose_inventory/operations/remove_groups.rb +12 -12
- data/lib/moose_inventory/operations/remove_hosts.rb +68 -0
- data/lib/moose_inventory/operations/remove_variables.rb +67 -0
- data/lib/moose_inventory/runtime_options.rb +31 -0
- data/lib/moose_inventory/version.rb +3 -1
- data/lib/moose_inventory.rb +10 -7
- data/moose-inventory.gemspec +19 -35
- data/scripts/check.sh +1 -0
- data/scripts/ci/check_generated_artifacts.sh +41 -0
- data/scripts/ci/check_permissions.sh +2 -0
- data/scripts/ci/check_rubocop.sh +30 -25
- data/scripts/ci/check_security.sh +4 -1
- data/scripts/files.rb +5 -4
- data/spec/examples/ci_examples_spec.rb +37 -0
- data/spec/lib/moose_inventory/ansible_plugin_examples_spec.rb +29 -0
- data/spec/lib/moose_inventory/cli/application_doctor_spec.rb +50 -0
- data/spec/lib/moose_inventory/cli/application_import_export_spec.rb +132 -0
- data/spec/lib/moose_inventory/cli/application_spec.rb +25 -15
- data/spec/lib/moose_inventory/cli/audit_spec.rb +56 -0
- data/spec/lib/moose_inventory/cli/cli_spec.rb +15 -19
- data/spec/lib/moose_inventory/cli/console_spec.rb +98 -0
- data/spec/lib/moose_inventory/cli/factory_spec.rb +27 -0
- data/spec/lib/moose_inventory/cli/formatter_spec.rb +95 -3
- data/spec/lib/moose_inventory/cli/group_add_spec.rb +140 -116
- data/spec/lib/moose_inventory/cli/group_addchild_spec.rb +89 -35
- data/spec/lib/moose_inventory/cli/group_addhost_spec.rb +81 -84
- data/spec/lib/moose_inventory/cli/group_addvar_spec.rb +65 -68
- data/spec/lib/moose_inventory/cli/group_get_spec.rb +17 -33
- data/spec/lib/moose_inventory/cli/group_list_spec.rb +16 -38
- data/spec/lib/moose_inventory/cli/group_listvar_spec.rb +33 -40
- data/spec/lib/moose_inventory/cli/group_rm_spec.rb +136 -96
- data/spec/lib/moose_inventory/cli/group_rmchild_spec.rb +66 -41
- data/spec/lib/moose_inventory/cli/group_rmhost_spec.rb +76 -78
- data/spec/lib/moose_inventory/cli/group_rmvar_spec.rb +57 -63
- data/spec/lib/moose_inventory/cli/group_spec.rb +2 -0
- data/spec/lib/moose_inventory/cli/helpers_spec.rb +146 -0
- data/spec/lib/moose_inventory/cli/host_add_spec.rb +170 -116
- data/spec/lib/moose_inventory/cli/host_addgroup_spec.rb +100 -83
- data/spec/lib/moose_inventory/cli/host_addvar_spec.rb +92 -74
- data/spec/lib/moose_inventory/cli/host_get_spec.rb +14 -33
- data/spec/lib/moose_inventory/cli/host_list_spec.rb +41 -33
- data/spec/lib/moose_inventory/cli/host_listvar_spec.rb +45 -53
- data/spec/lib/moose_inventory/cli/host_rm_spec.rb +66 -48
- data/spec/lib/moose_inventory/cli/host_rmgroup_spec.rb +73 -83
- data/spec/lib/moose_inventory/cli/host_rmvar_spec.rb +56 -63
- data/spec/lib/moose_inventory/cli/host_spec.rb +2 -0
- data/spec/lib/moose_inventory/cli/tags_spec.rb +81 -0
- data/spec/lib/moose_inventory/config/config_spec.rb +41 -3
- data/spec/lib/moose_inventory/db/db_spec.rb +396 -36
- data/spec/lib/moose_inventory/db/exceptions_spec.rb +18 -0
- data/spec/lib/moose_inventory/db/models_spec.rb +7 -3
- data/spec/lib/moose_inventory/db_lifecycle_spec.rb +73 -0
- data/spec/lib/moose_inventory/inventory_context_spec.rb +10 -0
- data/spec/lib/moose_inventory/operations/add_associations_spec.rb +34 -0
- data/spec/lib/moose_inventory/operations/add_groups_spec.rb +15 -0
- data/spec/lib/moose_inventory/operations/add_hosts_spec.rb +13 -0
- data/spec/lib/moose_inventory/operations/add_variables_spec.rb +103 -0
- data/spec/lib/moose_inventory/operations/group_child_relations_spec.rb +46 -0
- data/spec/lib/moose_inventory/operations/import_inventory_snapshot_spec.rb +239 -0
- data/spec/lib/moose_inventory/operations/inventory_doctor_spec.rb +77 -0
- data/spec/lib/moose_inventory/operations/inventory_snapshot_spec.rb +50 -0
- data/spec/lib/moose_inventory/operations/operation_event_support_spec.rb +78 -0
- data/spec/lib/moose_inventory/operations/query_inventory_spec.rb +146 -0
- data/spec/lib/moose_inventory/operations/remove_associations_spec.rb +35 -0
- data/spec/lib/moose_inventory/operations/remove_groups_spec.rb +21 -0
- data/spec/lib/moose_inventory/operations/remove_hosts_spec.rb +55 -0
- data/spec/lib/moose_inventory/operations/remove_variables_spec.rb +83 -0
- data/spec/shared/shared_config_setup.rb +4 -3
- data/spec/spec_helper.rb +50 -40
- data/spec/support/cli_harness.rb +33 -0
- metadata +81 -41
|
@@ -16,80 +16,44 @@ module Moose
|
|
|
16
16
|
class Host
|
|
17
17
|
desc 'addgroup HOSTNAME GROUPNAME [GROUPNAME ...]',
|
|
18
18
|
'Associate the host with a group'
|
|
19
|
+
option :dry_run, type: :boolean
|
|
20
|
+
option :plan_format, type: :string, desc: 'Emit dry-run plan events as yaml|json|pjson'
|
|
19
21
|
def addgroup(*args)
|
|
20
22
|
abort_if_missing_args(args, 2, '2 or more')
|
|
23
|
+
validate_machine_plan_request!
|
|
21
24
|
|
|
22
25
|
name = args[0].downcase
|
|
23
26
|
groups = normalize_names(args.slice(1, args.length - 1))
|
|
24
27
|
|
|
25
28
|
abort_if_automatic_group(groups)
|
|
26
29
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
)
|
|
36
|
-
fmt.puts 2, '- All OK'
|
|
30
|
+
result = add_groups_to_host(name, groups)
|
|
31
|
+
unless machine_plan_output_rendered?(
|
|
32
|
+
result, command: 'host addgroup'
|
|
33
|
+
)
|
|
34
|
+
record_audit({ command: 'host addgroup', action: 'associate', entity_type: 'host',
|
|
35
|
+
entity_names: name }, result: result, dry_run: options[:dry_run])
|
|
36
|
+
print_warning_summary(result, success_message: 'Succeeded',
|
|
37
|
+
warning_message: 'Succeeded')
|
|
37
38
|
end
|
|
38
|
-
|
|
39
|
-
puts 'Succeeded'
|
|
40
39
|
end
|
|
41
40
|
|
|
42
41
|
private
|
|
43
42
|
|
|
44
|
-
def
|
|
45
|
-
|
|
46
|
-
host
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def render_host_addgroup_events(events)
|
|
54
|
-
events.each { |event| render_host_addgroup_event(event) }
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def render_host_addgroup_event(event)
|
|
58
|
-
payload = event.payload
|
|
59
|
-
|
|
60
|
-
return render_host_addgroup_warning(event.type, payload) if host_addgroup_warning?(event.type)
|
|
61
|
-
return render_host_addgroup_status(payload) if event.type == :already_exists_skipping
|
|
62
|
-
|
|
63
|
-
render_host_addgroup_output(event.type, payload)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def host_addgroup_warning?(type)
|
|
67
|
-
%i[host_group_association_exists group_missing_created].include?(type)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def render_host_addgroup_warning(type, payload)
|
|
71
|
-
if type == :host_group_association_exists
|
|
72
|
-
fmt.warn "Association {host:#{payload[:host]} <-> group:#{payload[:group]}} already exists, skipping."
|
|
73
|
-
else
|
|
74
|
-
fmt.warn "Group '#{payload[:name]}' does not exist and will be created."
|
|
43
|
+
def add_groups_to_host(name, groups)
|
|
44
|
+
operation = build_operation(Moose::Inventory::Operations::AddAssociations)
|
|
45
|
+
run_host_relation_transaction(heading: "Associate host '#{name}' with groups '#{groups.join(',')}':") do
|
|
46
|
+
host = fetch_existing_host_or_raise(name)
|
|
47
|
+
result = operation.host_to_groups(host: host, host_name: name, group_names: groups,
|
|
48
|
+
dry_run: options[:dry_run])
|
|
49
|
+
render_host_addgroup_events(result.events) unless machine_plan_output_requested?
|
|
50
|
+
result
|
|
75
51
|
end
|
|
76
52
|
end
|
|
77
53
|
|
|
78
|
-
def
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def render_host_addgroup_output(type, payload)
|
|
83
|
-
case type
|
|
84
|
-
when :adding_host_group_association
|
|
85
|
-
fmt.puts 2, "- Add association {host:#{payload[:host]} <-> group:#{payload[:group]}}..."
|
|
86
|
-
when :group_creating_now
|
|
87
|
-
fmt.puts 4, '- Group does not exist, creating now...'
|
|
88
|
-
when :removing_automatic_group
|
|
89
|
-
fmt.puts 2, "- Remove automatic association {host:#{payload[:host]} <-> group:ungrouped}..."
|
|
90
|
-
when :ok
|
|
91
|
-
fmt.puts payload[:indent], '- OK'
|
|
92
|
-
end
|
|
54
|
+
def render_host_addgroup_events(events)
|
|
55
|
+
emitter = host_group_association_addition_emitter(perspective: :host)
|
|
56
|
+
events.each { |event| emitter.call(event) }
|
|
93
57
|
end
|
|
94
58
|
end
|
|
95
59
|
end
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'thor'
|
|
2
4
|
require 'json'
|
|
3
5
|
|
|
4
|
-
require_relative '
|
|
5
|
-
require_relative '../
|
|
6
|
+
require_relative '../inventory_context'
|
|
7
|
+
require_relative '../operations/add_variables'
|
|
6
8
|
|
|
7
9
|
module Moose
|
|
8
10
|
module Inventory
|
|
@@ -12,64 +14,38 @@ module Moose
|
|
|
12
14
|
class Host
|
|
13
15
|
#==========================
|
|
14
16
|
desc 'addvar', 'Add a variable to the host'
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"#{args.length} for 2 or more.")
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# Convenience
|
|
24
|
-
db = Moose::Inventory::DB
|
|
25
|
-
fmt = Moose::Inventory::Cli::Formatter
|
|
17
|
+
option :dry_run, type: :boolean
|
|
18
|
+
option :plan_format, type: :string, desc: 'Emit dry-run plan events as yaml|json|pjson'
|
|
19
|
+
def addvar(*args)
|
|
20
|
+
abort_if_missing_args(args, 2, '2 or more')
|
|
21
|
+
validate_machine_plan_request!
|
|
26
22
|
|
|
27
|
-
# Arguments
|
|
28
23
|
name = args[0].downcase
|
|
29
24
|
vars = args.slice(1, args.length - 1).uniq
|
|
25
|
+
operation = build_operation(Moose::Inventory::Operations::AddVariables,
|
|
26
|
+
entity_type: :host,
|
|
27
|
+
emitter: machine_plan_emitter(host_addvar_emitter(name, vars)))
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
result = db.transaction do
|
|
30
|
+
operation.call(name: name, vars: vars, dry_run: options[:dry_run])
|
|
31
|
+
end
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
host = db.models[:host].find(name: name)
|
|
37
|
-
if host.nil?
|
|
38
|
-
fail db.exceptions[:moose],
|
|
39
|
-
"The host '#{name}' does not exist."
|
|
40
|
-
end
|
|
41
|
-
fmt.puts 4, '- OK'
|
|
33
|
+
return if machine_plan_output_rendered?(result, command: 'host addvar')
|
|
42
34
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if v.start_with?('=') || v.end_with?('=') || vararray.length != 2
|
|
48
|
-
fail db.exceptions[:moose],
|
|
49
|
-
"Incorrect format in '{#{v}}'. Expected 'key=value'."
|
|
50
|
-
end
|
|
35
|
+
record_audit({ command: 'host addvar', action: 'add_variable', entity_type: 'host',
|
|
36
|
+
entity_names: name }, result: result, dry_run: options[:dry_run])
|
|
37
|
+
print_success_summary
|
|
38
|
+
end
|
|
51
39
|
|
|
52
|
-
|
|
53
|
-
hostvar = hostvars_ds[name: vararray[0]]
|
|
54
|
-
if !hostvar.nil?
|
|
55
|
-
unless hostvar[:value] == vararray[1]
|
|
56
|
-
fmt.puts 4, '- already exists, applying as an update...'
|
|
57
|
-
update = db.models[:hostvar].find(id: hostvar[:id])
|
|
58
|
-
update[:value] = vararray[1]
|
|
59
|
-
update.save
|
|
60
|
-
end
|
|
61
|
-
else
|
|
62
|
-
# hostvar doesn't exist, so create and associate
|
|
63
|
-
hostvar = db.models[:hostvar].create(name: vararray[0],
|
|
64
|
-
value: vararray[1])
|
|
65
|
-
host.add_hostvar(hostvar)
|
|
66
|
-
end
|
|
67
|
-
fmt.puts 4, '- OK'
|
|
68
|
-
end
|
|
69
|
-
fmt.puts 2, '- all OK'
|
|
70
|
-
end # Transaction end
|
|
40
|
+
private
|
|
71
41
|
|
|
72
|
-
|
|
42
|
+
def host_addvar_emitter(name, vars)
|
|
43
|
+
variable_operation_emitter(
|
|
44
|
+
action: :add,
|
|
45
|
+
entity_label: 'host',
|
|
46
|
+
entity_name: name,
|
|
47
|
+
variables_label: vars.join(',')
|
|
48
|
+
)
|
|
73
49
|
end
|
|
74
50
|
end
|
|
75
51
|
end
|
|
@@ -1,54 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'thor'
|
|
2
4
|
require 'json'
|
|
3
5
|
|
|
4
|
-
require_relative '
|
|
5
|
-
require_relative '../
|
|
6
|
+
require_relative '../inventory_context'
|
|
7
|
+
require_relative '../operations/query_inventory'
|
|
6
8
|
|
|
7
9
|
module Moose
|
|
8
10
|
module Inventory
|
|
9
11
|
module Cli
|
|
10
|
-
##
|
|
11
12
|
# Class implementing the "host get" method of the CLI
|
|
12
13
|
class Host
|
|
13
14
|
require_relative 'host_add'
|
|
14
15
|
|
|
15
|
-
#==========================
|
|
16
16
|
desc 'get HOST_1 [HOST_2 ...]',
|
|
17
17
|
'Get hosts HOST_n from the inventory'
|
|
18
|
-
def get(*argv)
|
|
19
|
-
if argv.empty?
|
|
20
|
-
abort('ERROR: Wrong number of arguments, '\
|
|
21
|
-
"#{argv.length} for 1 or more")
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# Convenience
|
|
25
|
-
db = Moose::Inventory::DB
|
|
26
|
-
fmt = Moose::Inventory::Cli::Formatter
|
|
27
|
-
|
|
28
|
-
# Arguments
|
|
29
|
-
names = argv.uniq.map(&:downcase)
|
|
30
|
-
|
|
31
|
-
# Process
|
|
32
|
-
results = {}
|
|
33
|
-
names.each do |name|
|
|
34
|
-
host = db.models[:host].find(name: name)
|
|
35
|
-
|
|
36
|
-
next if host.nil?
|
|
37
|
-
groups = host.groups_dataset.map(:name)
|
|
38
|
-
|
|
39
|
-
hostvars = {}
|
|
40
|
-
host.hostvars_dataset.each do |hv|
|
|
41
|
-
hostvars[hv[:name].to_sym] = hv[:value]
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
results[host[:name].to_sym] = {}
|
|
45
|
-
results[host[:name].to_sym][:groups] = groups unless groups.empty?
|
|
46
|
-
unless hostvars.empty?
|
|
47
|
-
results[host[:name].to_sym][:hostvars] = hostvars
|
|
48
|
-
end
|
|
49
|
-
end
|
|
18
|
+
def get(*argv)
|
|
19
|
+
abort("ERROR: Wrong number of arguments, #{argv.length} for 1 or more") if argv.empty?
|
|
50
20
|
|
|
51
|
-
|
|
21
|
+
names = normalize_names(argv)
|
|
22
|
+
results = inventory_query.get_hosts(names: names)
|
|
23
|
+
fmt.dump(results, output_format)
|
|
52
24
|
end
|
|
53
25
|
end
|
|
54
26
|
end
|
|
@@ -1,38 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'thor'
|
|
2
4
|
require 'json'
|
|
3
5
|
|
|
4
|
-
require_relative '
|
|
5
|
-
require_relative '../
|
|
6
|
+
require_relative '../inventory_context'
|
|
7
|
+
require_relative '../operations/query_inventory'
|
|
6
8
|
|
|
7
9
|
module Moose
|
|
8
10
|
module Inventory
|
|
9
11
|
module Cli
|
|
10
|
-
##
|
|
11
12
|
# Implementation of the "host list" method of the CLI
|
|
12
13
|
class Host
|
|
13
14
|
desc 'list', 'List the contents of the inventory by host'
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
option :group, type: :string, desc: 'Only include hosts in all comma-separated groups'
|
|
16
|
+
option :tag, type: :string, desc: 'Only include hosts with all comma-separated tags'
|
|
17
|
+
option :var, type: :string, desc: 'Only include hosts with comma-separated key=value variables'
|
|
18
|
+
def list
|
|
19
|
+
fmt.dump(inventory_query.list_hosts(filters: host_list_filters), output_format)
|
|
20
|
+
end
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def host_list_filters
|
|
25
|
+
{
|
|
26
|
+
groups: csv_option_names(options[:group]),
|
|
27
|
+
tags: csv_option_names(options[:tag]),
|
|
28
|
+
variables: variable_filter_options(options[:var])
|
|
29
|
+
}
|
|
30
|
+
end
|
|
25
31
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
def variable_filter_options(value)
|
|
33
|
+
csv_option_names(value).to_h do |entry|
|
|
34
|
+
key, variable_value = entry.split('=', 2)
|
|
35
|
+
abort("ERROR: Invalid variable filter '#{entry}'. Expected key=value.") if variable_value.nil?
|
|
30
36
|
|
|
31
|
-
|
|
32
|
-
results[host[:name].to_sym][:hostvars] = hostvars
|
|
33
|
-
end
|
|
37
|
+
[key, variable_value]
|
|
34
38
|
end
|
|
35
|
-
fmt.dump(results)
|
|
36
39
|
end
|
|
37
40
|
end
|
|
38
41
|
end
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'thor'
|
|
2
4
|
require 'json'
|
|
3
5
|
|
|
4
|
-
require_relative '
|
|
5
|
-
require_relative '../
|
|
6
|
+
require_relative '../inventory_context'
|
|
7
|
+
require_relative '../operations/query_inventory'
|
|
6
8
|
|
|
7
9
|
module Moose
|
|
8
10
|
module Inventory
|
|
@@ -13,68 +15,13 @@ module Moose
|
|
|
13
15
|
#==========================
|
|
14
16
|
desc 'listvar', 'List all variables associated with the host'
|
|
15
17
|
def listvars(*argv)
|
|
16
|
-
|
|
17
|
-
confopts = Moose::Inventory::Config._confopts
|
|
18
|
-
|
|
19
|
-
# sanity
|
|
20
|
-
if confopts[:ansible] == true
|
|
21
|
-
if argv.length != 1
|
|
22
|
-
abort('ERROR: Wrong number of arguments for Ansible mode, '\
|
|
23
|
-
"#{args.length} for 1.")
|
|
24
|
-
end
|
|
25
|
-
else
|
|
26
|
-
if argv.empty?
|
|
27
|
-
abort('ERROR: Wrong number of arguments, '\
|
|
28
|
-
"#{args.length} for 1 or more.")
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Convenience
|
|
33
|
-
db = Moose::Inventory::DB
|
|
34
|
-
fmt = Moose::Inventory::Cli::Formatter
|
|
35
|
-
|
|
36
|
-
# Arguments
|
|
37
|
-
names = argv.uniq.map(&:downcase)
|
|
38
|
-
|
|
39
|
-
# process
|
|
40
|
-
results = {}
|
|
41
|
-
|
|
42
|
-
if confopts[:ansible] == true
|
|
43
|
-
# This is the implementation per Ansible specs
|
|
44
|
-
name = names.first
|
|
45
|
-
host = db.models[:host].find(name: name)
|
|
46
|
-
if host.nil?
|
|
47
|
-
fmt.warn "The host #{name} does not exist.\n"
|
|
48
|
-
else
|
|
49
|
-
host.hostvars_dataset.each do |hv|
|
|
50
|
-
results[hv[:name].to_sym] = hv[:value]
|
|
51
|
-
end
|
|
52
|
-
end
|
|
18
|
+
validate_listvars_args(argv)
|
|
53
19
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
results['_meta'.to_sym]['hostvars'.to_sym] = {}
|
|
58
|
-
db.models[:host].each do |host|
|
|
59
|
-
results['_meta'.to_sym]['hostvars'.to_sym][host.name.to_sym] = {}
|
|
60
|
-
host.hostvars_dataset.each do |hv|
|
|
61
|
-
results['_meta'.to_sym]['hostvars'.to_sym][host.name.to_sym][hv[:name].to_sym] = hv[:value]
|
|
62
|
-
end
|
|
63
|
-
end
|
|
20
|
+
names = normalize_names(argv)
|
|
21
|
+
results = inventory_query.list_host_vars(names: names, ansible: ansible_mode?)
|
|
22
|
+
warn_if_missing_ansible_listvars_entity(:host, names.first)
|
|
64
23
|
|
|
65
|
-
|
|
66
|
-
# This our more flexible implementation, which is not compatible
|
|
67
|
-
# with the Ansible specs
|
|
68
|
-
names.each do |name|
|
|
69
|
-
host = db.models[:host].find(name: name)
|
|
70
|
-
next if host.nil?
|
|
71
|
-
results[name.to_sym] = {}
|
|
72
|
-
host.hostvars_dataset.each do |hv|
|
|
73
|
-
results[name.to_sym][hv[:name].to_sym] = hv[:value]
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
fmt.dump(results)
|
|
24
|
+
fmt.dump(results, output_format)
|
|
78
25
|
end
|
|
79
26
|
end
|
|
80
27
|
end
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'thor'
|
|
2
4
|
require 'json'
|
|
3
5
|
|
|
4
|
-
require_relative '
|
|
5
|
-
require_relative '../
|
|
6
|
+
require_relative '../inventory_context'
|
|
7
|
+
require_relative '../operations/remove_hosts'
|
|
6
8
|
|
|
7
9
|
module Moose
|
|
8
10
|
module Inventory
|
|
@@ -13,49 +15,65 @@ module Moose
|
|
|
13
15
|
#==========================
|
|
14
16
|
desc 'rm HOSTNAME_1 [HOSTNAME_2 ...]',
|
|
15
17
|
'Remove hosts HOSTNAME_n from the inventory'
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
option :dry_run, type: :boolean
|
|
19
|
+
option :yes, type: :boolean, desc: 'Confirm destructive removal without prompting'
|
|
20
|
+
option :plan_format, type: :string, desc: 'Emit dry-run plan events as yaml|json|pjson'
|
|
21
|
+
def rm(*argv)
|
|
22
|
+
abort_if_missing_args(argv, 1, '1 or more')
|
|
23
|
+
validate_machine_plan_request!
|
|
24
|
+
|
|
25
|
+
names = normalize_names(argv)
|
|
26
|
+
confirm_destructive_action!("host rm #{names.join(',')}")
|
|
27
|
+
result = remove_hosts_operation.call(names: names, dry_run: options[:dry_run])
|
|
28
|
+
return if machine_plan_output_rendered?(result, command: 'host rm')
|
|
29
|
+
|
|
30
|
+
record_audit({ command: 'host rm', action: 'remove', entity_type: 'host',
|
|
31
|
+
entity_names: names }, result: result, dry_run: options[:dry_run])
|
|
32
|
+
render_remove_hosts_events(result.events)
|
|
33
|
+
print_warning_summary(result)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def remove_hosts_operation
|
|
39
|
+
build_operation(Moose::Inventory::Operations::RemoveHosts)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def render_remove_hosts_events(events)
|
|
43
|
+
events.each { |event| render_remove_hosts_event(event) }
|
|
44
|
+
end
|
|
23
45
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
end
|
|
50
|
-
fmt.puts 2, '- All OK'
|
|
51
|
-
end
|
|
52
|
-
end # Transaction end
|
|
53
|
-
if warn_count == 0
|
|
54
|
-
puts 'Succeeded.'
|
|
55
|
-
else
|
|
56
|
-
puts 'Succeeded, with warnings.'
|
|
46
|
+
def render_remove_hosts_event(event)
|
|
47
|
+
payload = event.payload
|
|
48
|
+
|
|
49
|
+
return render_host_rm_progress(event.type, payload) if host_rm_progress_event?(event.type)
|
|
50
|
+
return render_host_rm_warning(payload) if event.type == :host_missing
|
|
51
|
+
return fmt.puts(payload[:indent], '- No such host, skipping.') if event.type == :missing_skipping
|
|
52
|
+
return fmt.puts(payload[:indent], '- OK') if event.type == :ok
|
|
53
|
+
|
|
54
|
+
return fmt.puts 2, '- All OK' if event.type == :host_complete
|
|
55
|
+
|
|
56
|
+
puts 'Dry run complete. No changes applied.' if event.type == :dry_run_summary
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def host_rm_progress_event?(type)
|
|
60
|
+
%i[host_started retrieving_host destroying_host].include?(type)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def render_host_rm_progress(type, payload)
|
|
64
|
+
case type
|
|
65
|
+
when :host_started
|
|
66
|
+
puts "Remove host '#{payload[:name]}':"
|
|
67
|
+
when :retrieving_host
|
|
68
|
+
fmt.puts 2, "- Retrieve host '#{payload[:name]}'..."
|
|
69
|
+
when :destroying_host
|
|
70
|
+
fmt.puts 2, "- Destroy host '#{payload[:name]}'..."
|
|
57
71
|
end
|
|
58
72
|
end
|
|
73
|
+
|
|
74
|
+
def render_host_rm_warning(payload)
|
|
75
|
+
fmt.warn "Host '#{payload[:name]}' does not exist, skipping.\n"
|
|
76
|
+
end
|
|
59
77
|
end
|
|
60
78
|
end
|
|
61
79
|
end
|
|
@@ -17,65 +17,46 @@ module Moose
|
|
|
17
17
|
#==========================
|
|
18
18
|
desc 'rmgroup HOSTNAME GROUPNAME [GROUPNAME ...]',
|
|
19
19
|
'dissociation the host from a group'
|
|
20
|
+
option :dry_run, type: :boolean
|
|
21
|
+
option :yes, type: :boolean, desc: 'Confirm destructive dissociation without prompting'
|
|
22
|
+
option :plan_format, type: :string, desc: 'Emit dry-run plan events as yaml|json|pjson'
|
|
20
23
|
def rmgroup(*args)
|
|
21
24
|
abort_if_missing_args(args, 2, '2 or more')
|
|
25
|
+
validate_machine_plan_request!
|
|
22
26
|
|
|
23
27
|
name = args[0].downcase
|
|
24
28
|
groups = normalize_names(args.slice(1, args.length - 1))
|
|
25
29
|
|
|
26
30
|
abort_if_automatic_group(groups)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
host
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
fmt.puts 2, '- All OK'
|
|
31
|
+
confirm_destructive_action!("host rmgroup #{name} #{groups.join(',')}")
|
|
32
|
+
|
|
33
|
+
result = remove_groups_from_host(name, groups)
|
|
34
|
+
unless machine_plan_output_rendered?(
|
|
35
|
+
result, command: 'host rmgroup'
|
|
36
|
+
)
|
|
37
|
+
record_audit({ command: 'host rmgroup', action: 'dissociate', entity_type: 'host',
|
|
38
|
+
entity_names: name }, result: result, dry_run: options[:dry_run])
|
|
39
|
+
print_warning_summary(result, success_message: 'Succeeded',
|
|
40
|
+
warning_message: 'Succeeded')
|
|
38
41
|
end
|
|
39
|
-
puts 'Succeeded'
|
|
40
42
|
end
|
|
41
43
|
|
|
42
44
|
private
|
|
43
45
|
|
|
44
|
-
def
|
|
45
|
-
|
|
46
|
-
host
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def render_host_rmgroup_events(events)
|
|
54
|
-
events.each { |event| render_host_rmgroup_event(event) }
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def render_host_rmgroup_event(event)
|
|
58
|
-
payload = event.payload
|
|
59
|
-
|
|
60
|
-
return render_host_rmgroup_warning(payload) if event.type == :host_group_association_missing
|
|
61
|
-
return render_host_rmgroup_missing(payload) if event.type == :missing_skipping
|
|
62
|
-
|
|
63
|
-
case event.type
|
|
64
|
-
when :removing_host_group_association
|
|
65
|
-
fmt.puts 2, "- Remove association {host:#{payload[:host]} <-> group:#{payload[:group]}}..."
|
|
66
|
-
when :adding_automatic_group
|
|
67
|
-
fmt.puts 2, "- Add automatic association {host:#{payload[:host]} <-> group:ungrouped}..."
|
|
68
|
-
when :ok
|
|
69
|
-
fmt.puts payload[:indent], '- OK'
|
|
46
|
+
def remove_groups_from_host(name, groups)
|
|
47
|
+
operation = build_operation(Moose::Inventory::Operations::RemoveAssociations)
|
|
48
|
+
run_host_relation_transaction(heading: "Dissociate host '#{name}' from groups '#{groups.join(',')}':") do
|
|
49
|
+
host = fetch_existing_host_or_raise(name)
|
|
50
|
+
result = operation.host_from_groups(host: host, host_name: name, group_names: groups,
|
|
51
|
+
dry_run: options[:dry_run])
|
|
52
|
+
render_host_rmgroup_events(result.events) unless machine_plan_output_requested?
|
|
53
|
+
result
|
|
70
54
|
end
|
|
71
55
|
end
|
|
72
56
|
|
|
73
|
-
def
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def render_host_rmgroup_missing(payload)
|
|
78
|
-
fmt.puts payload[:indent], "- Doesn't exist, skipping."
|
|
57
|
+
def render_host_rmgroup_events(events)
|
|
58
|
+
emitter = host_group_association_removal_emitter(perspective: :host)
|
|
59
|
+
events.each { |event| emitter.call(event) }
|
|
79
60
|
end
|
|
80
61
|
end
|
|
81
62
|
end
|