ucmt 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|