ucmt 0.0.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 +7 -0
- data/bin/ucmt +36 -0
- data/bin/ucmt-ansible +38 -0
- data/bin/ucmt-discovery +22 -0
- data/bin/ucmt-salt +39 -0
- data/bin/ucmt-users +109 -0
- data/lib/ucmt/ansible.rb +76 -0
- data/lib/ucmt/discovery/local_users.rb +96 -0
- data/lib/ucmt/salt.rb +73 -0
- data/lib/ucmt/users.rb +89 -0
- metadata +86 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2e407946f8dd4efc496b4232ae162ba9dc1bedb0bb9e530a0ba50d216d33f019
|
4
|
+
data.tar.gz: fb13b4a84d0452f2458bda86a950f4cd674e76e83662eaa4fdeed3aefbd912d9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: db9af1c0b72d1154028c1de3789aeae0f3b940754ca5c2285cf34b115fa3f511c2c9472ca75ccd5a8daf3be466d0762d286f6ba387fade942f7a45258c35e653
|
7
|
+
data.tar.gz: 0c7dec652069964770fd42b06e3b65a240642a905d308370da4809efa68dd5e7907daa4135607cfb96aa10fc89e5313a78e47b14065a6e6925c23896a671255a
|
data/bin/ucmt
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#! /usr/bin/ruby
|
2
|
+
|
3
|
+
require 'optimist'
|
4
|
+
require "cheetah"
|
5
|
+
|
6
|
+
bin_dirs = ENV["PATH"].split(":")
|
7
|
+
commands = bin_dirs.each_with_object([]) do |dir, res|
|
8
|
+
Dir["#{dir}/ucmt-*"].each do |cmd|
|
9
|
+
res << File.basename(cmd).delete_prefix("ucmt-")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
commands.uniq!
|
14
|
+
commands.sort!
|
15
|
+
|
16
|
+
opts = Optimist.options do
|
17
|
+
opt :list_commands, "List all available commands"
|
18
|
+
stop_on commands
|
19
|
+
end
|
20
|
+
|
21
|
+
if opts[:list_commands]
|
22
|
+
puts "Avaible commands:"
|
23
|
+
commands.each do |cmd|
|
24
|
+
help = Cheetah.run("ucmt-#{cmd}", "--help", stdout: :capture).lines.first.chomp
|
25
|
+
puts "#{cmd}\t#{help}"
|
26
|
+
end
|
27
|
+
|
28
|
+
exit 0
|
29
|
+
end
|
30
|
+
|
31
|
+
Optimist::educate if ARGV.empty?
|
32
|
+
Optimist::die "Unknown command '#{ARGV.first}'" unless commands.include?(ARGV.first)
|
33
|
+
|
34
|
+
cmd = ARGV.shift
|
35
|
+
ARGV.unshift("ucmt-#{cmd}")
|
36
|
+
exec(*ARGV)
|
data/bin/ucmt-ansible
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#! /usr/bin/ruby
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
require "ucmt/ansible"
|
6
|
+
require 'optimist'
|
7
|
+
opts = Optimist::options do
|
8
|
+
banner "Ansible backend to write salt configuration from UCMT configuration, run it or dry run it."
|
9
|
+
opt :config, "Create salt configuration from ucmt configuration. Can be passed also as STDIN.", type: String
|
10
|
+
opt :ansible_directory, "Where to write respective read states.yml", type: String, default: "~/"
|
11
|
+
opt :dry_run, "Just prints state and what actions will be applied"
|
12
|
+
opt :apply, "Apply configuration to system"
|
13
|
+
end
|
14
|
+
Optimist::die :apply, "Cannot have apply and dry_run together" if opts[:dry_run] && opts[:apply]
|
15
|
+
|
16
|
+
if opts[:config]
|
17
|
+
data = YAML.load_file(opts[:config])
|
18
|
+
else
|
19
|
+
stdin = STDIN.tty? ? nil : STDIN.read
|
20
|
+
data = YAML.load(stdin) if stdin && !stdin.empty?
|
21
|
+
end
|
22
|
+
|
23
|
+
ansible = UCMT::Ansible.new(File.expand_path(opts[:ansible_directory]))
|
24
|
+
nocmd = true
|
25
|
+
if data
|
26
|
+
ansible.write(data)
|
27
|
+
nocmd = false
|
28
|
+
end
|
29
|
+
if opts[:dry_run]
|
30
|
+
ansible.dry_run
|
31
|
+
nocmd = false
|
32
|
+
end
|
33
|
+
if opts[:apply]
|
34
|
+
ansible.apply
|
35
|
+
nocmd = false
|
36
|
+
end
|
37
|
+
|
38
|
+
Optimist::die "At least one of UCMT configuration/apply/dry-run has to be specified." if nocmd
|
data/bin/ucmt-discovery
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#! /usr/bin/ruby
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
require 'optimist'
|
5
|
+
|
6
|
+
require "ucmt/discovery/local_users"
|
7
|
+
|
8
|
+
def write(io, data)
|
9
|
+
io.puts(data.to_yaml)
|
10
|
+
end
|
11
|
+
|
12
|
+
opts = Optimist::options do
|
13
|
+
banner "Creates UCMT configuration from system configuration.\nCreated configuration contains more information when run as root user."
|
14
|
+
opt :path, "Path to UCMT configuration", type: String
|
15
|
+
end
|
16
|
+
|
17
|
+
users = UCMT::Discovery::LocalUsers.new.read_data
|
18
|
+
if opts[:path]
|
19
|
+
File.open(opts[:path], "w") { |f| write(f, users) }
|
20
|
+
else
|
21
|
+
write(STDOUT, users)
|
22
|
+
end
|
data/bin/ucmt-salt
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
#! /usr/bin/ruby
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
require "ucmt/salt"
|
6
|
+
require 'optimist'
|
7
|
+
|
8
|
+
opts = Optimist::options do
|
9
|
+
banner "Salt backend to write salt configuration from UCMT configuration, run it or dry run it."
|
10
|
+
opt :config, "Create salt configuration from ucmt configuration. Can be passed also as STDIN.", type: String
|
11
|
+
opt :salt_directory, "Where to write respective read salt configuration", type: String, default: "/srv/salt"
|
12
|
+
opt :dry_run, "Just prints state and what actions will be applied"
|
13
|
+
opt :apply, "Apply configuration to system"
|
14
|
+
end
|
15
|
+
Optimist::die :apply, "Cannot have apply and dry_run together" if opts[:dry_run] && opts[:apply]
|
16
|
+
|
17
|
+
if opts[:config]
|
18
|
+
data = YAML.load_file(opts[:config])
|
19
|
+
else
|
20
|
+
stdin = STDIN.tty? ? nil : STDIN.read
|
21
|
+
data = YAML.load(stdin) if stdin && !stdin.empty?
|
22
|
+
end
|
23
|
+
|
24
|
+
salt = UCMT::Salt.new(opts[:salt_directory])
|
25
|
+
nocmd = true
|
26
|
+
if data
|
27
|
+
salt.write(data)
|
28
|
+
nocmd = false
|
29
|
+
end
|
30
|
+
if opts[:dry_run]
|
31
|
+
salt.dry_run
|
32
|
+
nocmd = false
|
33
|
+
end
|
34
|
+
if opts[:apply]
|
35
|
+
salt.apply
|
36
|
+
nocmd = false
|
37
|
+
end
|
38
|
+
|
39
|
+
Optimist::die "At least one of UCMT configuration/apply/dry-run has to be specified." if nocmd
|
data/bin/ucmt-users
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
#! /usr/bin/ruby
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
require 'optimist'
|
5
|
+
|
6
|
+
require 'ucmt/users'
|
7
|
+
|
8
|
+
def write(io, data)
|
9
|
+
io.puts(data.to_yaml)
|
10
|
+
end
|
11
|
+
|
12
|
+
subcommands = [ "add", "edit", "remove", "ignore", "list", "show"]
|
13
|
+
|
14
|
+
user_opts = Optimist::options do
|
15
|
+
banner("CLI for user managent in UCMT configuration.\n" \
|
16
|
+
"Configuration can be passed on stdin and then modified one to stdout or edit in place when using config option.\n" \
|
17
|
+
"Usage:\n\n" \
|
18
|
+
" ucmt-users <users options> <action> <action options>\n\n" \
|
19
|
+
"Available actions: add, edit, remove, ignore, list, show\n" \
|
20
|
+
"Use ucmt-users <action> --help for more details for specific actions.\n" \
|
21
|
+
"Users options:\n")
|
22
|
+
opt :config, "Configuration to edit in place.", type: String
|
23
|
+
stop_on subcommands
|
24
|
+
end
|
25
|
+
|
26
|
+
if user_opts[:config]
|
27
|
+
data = File.exist?(user_opts[:config]) && YAML.load_file(user_opts[:config])
|
28
|
+
else
|
29
|
+
stdin = STDIN.tty? ? nil : STDIN.read
|
30
|
+
data = YAML.load(stdin) if stdin && !stdin.empty?
|
31
|
+
end
|
32
|
+
data ||= {}
|
33
|
+
|
34
|
+
users = UCMT::Users.new(data)
|
35
|
+
|
36
|
+
write_result = false
|
37
|
+
|
38
|
+
loop do
|
39
|
+
command = ARGV.shift
|
40
|
+
case command
|
41
|
+
when "add", "edit"
|
42
|
+
write_result = true
|
43
|
+
cmd_opts = Optimist::options do
|
44
|
+
banner "Adds/Edits user"
|
45
|
+
opt :name, "Name of user. Mandatory argument.", type: String
|
46
|
+
opt :fullname, "Full name of user.", type: String
|
47
|
+
opt :no_fullname, "Do not specify full name of user."
|
48
|
+
opt :uid, "User ID number.", type: Integer
|
49
|
+
opt :no_uid, "Do not specify User ID."
|
50
|
+
opt :primary_group, "User primary group specified by name.", type: String
|
51
|
+
opt :no_primary_group, "Do not specify primary group."
|
52
|
+
opt :shell, "User shell.", type: String
|
53
|
+
opt :no_shell, "Do not specify user shell."
|
54
|
+
opt :home, "User home directory.", type: String
|
55
|
+
opt :no_home, "Do not specify user home directory."
|
56
|
+
# TODO: password support
|
57
|
+
# opt :password, "Set user password. Both already encrypted and plain password is accepted. Always stored as encrypted.", type: String
|
58
|
+
# opt :no_password, "Do not specify user password."
|
59
|
+
# opt :forbid_logging, "Do not allow user to login"
|
60
|
+
stop_on subcommands
|
61
|
+
end
|
62
|
+
Optimist::die :name, "Name is mandatory" unless cmd_opts[:name]
|
63
|
+
users.edit(cmd_opts)
|
64
|
+
when "remove"
|
65
|
+
write_result = true
|
66
|
+
cmd_opts = Optimist::options do
|
67
|
+
banner "Marks user to be removed."
|
68
|
+
opt :name, "Name of user. Mandatory argument.", type: String
|
69
|
+
stop_on subcommands
|
70
|
+
end
|
71
|
+
Optimist::die :name, "Name is mandatory" unless cmd_opts[:name]
|
72
|
+
users.remove(cmd_opts[:name])
|
73
|
+
when "ignore"
|
74
|
+
write_result = true
|
75
|
+
cmd_opts = Optimist::options do
|
76
|
+
banner "Mark user to not be modified."
|
77
|
+
opt :name, "Name of user. Mandatory argument.", type: String
|
78
|
+
stop_on subcommands
|
79
|
+
end
|
80
|
+
Optimist::die :name, "Name is mandatory" unless cmd_opts[:name]
|
81
|
+
users.ignore(cmd_opts[:name])
|
82
|
+
when "list"
|
83
|
+
cmd_opts = Optimist::options do
|
84
|
+
banner "List all user to modify."
|
85
|
+
stop_on subcommands
|
86
|
+
end
|
87
|
+
users.list
|
88
|
+
when "show"
|
89
|
+
cmd_opts = Optimist::options do
|
90
|
+
banner "Mark user to not be modified."
|
91
|
+
opt :name, "Name of user. Mandatory argument.", type: String
|
92
|
+
stop_on subcommands
|
93
|
+
end
|
94
|
+
Optimist::die :name, "Name is mandatory" unless cmd_opts[:name]
|
95
|
+
users.show(cmd_opts[:name])
|
96
|
+
when nil
|
97
|
+
break
|
98
|
+
else
|
99
|
+
Optimist.die "Invalid action '#{command}'"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
if write_result
|
104
|
+
if user_opts[:config]
|
105
|
+
File.open(user_opts[:config], "w") { |f| write(f, data) }
|
106
|
+
else
|
107
|
+
write(STDOUT, data)
|
108
|
+
end
|
109
|
+
end
|
data/lib/ucmt/ansible.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "cheetah"
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
module UCMT
|
6
|
+
class Ansible
|
7
|
+
def initialize(output_dir)
|
8
|
+
@output_dir = output_dir
|
9
|
+
end
|
10
|
+
|
11
|
+
def write(data)
|
12
|
+
FileUtils.mkdir_p(@output_dir)
|
13
|
+
|
14
|
+
result = local_users_content(data)
|
15
|
+
|
16
|
+
content = [{
|
17
|
+
"name" => "UCMT defined tasks",
|
18
|
+
"hosts" => "localhost",
|
19
|
+
"connection" => "local",
|
20
|
+
"tasks" => result
|
21
|
+
}]
|
22
|
+
|
23
|
+
File.write(File.join(@output_dir, "states.yml"), content.to_yaml)
|
24
|
+
end
|
25
|
+
|
26
|
+
def dry_run
|
27
|
+
Cheetah.run("ansible-playbook", File.join(@output_dir, "states.yml"), "--check", "--diff", stdout: STDOUT)
|
28
|
+
end
|
29
|
+
|
30
|
+
def apply
|
31
|
+
Cheetah.run("ansible-playbook", File.join(@output_dir, "states.yml"), stdout: STDOUT)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
USERS_MAPPING = {
|
37
|
+
"fullname" => "comment",
|
38
|
+
"name" => "name",
|
39
|
+
"uid" => "uid",
|
40
|
+
"groups" => "groups",
|
41
|
+
"primary_group" => "group",
|
42
|
+
"shell" => "shell",
|
43
|
+
"home" => "home",
|
44
|
+
"password" => "password"
|
45
|
+
}
|
46
|
+
def local_users_content(data)
|
47
|
+
result = []
|
48
|
+
|
49
|
+
users_data = data["local_users"]
|
50
|
+
return [] unless users_data
|
51
|
+
|
52
|
+
(users_data["add"] || []).each do |user|
|
53
|
+
res = { "name" => "User #{user["name"]}" }
|
54
|
+
key2 = "ansible.builtin.user"
|
55
|
+
res[key2] = {}
|
56
|
+
USERS_MAPPING.each_pair do |k, v|
|
57
|
+
res[key2][v] = user[k] if user[k]
|
58
|
+
end
|
59
|
+
|
60
|
+
result << res
|
61
|
+
end
|
62
|
+
|
63
|
+
(users_data["remove"] || []).each do |user|
|
64
|
+
res = { "name" => "remove " + user["name"] }
|
65
|
+
key2 = "ansible.builtin.user"
|
66
|
+
res[key2] = { "name" => user["name"],
|
67
|
+
"state" => "absent", "remove" => "yes" }
|
68
|
+
|
69
|
+
result << res
|
70
|
+
end
|
71
|
+
|
72
|
+
result
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require "json"
|
2
|
+
require "cheetah"
|
3
|
+
|
4
|
+
module UCMT
|
5
|
+
module Discovery
|
6
|
+
class LocalUsers
|
7
|
+
# TODO: remote machine
|
8
|
+
def initialize
|
9
|
+
end
|
10
|
+
|
11
|
+
def read_data
|
12
|
+
{
|
13
|
+
"local_users" => {
|
14
|
+
"add" => read
|
15
|
+
}
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def read_users
|
22
|
+
output = Cheetah.run("ansible", "localhost", "-m", "getent", "-a", "database=passwd", stdout: :capture)
|
23
|
+
|
24
|
+
res = JSON.parse(output.sub(/^.*=>/, ""))
|
25
|
+
res["ansible_facts"]["getent_passwd"]
|
26
|
+
end
|
27
|
+
|
28
|
+
def read_groups
|
29
|
+
output = Cheetah.run("ansible", "localhost", "-m", "getent", "-a", "database=group", stdout: :capture)
|
30
|
+
|
31
|
+
res = JSON.parse(output.sub(/^.*=>/, ""))
|
32
|
+
res["ansible_facts"]["getent_group"]
|
33
|
+
end
|
34
|
+
|
35
|
+
def read_passwords
|
36
|
+
output = Cheetah.run("ansible", "localhost", "-m", "getent", "-a", "database=shadow", stdout: :capture)
|
37
|
+
|
38
|
+
res = JSON.parse(output.sub(/^.*=>/, ""))
|
39
|
+
res["ansible_facts"]["getent_shadow"]
|
40
|
+
end
|
41
|
+
|
42
|
+
USERS_KEYS_MAPPING = {
|
43
|
+
"uid" => 1,
|
44
|
+
"gid" => 2,
|
45
|
+
"fullname" => 3,
|
46
|
+
"home" => 4,
|
47
|
+
"shell" => 5
|
48
|
+
}
|
49
|
+
INTEGER_KEYS = ["uid", "gid"]
|
50
|
+
SYSTEM_USER_LIMIT = 500
|
51
|
+
|
52
|
+
GROUPS_KEYS_MAPPING = {
|
53
|
+
"gid" => 1,
|
54
|
+
"users" => 2
|
55
|
+
}
|
56
|
+
|
57
|
+
def read
|
58
|
+
# reading shadow need root permissions
|
59
|
+
# TODO: for remote check needs to be different
|
60
|
+
if Process.euid == 0
|
61
|
+
passwd = read_passwords
|
62
|
+
else
|
63
|
+
passwd = {}
|
64
|
+
end
|
65
|
+
|
66
|
+
groups = read_groups
|
67
|
+
|
68
|
+
users = read_users.map do |dk, dv|
|
69
|
+
USERS_KEYS_MAPPING.each_with_object({"name" => dk}) { |(k, v), r| r[k] = dv[v] }
|
70
|
+
end
|
71
|
+
users.each { |u| INTEGER_KEYS.each { |i| u[i] = u[i].to_i } }
|
72
|
+
users.select! { |v| v["uid"] == 0 || v["uid"] > SYSTEM_USER_LIMIT } # select only non system users
|
73
|
+
users.each do |user|
|
74
|
+
user["groups"] = []
|
75
|
+
|
76
|
+
groups.each do |name, group|
|
77
|
+
gid = group[GROUPS_KEYS_MAPPING["gid"]].to_i
|
78
|
+
group_users = group[GROUPS_KEYS_MAPPING["users"]].split(",") # see man group
|
79
|
+
|
80
|
+
if user["gid"] == gid || group_users.include?(user["name"])
|
81
|
+
user["groups"] << name
|
82
|
+
end
|
83
|
+
|
84
|
+
if user["gid"] == gid
|
85
|
+
user["primary_group"] = name
|
86
|
+
user.delete("gid")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
user["password"] = passwd[user["name"]].first if passwd[user["name"]]
|
90
|
+
end
|
91
|
+
|
92
|
+
users
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/ucmt/salt.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "cheetah"
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
module UCMT
|
6
|
+
class Salt
|
7
|
+
def initialize(output_dir)
|
8
|
+
@output_dir = output_dir
|
9
|
+
end
|
10
|
+
|
11
|
+
def write(data)
|
12
|
+
FileUtils.mkdir_p(@output_dir)
|
13
|
+
|
14
|
+
states = []
|
15
|
+
states << "local_users" if write_local_users(data)
|
16
|
+
|
17
|
+
write_states(states)
|
18
|
+
end
|
19
|
+
|
20
|
+
def dry_run
|
21
|
+
Cheetah.run("salt-call", "--local", "--file-root=#{@output_dir}", "state.apply", "test=true", stdout: STDOUT)
|
22
|
+
end
|
23
|
+
|
24
|
+
def apply
|
25
|
+
Cheetah.run("salt-call", "--local", "--file-root=#{@output_dir}", "state.apply", stdout: STDOUT)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def write_states(states)
|
31
|
+
content = { "base" => { "*" => states } }
|
32
|
+
|
33
|
+
File.write(File.join(@output_dir, "top.sls"), content.to_yaml)
|
34
|
+
end
|
35
|
+
|
36
|
+
USERS_MAPPING = {
|
37
|
+
"fullname" => "fullname",
|
38
|
+
"name" => "name",
|
39
|
+
"uid" => "uid",
|
40
|
+
"groups" => "groups",
|
41
|
+
"primary_group" => "gid",
|
42
|
+
"shell" => "shell",
|
43
|
+
"home" => "home",
|
44
|
+
"password" => "password"
|
45
|
+
}
|
46
|
+
def write_local_users(data)
|
47
|
+
result = {}
|
48
|
+
|
49
|
+
users_data = data["local_users"]
|
50
|
+
return false unless users_data
|
51
|
+
|
52
|
+
(users_data["add"] || []).each do |user|
|
53
|
+
key = user["name"]
|
54
|
+
key2 = "user.present"
|
55
|
+
result[key] = { key2 => [] }
|
56
|
+
target = result[key][key2]
|
57
|
+
USERS_MAPPING.each_pair do |k, v|
|
58
|
+
target << { v => user[k] } if user[k]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
(users_data["remove"] || []).each do |user|
|
63
|
+
key = "remove " + user["name"]
|
64
|
+
key2 = "user.absent"
|
65
|
+
result[key] = { key2 => [{"name" => user["name"]}] }
|
66
|
+
end
|
67
|
+
|
68
|
+
File.write(File.join(@output_dir, "local_users.sls"), result.to_yaml)
|
69
|
+
|
70
|
+
return true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/ucmt/users.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "cheetah"
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
module UCMT
|
6
|
+
class Users
|
7
|
+
attr_reader :data
|
8
|
+
|
9
|
+
def initialize(data)
|
10
|
+
@data = data
|
11
|
+
end
|
12
|
+
|
13
|
+
def ignore(name)
|
14
|
+
(users["add"] || []).delete_if { |u| u["name"] == name }
|
15
|
+
(users["remove"] || []).delete_if { |u| u["name"] == name }
|
16
|
+
end
|
17
|
+
|
18
|
+
def remove(name)
|
19
|
+
(users["add"] || []).delete_if { |u| u["name"] == name }
|
20
|
+
users["remove"] ||= []
|
21
|
+
users["remove"] << { "name" => name }
|
22
|
+
end
|
23
|
+
|
24
|
+
def edit(options)
|
25
|
+
name = options[:name]
|
26
|
+
|
27
|
+
# ensure that user is not removed
|
28
|
+
(users["remove"] || []).delete_if { |u| u["name"] == name }
|
29
|
+
users["add"] ||= []
|
30
|
+
user = users["add"].find { |u| u["name"] == name }
|
31
|
+
if !user
|
32
|
+
user = { "name" => name }
|
33
|
+
users["add"] << user
|
34
|
+
end
|
35
|
+
|
36
|
+
handle_option(:fullname, user, options)
|
37
|
+
handle_option(:uid, user, options)
|
38
|
+
handle_option(:primary_group, user, options)
|
39
|
+
handle_option(:shell, user, options)
|
40
|
+
handle_option(:home, user, options)
|
41
|
+
|
42
|
+
# TODO: PASSWORD
|
43
|
+
# TODO: groups
|
44
|
+
end
|
45
|
+
|
46
|
+
def list
|
47
|
+
puts "Users to add/edit:"
|
48
|
+
to_add = (users["add"] || []).map {|u| u["name"]}
|
49
|
+
puts to_add.to_yaml unless to_add.empty?
|
50
|
+
puts "\nUsers to remove:"
|
51
|
+
to_remove = (users["remove"] || []).map {|u| u["name"]}
|
52
|
+
puts to_remove.to_yaml unless to_remove.empty?
|
53
|
+
puts
|
54
|
+
end
|
55
|
+
|
56
|
+
def show(name)
|
57
|
+
remove = (users["remove"] || []).find { |u| u["name"] == name }
|
58
|
+
if remove
|
59
|
+
puts "User #{name} will be removed."
|
60
|
+
return
|
61
|
+
end
|
62
|
+
add = (users["add"] || []).find { |u| u["name"] == name }
|
63
|
+
if !add
|
64
|
+
puts "User #{name} won't be modified."
|
65
|
+
return
|
66
|
+
end
|
67
|
+
|
68
|
+
puts "User #{name} will have following settings:"
|
69
|
+
puts add.to_yaml
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def handle_option(key, user, options)
|
75
|
+
if options[:"no_#{key}"]
|
76
|
+
user.delete(key.to_s)
|
77
|
+
elsif options[key]
|
78
|
+
user[key.to_s] = options[key]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def users
|
83
|
+
return @users if @users
|
84
|
+
|
85
|
+
@data["local_users"] ||= {}
|
86
|
+
@users = @data["local_users"]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ucmt
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Josef Reidinger
|
8
|
+
autorequire:
|
9
|
+
bindir: bin/
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-03-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: optimist
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: cheetah
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.5.2
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.5.2
|
41
|
+
description: A set of tools to generate configuration for various configuration management
|
42
|
+
tools like salt or ansible.
|
43
|
+
email: jreidinger@suse.com
|
44
|
+
executables:
|
45
|
+
- ucmt-discovery
|
46
|
+
- ucmt-ansible
|
47
|
+
- ucmt-salt
|
48
|
+
- ucmt
|
49
|
+
- ucmt-users
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- bin/ucmt
|
54
|
+
- bin/ucmt-ansible
|
55
|
+
- bin/ucmt-discovery
|
56
|
+
- bin/ucmt-salt
|
57
|
+
- bin/ucmt-users
|
58
|
+
- lib/ucmt/ansible.rb
|
59
|
+
- lib/ucmt/discovery/local_users.rb
|
60
|
+
- lib/ucmt/salt.rb
|
61
|
+
- lib/ucmt/users.rb
|
62
|
+
homepage: https://github.com/jreidinger/ucmt
|
63
|
+
licenses:
|
64
|
+
- GPL-2.0
|
65
|
+
metadata: {}
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options: []
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
requirements: []
|
81
|
+
rubyforge_project:
|
82
|
+
rubygems_version: 2.7.6.2
|
83
|
+
signing_key:
|
84
|
+
specification_version: 4
|
85
|
+
summary: Universal configuration management tool
|
86
|
+
test_files: []
|