racf 0.6.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.
- data/.gitignore +8 -0
- data/.rspec +2 -0
- data/.travis.yml +18 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +40 -0
- data/README.rdoc +56 -0
- data/Rakefile +9 -0
- data/TODO.txt +5 -0
- data/config/racf.yml.sample +27 -0
- data/lib/racf.rb +78 -0
- data/lib/racf/client.rb +138 -0
- data/lib/racf/commands/abstract_command.rb +13 -0
- data/lib/racf/commands/listgrp.rb +189 -0
- data/lib/racf/commands/listgrp/group_members_parser.rb +104 -0
- data/lib/racf/commands/listuser.rb +112 -0
- data/lib/racf/commands/rlist.rb +208 -0
- data/lib/racf/commands/search.rb +31 -0
- data/lib/racf/pagers/cached_ispf_pager.rb +91 -0
- data/lib/racf/pagers/ispf_pager.rb +57 -0
- data/lib/racf/s3270.rb +136 -0
- data/lib/racf/session.rb +149 -0
- data/lib/racf/version.rb +3 -0
- data/racf.gemspec +16 -0
- data/script/ci +3 -0
- data/spec/fixtures/config/racf.yml +9 -0
- data/spec/fixtures/listgrp/multiple_groups_multiple_members.txt +26 -0
- data/spec/fixtures/listgrp/multiple_members.txt +10 -0
- data/spec/fixtures/listgrp/no_members.txt +4 -0
- data/spec/fixtures/listgrp/not_found_groups.txt +21 -0
- data/spec/fixtures/listgrp/one_group.txt +7 -0
- data/spec/fixtures/listgrp/one_group_with_members.txt +13 -0
- data/spec/fixtures/listgrp/one_member.txt +7 -0
- data/spec/fixtures/listuser/all_users.txt +45 -0
- data/spec/fixtures/listuser/just_users_not_found.txt +3 -0
- data/spec/fixtures/listuser/one_user.txt +47 -0
- data/spec/fixtures/listuser/some_not_found_users.txt +88 -0
- data/spec/fixtures/racf_cache_dump.yml +9 -0
- data/spec/fixtures/rlist/gims.txt +135 -0
- data/spec/fixtures/rlist/gims_with_no_tims.txt +135 -0
- data/spec/fixtures/rlist/gims_with_not_found.txt +89 -0
- data/spec/fixtures/rlist/just_one_not_found.txt +1 -0
- data/spec/fixtures/rlist/multiple_not_found.txt +3 -0
- data/spec/fixtures/rlist/rlist_success.txt +50 -0
- data/spec/fixtures/rlist/tims_without_users.txt +119 -0
- data/spec/fixtures/search/gims.txt +30 -0
- data/spec/fixtures/search/tims.txt +30 -0
- data/spec/fixtures/session/screen_with_bottom_menu.txt +31 -0
- data/spec/fixtures/session/screen_with_top_and_bottom_menu.txt +47 -0
- data/spec/fixtures/session/screen_with_top_menu.txt +29 -0
- data/spec/fixtures/session/screen_without_menu.txt +13 -0
- data/spec/racf/client_spec.rb +155 -0
- data/spec/racf/commands/listgrp/group_members_parser_spec.rb +82 -0
- data/spec/racf/commands/listgrp_spec.rb +303 -0
- data/spec/racf/commands/listuser_spec.rb +123 -0
- data/spec/racf/commands/rlist_spec.rb +257 -0
- data/spec/racf/commands/search_spec.rb +66 -0
- data/spec/racf/pagers/cached_ispf_pager_spec.rb +212 -0
- data/spec/racf/pagers/ispf_pager_spec.rb +59 -0
- data/spec/racf/session_spec.rb +114 -0
- data/spec/racf_spec.rb +106 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/helpers.rb +5 -0
- metadata +162 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# http://about.travis-ci.org/docs/user/getting-started/
|
2
|
+
#
|
3
|
+
# use before_install to install any missing packages you require
|
4
|
+
# before_install: sudo apt-get install my-special-packages
|
5
|
+
#
|
6
|
+
# use before_script to setup any DB's you need, or start xvfb
|
7
|
+
# before_script: mysql -e 'create database myapp_test;'
|
8
|
+
#
|
9
|
+
# override script if you have a special script to run
|
10
|
+
# (default is bundle exec rake)
|
11
|
+
# script: bundle exec rspec
|
12
|
+
source_key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBNnZ5bVk3bjRXRlBPYnNSb2JnZnlFVXhkYjZYTWtoSkh0bkwwY2NUOHZXUnE3VTVJCkM2TlpTRHlhcFdscGxyL3MwMEJaOUxUcFNmMUl5Q1ZQUTVtTk1CS041cDd2RTQ2NVJucWtaMzd2OWk2dFdDb2IKQmFQeWsrRlZrbGE3amJ4bElORG9odGhzcVlxN1YxRzB1a1ovWHBiSXlCbElIWW4zRk9RTFJldWpZQUpxalFKMgplRnRhQVNRZFJJNUpBcHcrampBUmhzaEJ1a2ZmNnRob3IvMkppajlqOGplYmpjZjdkSjBNUkdDa21aL2duclhDCjNCWjl2ZW4ydWJEWEVHSEp3ZXJDd29EajhHRzBMcU5PdXZUVVlnZG9mNkEzSHRYOWlzWnYwZElNUTIrTE0yYWcKTWV1S1BjRDcwY0d5bjZYREVZMlIyd2lhUGx3ckhGT2FCUnBEU3dJREFRQUJBb0lCQUJVajliUXpIdXlUWFpjSgpoSHVocTExekNWVHFEYVYyajQwU2VudGh4WHNrZFpvejJMZHpVcXoxQnRTcXp4MEkxejJLSkkybldQV1hHOTBsCnZpUE94S3oyZmwycEhza2JwZjk1T1NFMmdsQWJrdXpEblBxODdTM3dyOG9pazIwRTdmUXQxejJLYXZ3MXh2VHMKSE41RlNXQXhJSThmOXlUeExKRnNrcmNQby9zWEVHWHhyR3YyeEhQeDBYYUEzM3JwajQwMUZhVnJHTC9RM24yRQo0L1JEbnE1blkrQzdQOUJqWUFBVHMzb0R5UEkxeTE3MnUrc3p4TVNNb2VpZzRVcHVaQlp5czRBYzE4RktCUXM5CitxNUk4RnVvaU83dVVieWlkcUozc3ZMdkh3NCtnOTFqV1VpNkVVL1pGZVBzV0UvaUhyMjVDSGVZbGltNjdHTksKcE9wVkR3RUNnWUVBL2tCSHlnVnNoRUYyWW5GdldmVythdTJ3Q0dHNitpeDBRZTI4RjNHZGlYMUtPRHYrVElDUgpBSDgzaFJLS3hiaHNaRTI3RzZyd2JMakRMajNHKy9HUVlLRnlPM0JmcWpTalV5QnhmSVpTNjYwKzVWUWVhQWJOCkJFcjUwWE5Yd0FoanViRWJ2ZUkra0RuNENHb0dCMzQvV2NDZFhyRXRmOVJwYVFyWkR1VlZRN0VDZ1lFQTdKcHkKVWoxTlRCUXBKeTlxWmE1WFN5dVdmV1pTZTRmWjhXeDI2YTZXd1JkMlNaUTRseVN5Umk4K2VGdU16RFdwc09oVgppT05hM1prUVJhYUVXRjhYby9lSkhmTkpoanQrTU9SaGY3S1FtYWc5WHhGODZzRURueElGV1hxR0JYd2JXNVlnCk41VlpMUDRmaWtscWpSTU5qR2ZmbXY2OCt3MG9WUk9hMXFHWUlic0NnWUVBNEIxM2FudTNlU2xmR2syVmN2M3MKbU1MbDNhMlFMTUFNeHFTeG5SUzRZaDUvMHV3SE1CWlFPdGpvN3JNMmdpMGIxcHF5S1NSWUErNGxsbGc4VTQ5Twp6ZTdpQzhUMWZzb2hnYi9pYVhyYUV3YkduMDJ6ODh5eTRXclpwU2dBSExVSWV3SEVTMi9uNkNoQkcrR2hhTzkvCkZqVjBRcWo1UzJuSWRTQnJ6dmFXWHpFQ2dZQWdwNHhCcDI1RW9yK3ZSYzhFYTVSbXJvUmRtRUtvQSs3amNCblYKVktBLzFCSWlRNCt2NUlPcVJoajBHWWFNSkpmZjc2RG1mS0pzMVJRMjJLMkpXTTUxZGRONXk3c0pDS3I0Nm53KwpXSkcyYzA3TkVTZlJhb1FxYngxSDVTNGNFYTNnN1E1VFBZeUtOaVI2UEhtMU1Mejg5TlVhbWkvUTRsYi9ydTMrCjAxeWd3d0tCZ0hqRU5FOWlFejJlc0F4MWxFWGoyUGRGVE12VmxyeVpWbjZjSmJlbVU2R1JvYUFQdno5MWk5eTMKU3NUYVRMMnBRZ0x4R014OCtOTXlFRnNYRzY2RnA0RWxKQVRxc05Fb3V3UVRGNG54ZXlMRDZSMUF0VFlyeEZ5NgpVbnoxOHFKcTZTeTE5UDM0TDNiYUg4L3grS2lCcVBuN1ZCMHZsV2pmcG5JRnpZOTl5M0NvCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==
|
13
|
+
notifications:
|
14
|
+
campfire: "autoseg:48d4106f85af3071772752d83f7c0ef0160d89e4@451526"
|
15
|
+
language: ruby
|
16
|
+
script: script/ci
|
17
|
+
rvm:
|
18
|
+
- 1.8.7
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
racf (0.6.0)
|
5
|
+
state_machine (~> 1.1.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
awesome_print (1.0.1)
|
11
|
+
columnize (0.3.5)
|
12
|
+
diff-lcs (1.1.3)
|
13
|
+
linecache (0.46)
|
14
|
+
rbx-require-relative (> 0.0.4)
|
15
|
+
rake (0.9.2.2)
|
16
|
+
rbx-require-relative (0.0.5)
|
17
|
+
rspec (2.8.0)
|
18
|
+
rspec-core (~> 2.8.0)
|
19
|
+
rspec-expectations (~> 2.8.0)
|
20
|
+
rspec-mocks (~> 2.8.0)
|
21
|
+
rspec-core (2.8.0)
|
22
|
+
rspec-expectations (2.8.0)
|
23
|
+
diff-lcs (~> 1.1.2)
|
24
|
+
rspec-mocks (2.8.0)
|
25
|
+
ruby-debug (0.10.4)
|
26
|
+
columnize (>= 0.1)
|
27
|
+
ruby-debug-base (~> 0.10.4.0)
|
28
|
+
ruby-debug-base (0.10.4)
|
29
|
+
linecache (>= 0.3)
|
30
|
+
state_machine (1.1.2)
|
31
|
+
|
32
|
+
PLATFORMS
|
33
|
+
ruby
|
34
|
+
|
35
|
+
DEPENDENCIES
|
36
|
+
awesome_print
|
37
|
+
racf!
|
38
|
+
rake
|
39
|
+
rspec
|
40
|
+
ruby-debug
|
data/README.rdoc
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
== RACF
|
2
|
+
|
3
|
+
This gem is a Ruby wrapper around the around IBM's Resource Access Control
|
4
|
+
Facility (RACF). RACF It is a security system that provides access control
|
5
|
+
and auditing functionality for the z/OS and z/VM operating systems.
|
6
|
+
|
7
|
+
Yes, we're talking about using Ruby in order to connect to a mainframe!!!
|
8
|
+
|
9
|
+
After the mainframe fear passes, let's talk a bit more. (...)
|
10
|
+
|
11
|
+
|
12
|
+
== How to install?
|
13
|
+
bundle
|
14
|
+
bundle exec rake install
|
15
|
+
|
16
|
+
|
17
|
+
== How to run the specs?
|
18
|
+
bundle exec rake
|
19
|
+
|
20
|
+
|
21
|
+
== How the gem connect to a mainframe?
|
22
|
+
|
23
|
+
The way the RACF gem connects to the mainframe is through a terminal emulator
|
24
|
+
called S3270 (http://x3270.bgp.nu/s3270-man.html). S3270 opens a telnet
|
25
|
+
connection with the mainframe. After that, it opens a socket on which we can
|
26
|
+
open a telnet connection. It's through that last connection that we use s3270
|
27
|
+
to communicate with the mainframe.
|
28
|
+
|
29
|
+
So, it's something like that:
|
30
|
+
|
31
|
+
mainframe <=== telnet ===> s3270 (terminal emulator) <=== telnet ===> RACF Gem
|
32
|
+
|
33
|
+
|
34
|
+
== Ok, but, how can I use that &*%@@!%&* gem ???
|
35
|
+
|
36
|
+
You can run a RACF command as simple as that:
|
37
|
+
|
38
|
+
client = Racf::Client.new(:user_id => "mainframe_user_id",
|
39
|
+
:password => "mainframe_user_password",
|
40
|
+
:server_address => "192.168.0.1")
|
41
|
+
|
42
|
+
client.start_session do |c|
|
43
|
+
users = c.listuser("AJ00042")
|
44
|
+
puts users
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
== What's next?
|
50
|
+
|
51
|
+
Besides Ruby, the only thing you need to really use the power of that gem
|
52
|
+
is a big, big, really big client who owns a mainframe!!!
|
53
|
+
|
54
|
+
But, if you're trying that in your home and you don't have a mainframe in
|
55
|
+
your bedroom, you should give Hercules a try (http://www.hercules-390.org/).
|
56
|
+
|
data/Rakefile
ADDED
data/TODO.txt
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
development:
|
2
|
+
user_id: "user"
|
3
|
+
password: "password"
|
4
|
+
server_address: "127.0.0.1"
|
5
|
+
server_port: 3270
|
6
|
+
script_port: 3003
|
7
|
+
caching: false
|
8
|
+
dump_file: "racf_cache_dump.yml"
|
9
|
+
|
10
|
+
test:
|
11
|
+
user_id: "user"
|
12
|
+
password: "password"
|
13
|
+
server_address: "127.0.0.1"
|
14
|
+
server_port: 3270
|
15
|
+
script_port: 3003
|
16
|
+
caching: false
|
17
|
+
dump_file: "racf_cache_dump.yml"
|
18
|
+
|
19
|
+
production:
|
20
|
+
user_id: "user"
|
21
|
+
password: "password"
|
22
|
+
server_address: "127.0.0.1"
|
23
|
+
server_port: 3270
|
24
|
+
script_port: 3003
|
25
|
+
caching: false
|
26
|
+
dump_file: "racf_cache_dump.yml"
|
27
|
+
|
data/lib/racf.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler/setup"
|
3
|
+
|
4
|
+
require "fileutils"
|
5
|
+
require "logger"
|
6
|
+
require "pathname"
|
7
|
+
|
8
|
+
module Racf
|
9
|
+
class << self
|
10
|
+
def root
|
11
|
+
@_root ||=
|
12
|
+
if defined?(Rails)
|
13
|
+
Rails.root
|
14
|
+
else
|
15
|
+
Pathname.new(File.expand_path("../..", __FILE__))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def logger
|
20
|
+
@logger ||= default_logger
|
21
|
+
end
|
22
|
+
attr_writer :logger
|
23
|
+
|
24
|
+
def env
|
25
|
+
@_env ||= ENV['RAILS_ENV'] || ENV['RACF_ENV'] || "development"
|
26
|
+
end
|
27
|
+
|
28
|
+
def env=(environment)
|
29
|
+
@_env = environment
|
30
|
+
end
|
31
|
+
|
32
|
+
def config
|
33
|
+
@config ||= symbolize_keys(load_config[env])
|
34
|
+
end
|
35
|
+
attr_writer :config
|
36
|
+
|
37
|
+
def config_file_path
|
38
|
+
@config_file_path ||= root.join("config", "racf.yml")
|
39
|
+
end
|
40
|
+
attr_writer :config_file_path
|
41
|
+
|
42
|
+
def dump_file_path
|
43
|
+
@dump_file_path ||= root.join("tmp", config[:dump_file] || "racf_cache_dump.yml")
|
44
|
+
end
|
45
|
+
attr_writer :dump_file_path
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def load_config
|
50
|
+
YAML.load_file(config_file_path)
|
51
|
+
end
|
52
|
+
|
53
|
+
def default_logger
|
54
|
+
create_log_dir
|
55
|
+
|
56
|
+
log_path = default_log_dir.join("racf.log")
|
57
|
+
logger = Logger.new(log_path)
|
58
|
+
logger.formatter = Logger::Formatter.new
|
59
|
+
logger
|
60
|
+
end
|
61
|
+
|
62
|
+
def create_log_dir
|
63
|
+
FileUtils.mkdir_p(default_log_dir)
|
64
|
+
end
|
65
|
+
|
66
|
+
def default_log_dir
|
67
|
+
root.join("log")
|
68
|
+
end
|
69
|
+
|
70
|
+
def symbolize_keys(hash)
|
71
|
+
return {} unless hash
|
72
|
+
|
73
|
+
h = {}
|
74
|
+
hash.each_pair { |k, v| h[k.to_sym] = v }
|
75
|
+
h
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/racf/client.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
Dir["#{File.dirname(__FILE__)}/commands/*.rb"].each { |file_path| require file_path }
|
2
|
+
require 'racf/session'
|
3
|
+
require 'racf/pagers/ispf_pager'
|
4
|
+
require 'racf/pagers/cached_ispf_pager'
|
5
|
+
|
6
|
+
module Racf
|
7
|
+
# That class is intended to be "the" API for clients of the
|
8
|
+
# RACF gem. You can use it to open a connection with a mainframe
|
9
|
+
# run some RACF commands and then close the connection.
|
10
|
+
#
|
11
|
+
class Client
|
12
|
+
COMMANDS = %w(listgrp listuser rlist search)
|
13
|
+
|
14
|
+
attr_accessor :user_id, :password, :server_address, :server_port, :script_port,
|
15
|
+
:caching, :verbose, :session, :telnet_timeout
|
16
|
+
|
17
|
+
def initialize(settings={})
|
18
|
+
settings = Racf.config.merge(settings)
|
19
|
+
|
20
|
+
@user_id = settings[:user_id]
|
21
|
+
@password = settings[:password]
|
22
|
+
@server_address = settings[:server_address]
|
23
|
+
@server_port = settings[:server_port]
|
24
|
+
@script_port = settings[:script_port]
|
25
|
+
@telnet_timeout = settings[:telnet_timeout]
|
26
|
+
@caching = settings[:caching].nil? ? true : settings[:caching]
|
27
|
+
@verbose = settings[:verbose].nil? ? false : settings[:verbose]
|
28
|
+
|
29
|
+
@session = settings[:session] || create_session
|
30
|
+
|
31
|
+
load_pagers
|
32
|
+
end
|
33
|
+
|
34
|
+
def start_session
|
35
|
+
session.start
|
36
|
+
|
37
|
+
if block_given?
|
38
|
+
begin
|
39
|
+
yield self
|
40
|
+
rescue Exception => exception
|
41
|
+
Racf.logger.error exception.message
|
42
|
+
Racf.logger.error exception.backtrace.join("\n")
|
43
|
+
raise
|
44
|
+
ensure
|
45
|
+
finish_session
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def finish_session
|
51
|
+
session.finish
|
52
|
+
end
|
53
|
+
|
54
|
+
# we should deprecate this api, in favor to config it globally
|
55
|
+
def logger=(logger)
|
56
|
+
Racf.logger = logger
|
57
|
+
end
|
58
|
+
|
59
|
+
COMMANDS.each do |command_method_name|
|
60
|
+
define_method(command_method_name) do |*args| # def listgrp(*args)
|
61
|
+
command_class = command(command_method_name) # command_class = Racf::Commands::Listgrp
|
62
|
+
#
|
63
|
+
@pagers[command_class].run(*args) # @pagers[command_class].run(*args)
|
64
|
+
end # end
|
65
|
+
end
|
66
|
+
|
67
|
+
# we should deprecate this api, in favor to config it globally
|
68
|
+
def logger=(logger)
|
69
|
+
Racf.logger = logger
|
70
|
+
end
|
71
|
+
|
72
|
+
def dump_cache
|
73
|
+
File.open(Racf.dump_file_path, "w") { |f| YAML.dump(cache, f) }
|
74
|
+
end
|
75
|
+
|
76
|
+
def load_cache
|
77
|
+
self.cache = YAML.load_file(Racf.dump_file_path)
|
78
|
+
rescue StandardError
|
79
|
+
Racf.logger.warn "Could not load cache file! Running with empty cache."
|
80
|
+
end
|
81
|
+
|
82
|
+
def cache=(serialized_cache)
|
83
|
+
@pagers.each do |command_class, pager|
|
84
|
+
if pager.respond_to? :cache=
|
85
|
+
pager.cache = serialized_cache[command_class.to_s]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def cache
|
91
|
+
cache_dump = {}
|
92
|
+
|
93
|
+
@pagers.each do |command_class, pager|
|
94
|
+
if pager.respond_to? :cache
|
95
|
+
cache_dump[command_class.to_s] = pager.cache
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
cache_dump
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def create_session
|
105
|
+
Session.new(
|
106
|
+
:user_id => user_id,
|
107
|
+
:password => password,
|
108
|
+
:server_address => server_address,
|
109
|
+
:server_port => server_port,
|
110
|
+
:script_port => script_port,
|
111
|
+
:verbose => verbose,
|
112
|
+
:telnet_timeout => telnet_timeout
|
113
|
+
)
|
114
|
+
end
|
115
|
+
|
116
|
+
def load_pagers
|
117
|
+
@pagers = {}
|
118
|
+
|
119
|
+
COMMANDS.each do |command_method_name|
|
120
|
+
command_class = command(command_method_name)
|
121
|
+
|
122
|
+
@pagers[command_class] ||= pager_class.new(command_class.new(session))
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def pager_class
|
127
|
+
if caching
|
128
|
+
Pagers::CachedIspfPager
|
129
|
+
else
|
130
|
+
Pagers::IspfPager
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def command(command_method_name)
|
135
|
+
Racf::Commands.const_get("#{command_method_name.capitalize}")
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'racf/commands/abstract_command'
|
2
|
+
require 'racf/commands/listgrp/group_members_parser'
|
3
|
+
|
4
|
+
require 'state_machine'
|
5
|
+
|
6
|
+
module Racf
|
7
|
+
module Commands
|
8
|
+
class Listgrp < AbstractCommand
|
9
|
+
GROUP_CLUE = /INFORMATION FOR GROUP|NAME NOT FOUND IN RACF DATA SET/
|
10
|
+
|
11
|
+
state_machine :state, :initial => :nothing_processed do
|
12
|
+
state :nothing_processed, :name_processed, :superior_group_and_owner_processed,
|
13
|
+
:description_processed, :subgroups_processed,
|
14
|
+
:members_processed, :group_processed
|
15
|
+
|
16
|
+
event :process_name do
|
17
|
+
transition [:nothing_processed, :group_processed] => :name_processed
|
18
|
+
end
|
19
|
+
|
20
|
+
event :process_superior_group_and_owner do
|
21
|
+
transition :name_processed => :superior_group_and_owner_processed
|
22
|
+
end
|
23
|
+
|
24
|
+
event :process_description do
|
25
|
+
transition :superior_group_and_owner_processed => :description_processed
|
26
|
+
end
|
27
|
+
|
28
|
+
event :process_subgroups do
|
29
|
+
transition :description_processed => :subgroups_processed
|
30
|
+
end
|
31
|
+
|
32
|
+
event :finish_group_parsing do
|
33
|
+
transition :subgroups_processed => :group_processed
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(session)
|
38
|
+
@current_line = nil
|
39
|
+
@current_group = {}
|
40
|
+
@members_parser = Listgrp::GroupMembersParser.new
|
41
|
+
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def run(*args)
|
46
|
+
@groups = {}
|
47
|
+
@current_line = nil
|
48
|
+
|
49
|
+
groups_names = extract_groups_name(args)
|
50
|
+
command = "LISTGRP (#{groups_names})"
|
51
|
+
raw_output = @session.run_command(command)
|
52
|
+
log_number_of_groups(raw_output)
|
53
|
+
|
54
|
+
@group_index = 1
|
55
|
+
|
56
|
+
@number_of_lines = raw_output.lines.to_a.size
|
57
|
+
@line_number = 0
|
58
|
+
|
59
|
+
raw_output.each_line do |line|
|
60
|
+
@current_line = line.strip
|
61
|
+
@line_number += 1
|
62
|
+
|
63
|
+
group_parsed = catch(:group_parsed) do
|
64
|
+
case state_name
|
65
|
+
when :nothing_processed, :group_processed
|
66
|
+
process_name
|
67
|
+
when :name_processed
|
68
|
+
process_superior_group_and_owner
|
69
|
+
when :superior_group_and_owner_processed
|
70
|
+
process_description
|
71
|
+
when :description_processed
|
72
|
+
process_subgroups
|
73
|
+
when :subgroups_processed
|
74
|
+
@current_group[:members] ||= {}
|
75
|
+
@members_parser ||= Listgrp::GroupMembersParser.new
|
76
|
+
|
77
|
+
process_members unless next_group?
|
78
|
+
finish_group_parsing if members_parsed?
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
process_name if group_parsed == :true and !last_line?
|
83
|
+
end
|
84
|
+
|
85
|
+
@groups
|
86
|
+
end
|
87
|
+
|
88
|
+
def extract_resources(args)
|
89
|
+
resources = args.first
|
90
|
+
|
91
|
+
case resources
|
92
|
+
when String
|
93
|
+
[resources]
|
94
|
+
when Array
|
95
|
+
resources
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def extract_groups_name(arguments)
|
102
|
+
extract_resources(arguments).join(" ")
|
103
|
+
end
|
104
|
+
|
105
|
+
def log_number_of_groups(raw_output)
|
106
|
+
@number_of_groups = raw_output.scan(GROUP_CLUE).size
|
107
|
+
Racf.logger.info("Processing #{@number_of_groups} group(s)")
|
108
|
+
end
|
109
|
+
|
110
|
+
def process_name
|
111
|
+
Racf.logger.info("Processing group #{@group_index} of #{@number_of_groups} groups")
|
112
|
+
|
113
|
+
if match_data = @current_line.match(/INFORMATION FOR GROUP (.*?)$/)
|
114
|
+
group_name = match_data[1]
|
115
|
+
Racf.logger.info("\tGroup name: #{group_name}")
|
116
|
+
@groups[group_name.to_sym] = @current_group
|
117
|
+
super
|
118
|
+
else
|
119
|
+
register_not_found_group
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def register_not_found_group
|
124
|
+
Racf.logger.info("\tGroup not found")
|
125
|
+
@group_index += 1
|
126
|
+
end
|
127
|
+
|
128
|
+
def process_superior_group_and_owner
|
129
|
+
match_data = @current_line.match(/SUPERIOR GROUP=(.*?)\s+OWNER=(.*)$/)
|
130
|
+
@current_group[:superior_group] = match_data[1]
|
131
|
+
@current_group[:owner] = match_data[2]
|
132
|
+
|
133
|
+
super
|
134
|
+
end
|
135
|
+
|
136
|
+
def process_description
|
137
|
+
if match_data = @current_line.match(/INSTALLATION DATA=(.*?)$/)
|
138
|
+
@current_group[:description] = match_data[1]
|
139
|
+
elsif @current_line == "NO INSTALLATION DATA"
|
140
|
+
@current_group[:description] = "NO DESCRIPTION"
|
141
|
+
else
|
142
|
+
raise
|
143
|
+
end
|
144
|
+
|
145
|
+
super
|
146
|
+
end
|
147
|
+
|
148
|
+
def process_subgroups
|
149
|
+
if @current_line.match(/NO SUBGROUPS/)
|
150
|
+
@current_group[:subgroups] = []
|
151
|
+
super
|
152
|
+
elsif match_data = @current_line.match(/SUBGROUP\(S\)=(.*?)$/)
|
153
|
+
subgroups = match_data[1].strip
|
154
|
+
@current_group[:subgroups] = subgroups.split(" ")
|
155
|
+
super
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def process_members
|
160
|
+
@members_parser.parse(@current_line)
|
161
|
+
@current_group[:members].merge!(@members_parser.members) if @members_parser.parsed?
|
162
|
+
end
|
163
|
+
|
164
|
+
def next_group?
|
165
|
+
@current_line.match(GROUP_CLUE)
|
166
|
+
end
|
167
|
+
|
168
|
+
def members_parsed?
|
169
|
+
@members_parser.parsed? && (next_group? || last_line?)
|
170
|
+
end
|
171
|
+
|
172
|
+
def last_line?
|
173
|
+
@line_number == @number_of_lines
|
174
|
+
end
|
175
|
+
|
176
|
+
def finish_group_parsing
|
177
|
+
@current_group[:members].merge!(@members_parser.members)
|
178
|
+
|
179
|
+
@members_parser = nil
|
180
|
+
@current_group = {}
|
181
|
+
@group_index += 1
|
182
|
+
|
183
|
+
super
|
184
|
+
|
185
|
+
throw(:group_parsed, :true)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|