moose-inventory 1.0.8 → 2.0
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 +5 -5
- data/.github/workflows/ci.yml +49 -0
- data/.github/workflows/release.yml +58 -0
- data/.gitignore +1 -1
- data/.gitleaks.toml +9 -0
- data/.rubocop.yml +19 -784
- data/BACKLOG.md +290 -0
- data/Gemfile.lock +95 -0
- data/README.md +38 -9
- data/Rakefile +1 -1
- data/bin/moose-inventory +1 -1
- data/docs/release/publishing.md +109 -0
- data/docs/release/release-readiness.md +55 -0
- data/docs/security-audit-2026-05-21.md +71 -0
- data/docs/security-audit-2026-05-26-rerun.md +75 -0
- data/docs/security-audit-2026-05-26.md +63 -0
- data/lib/moose_inventory/cli/formatter.rb +16 -17
- data/lib/moose_inventory/cli/group.rb +4 -1
- data/lib/moose_inventory/cli/group_add.rb +89 -75
- data/lib/moose_inventory/cli/group_addchild.rb +84 -71
- data/lib/moose_inventory/cli/group_addhost.rb +78 -69
- data/lib/moose_inventory/cli/group_addvar.rb +37 -37
- data/lib/moose_inventory/cli/group_get.rb +23 -26
- data/lib/moose_inventory/cli/group_list.rb +12 -15
- data/lib/moose_inventory/cli/group_listvars.rb +12 -14
- data/lib/moose_inventory/cli/group_rm.rb +104 -76
- data/lib/moose_inventory/cli/group_rmchild.rb +99 -54
- data/lib/moose_inventory/cli/group_rmhost.rb +64 -60
- data/lib/moose_inventory/cli/group_rmvar.rb +5 -5
- data/lib/moose_inventory/cli/helpers.rb +76 -0
- data/lib/moose_inventory/cli/host.rb +4 -1
- data/lib/moose_inventory/cli/host_add.rb +51 -66
- data/lib/moose_inventory/cli/host_addgroup.rb +77 -68
- data/lib/moose_inventory/cli/host_addvar.rb +6 -6
- data/lib/moose_inventory/cli/host_get.rb +15 -18
- data/lib/moose_inventory/cli/host_list.rb +3 -3
- data/lib/moose_inventory/cli/host_listvars.rb +21 -23
- data/lib/moose_inventory/cli/host_rm.rb +9 -9
- data/lib/moose_inventory/cli/host_rmgroup.rb +63 -60
- data/lib/moose_inventory/cli/host_rmvar.rb +3 -3
- data/lib/moose_inventory/config/config.rb +43 -40
- data/lib/moose_inventory/db/db.rb +92 -52
- data/lib/moose_inventory/db/models.rb +11 -12
- data/lib/moose_inventory/inventory_context.rb +50 -0
- data/lib/moose_inventory/operations/add_associations.rb +127 -0
- data/lib/moose_inventory/operations/add_groups.rb +115 -0
- data/lib/moose_inventory/operations/add_hosts.rb +110 -0
- data/lib/moose_inventory/operations/group_child_relations.rb +118 -0
- data/lib/moose_inventory/operations/group_cleanup.rb +55 -0
- data/lib/moose_inventory/operations/remove_associations.rb +101 -0
- data/lib/moose_inventory/operations/remove_groups.rb +79 -0
- data/lib/moose_inventory/version.rb +1 -1
- data/moose-inventory.gemspec +38 -20
- data/scripts/check.sh +10 -0
- data/scripts/ci/check_permissions.sh +35 -0
- data/scripts/ci/check_rubocop.sh +28 -0
- data/scripts/ci/check_secrets.sh +26 -0
- data/scripts/ci/check_security.sh +68 -0
- data/scripts/ci/install_security_tools.sh +47 -0
- data/scripts/ci/package_sanity.sh +46 -0
- data/scripts/files.rb +1 -4
- data/scripts/install_dependencies.sh +19 -0
- data/scripts/reports.sh +2 -2
- data/spec/lib/moose_inventory/cli/cli_spec.rb +13 -14
- data/spec/lib/moose_inventory/cli/group_add_spec.rb +118 -119
- data/spec/lib/moose_inventory/cli/group_addchild_spec.rb +49 -51
- data/spec/lib/moose_inventory/cli/group_addhost_spec.rb +80 -83
- data/spec/lib/moose_inventory/cli/group_addvar_spec.rb +91 -91
- data/spec/lib/moose_inventory/cli/group_get_spec.rb +22 -23
- data/spec/lib/moose_inventory/cli/group_list_spec.rb +19 -20
- data/spec/lib/moose_inventory/cli/group_listvar_spec.rb +35 -36
- data/spec/lib/moose_inventory/cli/group_rm_spec.rb +115 -78
- data/spec/lib/moose_inventory/cli/group_rmchild_spec.rb +86 -45
- data/spec/lib/moose_inventory/cli/group_rmhost_spec.rb +43 -46
- data/spec/lib/moose_inventory/cli/group_rmvar_spec.rb +131 -131
- data/spec/lib/moose_inventory/cli/group_spec.rb +9 -9
- data/spec/lib/moose_inventory/cli/host_add_spec.rb +103 -43
- data/spec/lib/moose_inventory/cli/host_addgroup_spec.rb +78 -80
- data/spec/lib/moose_inventory/cli/host_addvar_spec.rb +122 -122
- data/spec/lib/moose_inventory/cli/host_get_spec.rb +16 -16
- data/spec/lib/moose_inventory/cli/host_list_spec.rb +8 -8
- data/spec/lib/moose_inventory/cli/host_listvar_spec.rb +50 -52
- data/spec/lib/moose_inventory/cli/host_rm_spec.rb +12 -12
- data/spec/lib/moose_inventory/cli/host_rmgroup_spec.rb +48 -51
- data/spec/lib/moose_inventory/cli/host_rmvar_spec.rb +136 -136
- data/spec/lib/moose_inventory/config/config_spec.rb +16 -3
- data/spec/lib/moose_inventory/db/db_spec.rb +386 -2
- data/spec/lib/moose_inventory/db/models_spec.rb +10 -11
- data/spec/lib/moose_inventory/operations/add_associations_spec.rb +77 -0
- data/spec/lib/moose_inventory/operations/add_groups_spec.rb +65 -0
- data/spec/lib/moose_inventory/operations/add_hosts_spec.rb +69 -0
- data/spec/lib/moose_inventory/operations/group_child_relations_spec.rb +76 -0
- data/spec/lib/moose_inventory/operations/remove_associations_spec.rb +78 -0
- data/spec/lib/moose_inventory/operations/remove_groups_spec.rb +57 -0
- data/spec/shared/shared_config_setup.rb +2 -2
- data/spec/spec_helper.rb +7 -8
- metadata +157 -105
- data/.coveralls.yml +0 -0
- data/Guardfile +0 -38
- data/config/dotfiles/coveralls.yml +0 -0
- data/config/dotfiles/gitignore +0 -20
- data/config/dotfiles/rubocop.yml +0 -793
- data/scripts/guard_quality.sh +0 -3
- data/scripts/guard_test.sh +0 -2
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'thor'
|
|
2
|
-
require_relative '
|
|
4
|
+
require_relative 'formatter'
|
|
5
|
+
require_relative '../inventory_context'
|
|
6
|
+
require_relative '../operations/remove_groups'
|
|
3
7
|
|
|
4
8
|
module Moose
|
|
5
9
|
module Inventory
|
|
@@ -7,85 +11,109 @@ module Moose
|
|
|
7
11
|
##
|
|
8
12
|
# Implementation of "group rm" methods of the CLI
|
|
9
13
|
class Group
|
|
10
|
-
|
|
11
14
|
#==========================
|
|
15
|
+
option :recursive,
|
|
16
|
+
type: :boolean,
|
|
17
|
+
default: false,
|
|
18
|
+
desc: 'Also delete child groups that become orphaned'
|
|
12
19
|
desc 'rm NAME',
|
|
13
20
|
'Remove a group NAME from the inventory'
|
|
14
|
-
def rm(*argv)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
# sanity
|
|
30
|
-
if names.include?('ungrouped')
|
|
31
|
-
abort("Cannot manually manipulate the automatic group 'ungrouped'\n")
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Transaction
|
|
35
|
-
warn_count = 0
|
|
36
|
-
db.transaction do # Transaction start
|
|
37
|
-
names.each do |name|
|
|
38
|
-
puts "Remove group '#{name}':"
|
|
39
|
-
fmt.puts 2, "- Retrieve group '#{name}'..."
|
|
40
|
-
group = db.models[:group].find(name: name)
|
|
41
|
-
if group.nil?
|
|
42
|
-
warn_count += 1
|
|
43
|
-
fmt.warn "Group '#{ name }' does not exist, skipping.\n"
|
|
44
|
-
fmt.puts 4, "- No such group, skipping."
|
|
45
|
-
end
|
|
46
|
-
fmt.puts 4, "- OK"
|
|
47
|
-
unless group.nil?
|
|
48
|
-
# Dissociate from any parent groups
|
|
49
|
-
pgroups_ds = group.parents_dataset
|
|
50
|
-
pgroups_ds.each do |parent|
|
|
51
|
-
fmt.puts 2, "- Remove association {group:#{name} <-> group:#{parent.name}}..."
|
|
52
|
-
parent.remove_child(group)
|
|
53
|
-
fmt.puts 4, '- OK'
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# Dissociate from any child groups
|
|
57
|
-
groups_ds = group.children_dataset
|
|
58
|
-
groups_ds.each do |child|
|
|
59
|
-
fmt.puts 2, "- Remove association {group:#{name} <-> group:#{child.name}}..."
|
|
60
|
-
group.remove_child(child)
|
|
61
|
-
# TODO: Should we propagate the delete to orphaned children?
|
|
62
|
-
fmt.puts 4, '- OK'
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Handle automatic group for any associated hosts
|
|
66
|
-
hosts_ds = group.hosts_dataset
|
|
67
|
-
hosts_ds.each do |host|
|
|
68
|
-
host_groups_ds = host.groups_dataset
|
|
69
|
-
if host_groups_ds.count == 1 # We're the only group
|
|
70
|
-
fmt.puts 2, "- Adding automatic association {group:ungrouped <-> host:#{host[:name]}}..."
|
|
71
|
-
ungrouped = db.models[:group].find_or_create(name: 'ungrouped')
|
|
72
|
-
host.add_group(ungrouped)
|
|
73
|
-
fmt.puts 4, "- OK"
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
# Remove the group
|
|
77
|
-
fmt.puts 2, "- Destroy group '#{name}'..."
|
|
78
|
-
group.remove_all_hosts
|
|
79
|
-
group.destroy
|
|
80
|
-
fmt.puts 4, "- OK"
|
|
81
|
-
end
|
|
82
|
-
fmt.puts 2, "- All OK"
|
|
83
|
-
end
|
|
84
|
-
end # Transaction end
|
|
85
|
-
if warn_count == 0
|
|
86
|
-
puts "Succeeded."
|
|
21
|
+
def rm(*argv)
|
|
22
|
+
abort_if_missing_args(argv, 1, '1 or more')
|
|
23
|
+
|
|
24
|
+
names = normalize_names(argv)
|
|
25
|
+
|
|
26
|
+
abort_if_automatic_group(
|
|
27
|
+
names,
|
|
28
|
+
"Cannot manually manipulate the automatic group 'ungrouped'\n"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
result = remove_groups(names)
|
|
32
|
+
|
|
33
|
+
if result.warning_count.zero?
|
|
34
|
+
puts 'Succeeded.'
|
|
87
35
|
else
|
|
88
|
-
puts
|
|
36
|
+
puts 'Succeeded, with warnings.'
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def remove_groups(names)
|
|
43
|
+
context = Moose::Inventory::InventoryContext.new(db: db)
|
|
44
|
+
operation = Moose::Inventory::Operations::RemoveGroups.new(context: context)
|
|
45
|
+
|
|
46
|
+
db.transaction do
|
|
47
|
+
result = operation.call(names: names, recursive: options[:recursive])
|
|
48
|
+
render_group_rm_events(result.events)
|
|
49
|
+
return result
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def render_group_rm_events(events)
|
|
54
|
+
events.each { |event| render_group_rm_event(event) }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def render_group_rm_event(event)
|
|
58
|
+
payload = event.payload
|
|
59
|
+
|
|
60
|
+
render_group_rm_warning(payload) if event.type == :group_missing
|
|
61
|
+
return render_group_rm_progress(event.type, payload) if group_rm_progress?(event.type)
|
|
62
|
+
|
|
63
|
+
render_group_rm_status(event.type, payload)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def group_rm_progress?(type)
|
|
67
|
+
%i[group_started retrieving_group removing_parent_association removing_child_association].include?(type)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def render_group_rm_warning(payload)
|
|
71
|
+
fmt.warn "Group '#{payload[:name]}' does not exist, skipping.\n"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def render_group_rm_progress(type, payload)
|
|
75
|
+
case type
|
|
76
|
+
when :group_started
|
|
77
|
+
puts "Remove group '#{payload[:name]}':"
|
|
78
|
+
when :retrieving_group
|
|
79
|
+
fmt.puts 2, "- Retrieve group '#{payload[:name]}'..."
|
|
80
|
+
when :removing_parent_association, :removing_child_association
|
|
81
|
+
fmt.puts 2, "- Remove association {group:#{payload[:group]} <-> group:#{payload[:related_group]}}..."
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def render_group_rm_status(type, payload)
|
|
86
|
+
return render_group_rm_secondary_status(type, payload) if group_rm_secondary_status?(type)
|
|
87
|
+
|
|
88
|
+
case type
|
|
89
|
+
when :group_missing
|
|
90
|
+
fmt.puts 4, '- No such group, skipping.'
|
|
91
|
+
when :destroying_group
|
|
92
|
+
fmt.puts payload[:indent], "- Destroy group '#{payload[:name]}'..."
|
|
93
|
+
when :group_complete
|
|
94
|
+
fmt.puts 2, '- All OK'
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def group_rm_secondary_status?(type)
|
|
99
|
+
%i[
|
|
100
|
+
recursively_delete_orphaned_group
|
|
101
|
+
removing_recursive_child_association
|
|
102
|
+
adding_automatic_group_to_host
|
|
103
|
+
ok
|
|
104
|
+
].include?(type)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def render_group_rm_secondary_status(type, payload)
|
|
108
|
+
case type
|
|
109
|
+
when :recursively_delete_orphaned_group
|
|
110
|
+
fmt.puts 2, "- Recursively delete orphaned group '#{payload[:name]}'..."
|
|
111
|
+
when :removing_recursive_child_association
|
|
112
|
+
fmt.puts 4, "- Remove association {group:#{payload[:parent]} <-> group:#{payload[:child]}}..."
|
|
113
|
+
when :adding_automatic_group_to_host
|
|
114
|
+
fmt.puts payload[:indent], "- Adding automatic association {group:ungrouped <-> host:#{payload[:host]}}..."
|
|
115
|
+
when :ok
|
|
116
|
+
fmt.puts payload[:indent], '- OK'
|
|
89
117
|
end
|
|
90
118
|
end
|
|
91
119
|
end
|
|
@@ -1,81 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'thor'
|
|
2
4
|
|
|
3
|
-
require_relative '
|
|
5
|
+
require_relative 'formatter'
|
|
6
|
+
require_relative '../inventory_context'
|
|
7
|
+
require_relative '../operations/group_child_relations'
|
|
4
8
|
|
|
5
9
|
module Moose
|
|
6
10
|
module Inventory
|
|
7
11
|
module Cli
|
|
8
12
|
##
|
|
9
13
|
# Implemention of the "group rmchild" methods of the CLI
|
|
10
|
-
class Group
|
|
14
|
+
class Group
|
|
11
15
|
#==========================
|
|
16
|
+
option :delete_orphans,
|
|
17
|
+
type: :boolean,
|
|
18
|
+
default: false,
|
|
19
|
+
desc: 'Delete child groups that become orphaned'
|
|
12
20
|
desc 'rmchild PARENTGROUP CHILDGROUP_1 [CHILDGROUP_2 ... ]',
|
|
13
|
-
|
|
21
|
+
'Dissociate one or more child-groups CHILDGROUP_n from PARENTGROUP'
|
|
14
22
|
def rmchild(*argv)
|
|
23
|
+
abort_if_missing_args(argv, 2, '2 or more')
|
|
15
24
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
abort("ERROR: Wrong number of arguments, #{args.length} "\
|
|
19
|
-
"for 2 or more.")
|
|
20
|
-
end
|
|
25
|
+
pname = argv[0].downcase
|
|
26
|
+
cnames = normalize_names(argv.slice(1, argv.length - 1))
|
|
21
27
|
|
|
22
|
-
|
|
23
|
-
pname = args[0].downcase
|
|
24
|
-
cnames = args.slice(1, args.length - 1).uniq.map(&:downcase)
|
|
28
|
+
abort_if_automatic_group([pname] + cnames)
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
result = remove_children_from_group(pname, cnames)
|
|
31
|
+
|
|
32
|
+
if result.warning_count.zero?
|
|
33
|
+
puts 'Succeeded.'
|
|
34
|
+
else
|
|
35
|
+
puts 'Succeeded, with warnings.'
|
|
29
36
|
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
30
40
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
41
|
+
def remove_children_from_group(parent_name, child_names)
|
|
42
|
+
context = Moose::Inventory::InventoryContext.new(db: db)
|
|
43
|
+
operation = Moose::Inventory::Operations::GroupChildRelations.new(context: context)
|
|
34
44
|
|
|
35
|
-
# Transaction
|
|
36
|
-
warn_count = 0
|
|
37
45
|
begin
|
|
38
|
-
db.transaction do
|
|
39
|
-
puts "Dissociate parent group '#{
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
# Dissociate parent group from the child groups
|
|
49
|
-
groups_ds = pgroup.children_dataset
|
|
50
|
-
cnames.each do |cname|
|
|
51
|
-
fmt.puts 2, "- remove association {group:#{pname} <-> group:#{cname}}..."
|
|
52
|
-
|
|
53
|
-
# Check against existing associations
|
|
54
|
-
if groups_ds[name: cname].nil?
|
|
55
|
-
warn_count += 1
|
|
56
|
-
fmt.warn "Association {group:#{pname} <-> group:#{cname}}"\
|
|
57
|
-
" does not exist, skipping.\n"
|
|
58
|
-
fmt.puts 4, "- doesn't exist, skipping."
|
|
59
|
-
fmt.puts 4, '- OK'
|
|
60
|
-
next
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# remove association
|
|
64
|
-
cgroup = db.models[:group].find(name: cname)
|
|
65
|
-
pgroup.remove_child(cgroup)
|
|
66
|
-
fmt.puts 4, '- OK'
|
|
67
|
-
end
|
|
46
|
+
db.transaction do
|
|
47
|
+
puts "Dissociate parent group '#{parent_name}' from child group(s) '#{child_names.join(',')}':"
|
|
48
|
+
parent_group = fetch_existing_group_for_rmchild(context, parent_name)
|
|
49
|
+
result = operation.remove_children(
|
|
50
|
+
parent_group: parent_group,
|
|
51
|
+
parent_name: parent_name,
|
|
52
|
+
child_names: child_names,
|
|
53
|
+
delete_orphans: options[:delete_orphans]
|
|
54
|
+
)
|
|
55
|
+
render_rmchild_events(result.events)
|
|
68
56
|
fmt.puts 2, '- all OK'
|
|
69
|
-
|
|
57
|
+
return result
|
|
58
|
+
end
|
|
70
59
|
rescue db.exceptions[:moose] => e
|
|
71
60
|
abort("ERROR: #{e}")
|
|
72
61
|
end
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def fetch_existing_group_for_rmchild(context, name)
|
|
65
|
+
fmt.puts 2, "- retrieve group '#{name}'..."
|
|
66
|
+
group = context.find_group(name)
|
|
67
|
+
abort("ERROR: The group '#{name}' does not exist.") if group.nil?
|
|
68
|
+
|
|
69
|
+
fmt.puts 4, '- OK'
|
|
70
|
+
group
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def render_rmchild_events(events)
|
|
74
|
+
events.each { |event| render_rmchild_event(event) }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def render_rmchild_event(event)
|
|
78
|
+
payload = event.payload
|
|
79
|
+
|
|
80
|
+
return render_rmchild_warning(payload) if event.type == :child_association_missing
|
|
81
|
+
return render_rmchild_missing(payload) if event.type == :missing_skipping
|
|
82
|
+
return render_rmchild_progress(event.type, payload) if rmchild_progress_event?(event.type)
|
|
83
|
+
|
|
84
|
+
render_rmchild_status(event.type, payload)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def rmchild_progress_event?(type)
|
|
88
|
+
%i[
|
|
89
|
+
removing_child_association
|
|
90
|
+
recursively_delete_orphaned_group
|
|
91
|
+
removing_recursive_child_association
|
|
92
|
+
].include?(type)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def render_rmchild_progress(type, payload)
|
|
96
|
+
case type
|
|
97
|
+
when :removing_child_association
|
|
98
|
+
fmt.puts 2, "- remove association {group:#{payload[:parent]} <-> group:#{payload[:child]}}..."
|
|
99
|
+
when :recursively_delete_orphaned_group
|
|
100
|
+
fmt.puts 2, "- Recursively delete orphaned group '#{payload[:name]}'..."
|
|
101
|
+
when :removing_recursive_child_association
|
|
102
|
+
fmt.puts 4, "- Remove association {group:#{payload[:parent]} <-> group:#{payload[:child]}}..."
|
|
77
103
|
end
|
|
78
104
|
end
|
|
105
|
+
|
|
106
|
+
def render_rmchild_status(type, payload)
|
|
107
|
+
case type
|
|
108
|
+
when :adding_automatic_group_to_host
|
|
109
|
+
fmt.puts payload[:indent], "- Adding automatic association {group:ungrouped <-> host:#{payload[:host]}}..."
|
|
110
|
+
when :destroying_group
|
|
111
|
+
fmt.puts payload[:indent], "- Destroy group '#{payload[:name]}'..."
|
|
112
|
+
when :ok
|
|
113
|
+
fmt.puts payload[:indent], '- OK'
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def render_rmchild_warning(payload)
|
|
118
|
+
fmt.warn "Association {group:#{payload[:parent]} <-> group:#{payload[:child]}} does not exist, skipping.\n"
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def render_rmchild_missing(payload)
|
|
122
|
+
fmt.puts payload[:indent], "- doesn't exist, skipping."
|
|
123
|
+
end
|
|
79
124
|
end
|
|
80
125
|
end
|
|
81
126
|
end
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'thor'
|
|
2
|
-
require_relative '
|
|
4
|
+
require_relative 'formatter'
|
|
5
|
+
require_relative '../inventory_context'
|
|
6
|
+
require_relative '../operations/remove_associations'
|
|
3
7
|
|
|
4
8
|
module Moose
|
|
5
9
|
module Inventory
|
|
@@ -7,82 +11,82 @@ module Moose
|
|
|
7
11
|
##
|
|
8
12
|
# Implementation of the "group rmhost" method of the CLI
|
|
9
13
|
class Group
|
|
10
|
-
|
|
11
14
|
#==========================
|
|
12
15
|
desc 'rmhost GROUPNAME HOSTNAME_1 [HOSTNAME_2 ...]',
|
|
13
16
|
'Dissociate the hosts HOSTNAME_n from the group NAME'
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
# rubocop:enable Metrics/LineLength
|
|
17
|
-
# Sanity
|
|
18
|
-
if args.length < 2
|
|
19
|
-
abort("ERROR: Wrong number of arguments, #{args.length} for 2 or more.")
|
|
20
|
-
end
|
|
17
|
+
def rmhost(*args)
|
|
18
|
+
abort_if_missing_args(args, 2, '2 or more')
|
|
21
19
|
|
|
22
|
-
# Arguments
|
|
23
20
|
name = args[0].downcase
|
|
24
|
-
hosts = args.slice(1, args.length - 1)
|
|
21
|
+
hosts = normalize_names(args.slice(1, args.length - 1))
|
|
22
|
+
|
|
23
|
+
abort_if_automatic_group([name])
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
result = remove_hosts_from_group(name, hosts)
|
|
26
|
+
|
|
27
|
+
if result.warning_count.zero?
|
|
28
|
+
puts 'Succeeded.'
|
|
29
|
+
else
|
|
30
|
+
puts 'Succeeded, with warnings.'
|
|
29
31
|
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
30
35
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
def remove_hosts_from_group(name, hosts)
|
|
37
|
+
context = Moose::Inventory::InventoryContext.new(db: db)
|
|
38
|
+
operation = Moose::Inventory::Operations::RemoveAssociations.new(context: context)
|
|
34
39
|
|
|
35
|
-
# Transaction
|
|
36
|
-
warn_count = 0
|
|
37
40
|
begin
|
|
38
|
-
db.transaction do
|
|
39
|
-
# Get the target group
|
|
41
|
+
db.transaction do
|
|
40
42
|
puts "Dissociate group '#{name}' from host(s) '#{hosts.join(',')}':"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
abort("ERROR: The group '#{name}' does not exist.")
|
|
45
|
-
end
|
|
46
|
-
fmt.puts 4, '- OK'
|
|
47
|
-
|
|
48
|
-
# dissociate group from the hosts
|
|
49
|
-
ungrouped = db.models[:group].find_or_create(name: 'ungrouped')
|
|
50
|
-
hosts_ds = group.hosts_dataset
|
|
51
|
-
hosts.each do |h| # rubocop:disable Style/Next
|
|
52
|
-
fmt.puts 2, "- remove association {group:#{name} <-> host:#{ h }}..."
|
|
53
|
-
|
|
54
|
-
# Check against existing associations
|
|
55
|
-
if hosts_ds[name: h].nil?
|
|
56
|
-
warn_count += 1
|
|
57
|
-
fmt.warn "Association {group:#{name} <-> host:#{ h }} doesn't"\
|
|
58
|
-
" exist, skipping.\n"
|
|
59
|
-
fmt.puts 4, '- doesn\'t exist, skipping.'
|
|
60
|
-
fmt.puts 4, '- OK'
|
|
61
|
-
next
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
host = db.models[:host].find(name: h)
|
|
65
|
-
group.remove_host(host) unless host.nil?
|
|
66
|
-
fmt.puts 4,'- OK'
|
|
67
|
-
|
|
68
|
-
# Add the host to the ungrouped group if not in any other group
|
|
69
|
-
if host.groups_dataset.count == 0
|
|
70
|
-
fmt.puts 2, "- add automatic association {group:ungrouped <-> host:#{h}}..."
|
|
71
|
-
host.add_group(ungrouped)
|
|
72
|
-
fmt.puts 4, '- OK'
|
|
73
|
-
end
|
|
74
|
-
end
|
|
43
|
+
group = fetch_existing_group_for_rmhost(context, name)
|
|
44
|
+
result = operation.group_from_hosts(group: group, group_name: name, host_names: hosts)
|
|
45
|
+
render_group_rmhost_events(result.events)
|
|
75
46
|
fmt.puts 2, '- all OK'
|
|
76
|
-
|
|
47
|
+
return result
|
|
48
|
+
end
|
|
77
49
|
rescue db.exceptions[:moose] => e
|
|
78
50
|
abort("ERROR: #{e.message}")
|
|
79
51
|
end
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def fetch_existing_group_for_rmhost(context, name)
|
|
55
|
+
fmt.puts 2, "- retrieve group '#{name}'..."
|
|
56
|
+
group = context.find_group(name)
|
|
57
|
+
abort("ERROR: The group '#{name}' does not exist.") if group.nil?
|
|
58
|
+
|
|
59
|
+
fmt.puts 4, '- OK'
|
|
60
|
+
group
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def render_group_rmhost_events(events)
|
|
64
|
+
events.each { |event| render_group_rmhost_event(event) }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def render_group_rmhost_event(event)
|
|
68
|
+
payload = event.payload
|
|
69
|
+
|
|
70
|
+
return render_group_rmhost_warning(payload) if event.type == :group_host_association_missing
|
|
71
|
+
return render_group_rmhost_missing(payload) if event.type == :missing_skipping
|
|
72
|
+
|
|
73
|
+
case event.type
|
|
74
|
+
when :removing_group_host_association
|
|
75
|
+
fmt.puts 2, "- remove association {group:#{payload[:group]} <-> host:#{payload[:host]}}..."
|
|
76
|
+
when :adding_automatic_group
|
|
77
|
+
fmt.puts 2, "- add automatic association {group:ungrouped <-> host:#{payload[:host]}}..."
|
|
78
|
+
when :ok
|
|
79
|
+
fmt.puts payload[:indent], '- OK'
|
|
84
80
|
end
|
|
85
81
|
end
|
|
82
|
+
|
|
83
|
+
def render_group_rmhost_warning(payload)
|
|
84
|
+
fmt.warn "Association {group:#{payload[:group]} <-> host:#{payload[:host]}} doesn't exist, skipping.\n"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def render_group_rmhost_missing(payload)
|
|
88
|
+
fmt.puts payload[:indent], "- doesn't exist, skipping."
|
|
89
|
+
end
|
|
86
90
|
end
|
|
87
91
|
end
|
|
88
92
|
end
|
|
@@ -15,7 +15,7 @@ module Moose
|
|
|
15
15
|
abort('ERROR: Wrong number of arguments, ' \
|
|
16
16
|
"#{args.length} for 2 or more.")
|
|
17
17
|
end
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
# Convenience
|
|
20
20
|
db = Moose::Inventory::DB
|
|
21
21
|
fmt = Moose::Inventory::Cli::Formatter
|
|
@@ -26,8 +26,8 @@ module Moose
|
|
|
26
26
|
|
|
27
27
|
# Transaction
|
|
28
28
|
db.transaction do # Transaction start
|
|
29
|
-
puts "Remove variable(s) '#{vars.join(
|
|
30
|
-
|
|
29
|
+
puts "Remove variable(s) '#{vars.join(',')}' from group '#{name}':"
|
|
30
|
+
|
|
31
31
|
fmt.puts 2, "- retrieve group '#{name}'..."
|
|
32
32
|
group = db.models[:group].find(name: name)
|
|
33
33
|
if group.nil?
|
|
@@ -35,7 +35,7 @@ module Moose
|
|
|
35
35
|
"The group '#{name}' does not exist."
|
|
36
36
|
end
|
|
37
37
|
fmt.puts 4, '- OK'
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
groupvars_ds = group.groupvars_dataset
|
|
40
40
|
vars.each do |v|
|
|
41
41
|
fmt.puts 2, "- remove variable '#{v}'..."
|
|
@@ -45,7 +45,7 @@ module Moose
|
|
|
45
45
|
"Incorrect format in {#{v}}. " \
|
|
46
46
|
'Expected \'key\' or \'key=value\'.'
|
|
47
47
|
end
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
# Check against existing associations
|
|
50
50
|
groupvar = groupvars_ds[name: vararray[0]]
|
|
51
51
|
unless groupvar.nil?
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Moose
|
|
4
|
+
module Inventory
|
|
5
|
+
module Cli
|
|
6
|
+
##
|
|
7
|
+
# Shared helpers for Thor command classes.
|
|
8
|
+
module Helpers
|
|
9
|
+
AUTOMATIC_GROUP = 'ungrouped'
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def db
|
|
14
|
+
Moose::Inventory::DB
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def fmt
|
|
18
|
+
Moose::Inventory::Cli::Formatter
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def normalize_names(values)
|
|
22
|
+
values.uniq.map(&:downcase)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def csv_option_names(value)
|
|
26
|
+
(value || '').downcase.split(',').uniq
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def abort_if_missing_args(args, minimum, label)
|
|
30
|
+
return unless args.length < minimum
|
|
31
|
+
|
|
32
|
+
abort("ERROR: Wrong number of arguments, #{args.length} for #{label}.")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def abort_if_automatic_group(names, message = nil)
|
|
36
|
+
return unless names.include?(AUTOMATIC_GROUP)
|
|
37
|
+
|
|
38
|
+
abort(message || "ERROR: Cannot manually manipulate the automatic group '#{AUTOMATIC_GROUP}'.")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def association_exists?(dataset, name)
|
|
42
|
+
!dataset.nil? && !dataset[name: name].nil?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def automatic_group
|
|
46
|
+
db.models[:group].find_or_create(name: AUTOMATIC_GROUP)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def remove_automatic_group_from_host(host, indent:, message:)
|
|
50
|
+
ungrouped = host.groups_dataset[name: AUTOMATIC_GROUP]
|
|
51
|
+
return if ungrouped.nil?
|
|
52
|
+
|
|
53
|
+
fmt.puts indent, message
|
|
54
|
+
host.remove_group(ungrouped)
|
|
55
|
+
fmt.puts indent + 2, '- OK'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def add_automatic_group_to_host_if_last_group(host, indent:, message:)
|
|
59
|
+
add_automatic_group_to_host_if_group_count(host, 1, indent: indent, message: message)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def add_automatic_group_to_host_if_no_groups(host, indent:, message:)
|
|
63
|
+
add_automatic_group_to_host_if_group_count(host, 0, indent: indent, message: message)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def add_automatic_group_to_host_if_group_count(host, group_count, indent:, message:)
|
|
67
|
+
return unless host.groups_dataset.count == group_count
|
|
68
|
+
|
|
69
|
+
fmt.puts indent, message
|
|
70
|
+
host.add_group(automatic_group)
|
|
71
|
+
fmt.puts indent + 2, '- OK'
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -2,6 +2,7 @@ require 'thor'
|
|
|
2
2
|
require 'json'
|
|
3
3
|
|
|
4
4
|
require_relative './formatter.rb'
|
|
5
|
+
require_relative './helpers.rb'
|
|
5
6
|
require_relative '../db/exceptions.rb'
|
|
6
7
|
|
|
7
8
|
module Moose
|
|
@@ -9,7 +10,9 @@ module Moose
|
|
|
9
10
|
module Cli
|
|
10
11
|
##
|
|
11
12
|
# Class implementing the "host" methods of the CLI
|
|
12
|
-
class Host < Thor
|
|
13
|
+
class Host < Thor
|
|
14
|
+
include Moose::Inventory::Cli::Helpers
|
|
15
|
+
|
|
13
16
|
require_relative 'host_add'
|
|
14
17
|
require_relative 'host_get'
|
|
15
18
|
require_relative 'host_list'
|