moose-inventory 1.0.9 → 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 +4 -4
- data/.github/workflows/ci.yml +15 -1
- data/.github/workflows/release.yml +58 -0
- data/.gitleaks.toml +9 -0
- data/.rubocop.yml +28 -0
- data/BACKLOG.md +130 -24
- data/Gemfile.lock +36 -1
- data/README.md +26 -6
- data/Rakefile +1 -1
- data/docs/release/publishing.md +44 -48
- data/docs/release/release-readiness.md +14 -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/group.rb +3 -0
- data/lib/moose_inventory/cli/group_add.rb +89 -73
- data/lib/moose_inventory/cli/group_addchild.rb +77 -60
- data/lib/moose_inventory/cli/group_addhost.rb +78 -65
- data/lib/moose_inventory/cli/group_rm.rb +101 -71
- data/lib/moose_inventory/cli/group_rmchild.rb +99 -53
- data/lib/moose_inventory/cli/group_rmhost.rb +64 -56
- data/lib/moose_inventory/cli/helpers.rb +76 -0
- data/lib/moose_inventory/cli/host.rb +3 -0
- data/lib/moose_inventory/cli/host_add.rb +47 -62
- data/lib/moose_inventory/cli/host_addgroup.rb +73 -64
- data/lib/moose_inventory/cli/host_rmgroup.rb +58 -55
- data/lib/moose_inventory/db/db.rb +27 -7
- 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 +3 -0
- data/scripts/check.sh +2 -0
- data/scripts/ci/check_permissions.sh +3 -0
- data/scripts/ci/check_rubocop.sh +28 -0
- data/scripts/ci/check_secrets.sh +26 -0
- data/scripts/ci/check_security.sh +18 -0
- data/scripts/ci/install_security_tools.sh +47 -0
- data/scripts/install_dependencies.sh +2 -0
- data/spec/lib/moose_inventory/cli/group_rm_spec.rb +40 -0
- data/spec/lib/moose_inventory/cli/group_rmchild_spec.rb +45 -0
- data/spec/lib/moose_inventory/db/db_spec.rb +162 -0
- 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
- metadata +90 -1
|
@@ -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/add_associations'
|
|
3
7
|
|
|
4
8
|
module Moose
|
|
5
9
|
module Inventory
|
|
@@ -10,82 +14,91 @@ module Moose
|
|
|
10
14
|
#==========================
|
|
11
15
|
desc 'addhost NAME HOSTNAME',
|
|
12
16
|
'Associate a host HOSTNAME with the group NAME'
|
|
13
|
-
def addhost(*args)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
def addhost(*args)
|
|
18
|
+
abort_if_missing_args(args, 2, '2 or more')
|
|
19
|
+
|
|
20
|
+
name = args[0].downcase
|
|
21
|
+
hosts = normalize_names(args.slice(1, args.length - 1))
|
|
22
|
+
|
|
23
|
+
abort_if_automatic_group([name])
|
|
19
24
|
|
|
20
|
-
|
|
21
|
-
name = args[0].downcase
|
|
22
|
-
hosts = args.slice(1, args.length - 1).uniq.map(&:downcase)
|
|
25
|
+
result = add_hosts_to_group(name, hosts)
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
if result.warning_count.zero?
|
|
28
|
+
puts 'Succeeded.'
|
|
29
|
+
else
|
|
30
|
+
puts 'Succeeded, with warnings.'
|
|
27
31
|
end
|
|
32
|
+
end
|
|
28
33
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def add_hosts_to_group(name, hosts)
|
|
37
|
+
context = Moose::Inventory::InventoryContext.new(db: db)
|
|
38
|
+
operation = Moose::Inventory::Operations::AddAssociations.new(context: context)
|
|
32
39
|
|
|
33
|
-
# Transaction
|
|
34
|
-
warn_count = 0
|
|
35
40
|
begin
|
|
36
|
-
db.transaction do
|
|
41
|
+
db.transaction do
|
|
37
42
|
puts "Associate group '#{name}' with host(s) '#{hosts.join(',')}':"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
abort("ERROR: The group '#{name}' does not exist.") if group.nil?
|
|
42
|
-
fmt.puts 4, '- OK'
|
|
43
|
-
|
|
44
|
-
# Associate group with the hosts
|
|
45
|
-
ungrouped = db.models[:group].find_or_create(name: 'ungrouped')
|
|
46
|
-
hosts_ds = group.hosts_dataset
|
|
47
|
-
hosts.each do |h|
|
|
48
|
-
fmt.puts 2, "- add association {group:#{name} <-> host:#{h}}..."
|
|
49
|
-
|
|
50
|
-
# Check against existing associations
|
|
51
|
-
unless hosts_ds[name: h].nil?
|
|
52
|
-
warn_count += 1
|
|
53
|
-
fmt.warn "Association {group:#{name} <-> host:#{h}} already"\
|
|
54
|
-
" exists, skipping.\n"
|
|
55
|
-
fmt.puts 4, '- already exists, skipping.'
|
|
56
|
-
fmt.puts 4, '- OK'
|
|
57
|
-
next
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Add new association
|
|
61
|
-
host = db.models[:host].find(name: h)
|
|
62
|
-
if host.nil?
|
|
63
|
-
warn_count += 1
|
|
64
|
-
fmt.warn "Host '#{h}' does not exist and will be created.\n"
|
|
65
|
-
fmt.puts 4, '- host does not exist, creating now...'
|
|
66
|
-
host = db.models[:host].create(name: h)
|
|
67
|
-
fmt.puts 6, '- OK'
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
group.add_host(host)
|
|
71
|
-
fmt.puts 4, '- OK'
|
|
72
|
-
|
|
73
|
-
# Remove the host from the ungrouped group, if necessary
|
|
74
|
-
next if host.groups_dataset[name: 'ungrouped'].nil?
|
|
75
|
-
fmt.puts 2, '- remove automatic association '\
|
|
76
|
-
"{group:ungrouped <-> host:#{h}}..."
|
|
77
|
-
host.remove_group(ungrouped)
|
|
78
|
-
fmt.puts 4, '- OK'
|
|
79
|
-
end
|
|
43
|
+
group = fetch_existing_group_for_addhost(context, name)
|
|
44
|
+
result = operation.group_to_hosts(group: group, group_name: name, host_names: hosts)
|
|
45
|
+
render_group_addhost_events(result.events)
|
|
80
46
|
fmt.puts 2, '- all OK'
|
|
81
|
-
|
|
47
|
+
return result
|
|
48
|
+
end
|
|
82
49
|
rescue db.exceptions[:moose] => e
|
|
83
50
|
abort("ERROR: #{e.message}")
|
|
84
51
|
end
|
|
85
|
-
|
|
86
|
-
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def fetch_existing_group_for_addhost(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_addhost_events(events)
|
|
64
|
+
events.each { |event| render_group_addhost_event(event) }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def render_group_addhost_event(event)
|
|
68
|
+
payload = event.payload
|
|
69
|
+
|
|
70
|
+
return render_group_addhost_warning(event.type, payload) if group_addhost_warning?(event.type)
|
|
71
|
+
return render_group_addhost_status(payload) if event.type == :already_exists_skipping
|
|
72
|
+
|
|
73
|
+
render_group_addhost_output(event.type, payload)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def group_addhost_warning?(type)
|
|
77
|
+
%i[group_host_association_exists host_missing_created].include?(type)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def render_group_addhost_warning(type, payload)
|
|
81
|
+
if type == :group_host_association_exists
|
|
82
|
+
fmt.warn "Association {group:#{payload[:group]} <-> host:#{payload[:host]}} already exists, skipping.\n"
|
|
87
83
|
else
|
|
88
|
-
|
|
84
|
+
fmt.warn "Host '#{payload[:name]}' does not exist and will be created.\n"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def render_group_addhost_status(payload)
|
|
89
|
+
fmt.puts payload[:indent], '- already exists, skipping.'
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def render_group_addhost_output(type, payload)
|
|
93
|
+
case type
|
|
94
|
+
when :adding_group_host_association
|
|
95
|
+
fmt.puts 2, "- add association {group:#{payload[:group]} <-> host:#{payload[:host]}}..."
|
|
96
|
+
when :host_creating_now
|
|
97
|
+
fmt.puts 4, '- host does not exist, creating now...'
|
|
98
|
+
when :removing_automatic_group
|
|
99
|
+
fmt.puts 2, "- remove automatic association {group:ungrouped <-> host:#{payload[:host]}}..."
|
|
100
|
+
when :ok
|
|
101
|
+
fmt.puts payload[:indent], '- OK'
|
|
89
102
|
end
|
|
90
103
|
end
|
|
91
104
|
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_groups'
|
|
3
7
|
|
|
4
8
|
module Moose
|
|
5
9
|
module Inventory
|
|
@@ -8,84 +12,110 @@ module Moose
|
|
|
8
12
|
# Implementation of "group rm" methods of the CLI
|
|
9
13
|
class Group
|
|
10
14
|
#==========================
|
|
15
|
+
option :recursive,
|
|
16
|
+
type: :boolean,
|
|
17
|
+
default: false,
|
|
18
|
+
desc: 'Also delete child groups that become orphaned'
|
|
11
19
|
desc 'rm NAME',
|
|
12
20
|
'Remove a group NAME from the inventory'
|
|
13
|
-
def rm(*argv)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
db = Moose::Inventory::DB
|
|
23
|
-
fmt = Moose::Inventory::Cli::Formatter
|
|
24
|
-
|
|
25
|
-
# Arguments
|
|
26
|
-
names = argv.uniq.map(&:downcase)
|
|
27
|
-
|
|
28
|
-
# sanity
|
|
29
|
-
if names.include?('ungrouped')
|
|
30
|
-
abort("Cannot manually manipulate the automatic group 'ungrouped'\n")
|
|
31
|
-
end
|
|
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
|
+
)
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
names.each do |name|
|
|
37
|
-
puts "Remove group '#{name}':"
|
|
38
|
-
fmt.puts 2, "- Retrieve group '#{name}'..."
|
|
39
|
-
group = db.models[:group].find(name: name)
|
|
40
|
-
if group.nil?
|
|
41
|
-
warn_count += 1
|
|
42
|
-
fmt.warn "Group '#{name}' does not exist, skipping.\n"
|
|
43
|
-
fmt.puts 4, '- No such group, skipping.'
|
|
44
|
-
end
|
|
45
|
-
fmt.puts 4, '- OK'
|
|
46
|
-
unless group.nil?
|
|
47
|
-
# Dissociate from any parent groups
|
|
48
|
-
pgroups_ds = group.parents_dataset
|
|
49
|
-
pgroups_ds.each do |parent|
|
|
50
|
-
fmt.puts 2, "- Remove association {group:#{name} <-> group:#{parent.name}}..."
|
|
51
|
-
parent.remove_child(group)
|
|
52
|
-
fmt.puts 4, '- OK'
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Dissociate from any child groups
|
|
56
|
-
groups_ds = group.children_dataset
|
|
57
|
-
groups_ds.each do |child|
|
|
58
|
-
fmt.puts 2, "- Remove association {group:#{name} <-> group:#{child.name}}..."
|
|
59
|
-
group.remove_child(child)
|
|
60
|
-
# TODO: Should we propagate the delete to orphaned children?
|
|
61
|
-
fmt.puts 4, '- OK'
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# Handle automatic group for any associated hosts
|
|
65
|
-
hosts_ds = group.hosts_dataset
|
|
66
|
-
hosts_ds.each do |host|
|
|
67
|
-
host_groups_ds = host.groups_dataset
|
|
68
|
-
next unless host_groups_ds.count == 1 # We're the only group
|
|
69
|
-
fmt.puts 2, "- Adding automatic association {group:ungrouped <-> host:#{host[:name]}}..."
|
|
70
|
-
ungrouped = db.models[:group].find_or_create(name: 'ungrouped')
|
|
71
|
-
host.add_group(ungrouped)
|
|
72
|
-
fmt.puts 4, '- OK'
|
|
73
|
-
end
|
|
74
|
-
# Remove the group
|
|
75
|
-
fmt.puts 2, "- Destroy group '#{name}'..."
|
|
76
|
-
group.remove_all_hosts
|
|
77
|
-
group.destroy
|
|
78
|
-
fmt.puts 4, '- OK'
|
|
79
|
-
end
|
|
80
|
-
fmt.puts 2, '- All OK'
|
|
81
|
-
end
|
|
82
|
-
end # Transaction end
|
|
83
|
-
if warn_count == 0
|
|
31
|
+
result = remove_groups(names)
|
|
32
|
+
|
|
33
|
+
if result.warning_count.zero?
|
|
84
34
|
puts 'Succeeded.'
|
|
85
35
|
else
|
|
86
36
|
puts 'Succeeded, with warnings.'
|
|
87
37
|
end
|
|
88
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'
|
|
117
|
+
end
|
|
118
|
+
end
|
|
89
119
|
end
|
|
90
120
|
end
|
|
91
121
|
end
|
|
@@ -1,6 +1,10 @@
|
|
|
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
|
|
@@ -9,72 +13,114 @@ module Moose
|
|
|
9
13
|
# Implemention of the "group rmchild" methods of the CLI
|
|
10
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
|
-
def rmchild(*
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
def rmchild(*argv)
|
|
23
|
+
abort_if_missing_args(argv, 2, '2 or more')
|
|
24
|
+
|
|
25
|
+
pname = argv[0].downcase
|
|
26
|
+
cnames = normalize_names(argv.slice(1, argv.length - 1))
|
|
27
|
+
|
|
28
|
+
abort_if_automatic_group([pname] + cnames)
|
|
20
29
|
|
|
21
|
-
|
|
22
|
-
pname = args[0].downcase
|
|
23
|
-
cnames = args.slice(1, args.length - 1).uniq.map(&:downcase)
|
|
30
|
+
result = remove_children_from_group(pname, cnames)
|
|
24
31
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
32
|
+
if result.warning_count.zero?
|
|
33
|
+
puts 'Succeeded.'
|
|
34
|
+
else
|
|
35
|
+
puts 'Succeeded, with warnings.'
|
|
28
36
|
end
|
|
37
|
+
end
|
|
29
38
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
private
|
|
40
|
+
|
|
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)
|
|
33
44
|
|
|
34
|
-
# Transaction
|
|
35
|
-
warn_count = 0
|
|
36
45
|
begin
|
|
37
|
-
db.transaction do
|
|
38
|
-
puts "Dissociate parent group '#{
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
# Dissociate parent group from the child groups
|
|
48
|
-
groups_ds = pgroup.children_dataset
|
|
49
|
-
cnames.each do |cname|
|
|
50
|
-
fmt.puts 2, "- remove association {group:#{pname} <-> group:#{cname}}..."
|
|
51
|
-
|
|
52
|
-
# Check against existing associations
|
|
53
|
-
if groups_ds[name: cname].nil?
|
|
54
|
-
warn_count += 1
|
|
55
|
-
fmt.warn "Association {group:#{pname} <-> group:#{cname}}"\
|
|
56
|
-
" does not exist, skipping.\n"
|
|
57
|
-
fmt.puts 4, "- doesn't exist, skipping."
|
|
58
|
-
fmt.puts 4, '- OK'
|
|
59
|
-
next
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# remove association
|
|
63
|
-
cgroup = db.models[:group].find(name: cname)
|
|
64
|
-
pgroup.remove_child(cgroup)
|
|
65
|
-
fmt.puts 4, '- OK'
|
|
66
|
-
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)
|
|
67
56
|
fmt.puts 2, '- all OK'
|
|
68
|
-
|
|
57
|
+
return result
|
|
58
|
+
end
|
|
69
59
|
rescue db.exceptions[:moose] => e
|
|
70
60
|
abort("ERROR: #{e}")
|
|
71
61
|
end
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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]}}..."
|
|
103
|
+
end
|
|
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'
|
|
76
114
|
end
|
|
77
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
|
|
78
124
|
end
|
|
79
125
|
end
|
|
80
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
|
|
@@ -10,75 +14,79 @@ module Moose
|
|
|
10
14
|
#==========================
|
|
11
15
|
desc 'rmhost GROUPNAME HOSTNAME_1 [HOSTNAME_2 ...]',
|
|
12
16
|
'Dissociate the hosts HOSTNAME_n from the group NAME'
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
# rubocop:enable Metrics/LineLength
|
|
16
|
-
# Sanity
|
|
17
|
-
if args.length < 2
|
|
18
|
-
abort("ERROR: Wrong number of arguments, #{args.length} for 2 or more.")
|
|
19
|
-
end
|
|
17
|
+
def rmhost(*args)
|
|
18
|
+
abort_if_missing_args(args, 2, '2 or more')
|
|
20
19
|
|
|
21
|
-
# Arguments
|
|
22
20
|
name = args[0].downcase
|
|
23
|
-
hosts = args.slice(1, args.length - 1)
|
|
21
|
+
hosts = normalize_names(args.slice(1, args.length - 1))
|
|
22
|
+
|
|
23
|
+
abort_if_automatic_group([name])
|
|
24
|
+
|
|
25
|
+
result = remove_hosts_from_group(name, hosts)
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
if result.warning_count.zero?
|
|
28
|
+
puts 'Succeeded.'
|
|
29
|
+
else
|
|
30
|
+
puts 'Succeeded, with warnings.'
|
|
28
31
|
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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)
|
|
33
39
|
|
|
34
|
-
# Transaction
|
|
35
|
-
warn_count = 0
|
|
36
40
|
begin
|
|
37
|
-
db.transaction do
|
|
38
|
-
# Get the target group
|
|
41
|
+
db.transaction do
|
|
39
42
|
puts "Dissociate group '#{name}' from host(s) '#{hosts.join(',')}':"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
fmt.puts 4, '- OK'
|
|
44
|
-
|
|
45
|
-
# dissociate group from the hosts
|
|
46
|
-
ungrouped = db.models[:group].find_or_create(name: 'ungrouped')
|
|
47
|
-
hosts_ds = group.hosts_dataset
|
|
48
|
-
hosts.each do |h|
|
|
49
|
-
fmt.puts 2, "- remove association {group:#{name} <-> host:#{h}}..."
|
|
50
|
-
|
|
51
|
-
# Check against existing associations
|
|
52
|
-
if hosts_ds[name: h].nil?
|
|
53
|
-
warn_count += 1
|
|
54
|
-
fmt.warn "Association {group:#{name} <-> host:#{h}} doesn't"\
|
|
55
|
-
" exist, skipping.\n"
|
|
56
|
-
fmt.puts 4, '- doesn\'t exist, skipping.'
|
|
57
|
-
fmt.puts 4, '- OK'
|
|
58
|
-
next
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
host = db.models[:host].find(name: h)
|
|
62
|
-
group.remove_host(host) unless host.nil?
|
|
63
|
-
fmt.puts 4, '- OK'
|
|
64
|
-
|
|
65
|
-
# Add the host to the ungrouped group if not in any other group
|
|
66
|
-
next unless host.groups_dataset.count == 0
|
|
67
|
-
fmt.puts 2, "- add automatic association {group:ungrouped <-> host:#{h}}..."
|
|
68
|
-
host.add_group(ungrouped)
|
|
69
|
-
fmt.puts 4, '- OK'
|
|
70
|
-
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)
|
|
71
46
|
fmt.puts 2, '- all OK'
|
|
72
|
-
|
|
47
|
+
return result
|
|
48
|
+
end
|
|
73
49
|
rescue db.exceptions[:moose] => e
|
|
74
50
|
abort("ERROR: #{e.message}")
|
|
75
51
|
end
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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'
|
|
80
80
|
end
|
|
81
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
|
|
82
90
|
end
|
|
83
91
|
end
|
|
84
92
|
end
|