From dfc96db3b461fd9758241ad335957e73f3237f31 Mon Sep 17 00:00:00 2001 From: Albin Gustavsson Date: Wed, 11 Oct 2017 12:37:30 +0200 Subject: [PATCH 1/8] Import work in progress pj2ipahost tool from @albgus In https://github.com/saab-simc-admin/palletjack/pull/91 @albgus was working on a tool for adding FreeIPA hosts for Pallet Jack systems. However, he left the project before the tool vould be merged. Import his latest version as a base for future work. --- tools/exe/palletjack2ipahost | 176 +++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100755 tools/exe/palletjack2ipahost diff --git a/tools/exe/palletjack2ipahost b/tools/exe/palletjack2ipahost new file mode 100755 index 0000000..ff02d96 --- /dev/null +++ b/tools/exe/palletjack2ipahost @@ -0,0 +1,176 @@ +#!/usr/bin/env ruby + +# Create host objects in a FreeIPA or Red Hat IdM instance. +# +# Unless a specific system is specified this will loop trough all systems +# in the warehouse and ensure that a corresponding host object exists. +# Hostgroups are synchronized with the warehouse and any group not +# specified in the warehouse will be removed. +# +# Host objects will be created with a randomly generated password. The +# password is saved in the warehouse as system.ipa.otp, which can later be +# exported to the Configuration Management tool. +# +# When specifying the --reinstall parameter the existing host object will +# be removed and a new one created, allowing the machine be be reinstalled. +# +# If the IPA server is configured as authoritative DNS server, an A record +# will be created for the systems primary interface (interface whose +# domain matches with the system). +# +# TODO: Implement full synchronization of all relevant DNS records +# +# Data model assumptions: +# - Multiple IP addresses in the same domain is equivalent + +require 'palletjack/tool' +require 'ipa/client' +require 'yaml' + +class PalletJack2IPAHost < PalletJack::Tool + def parse_options(opts) + opts.banner = +"Usage: #{$PROGRAM_NAME} -w -i [-s [--reinstall]] + +Synchronize the warehouse with the IPA server." + + opts.on('-i SERVER', '--ipa-server SERVER', 'IPA server fqdn', String) {|srv| + options[:ipa] = srv + } + opts.on('-s SYSTEM', '--system SYSTEM', 'name of system to synchronize', String) {|system| + options[:system] = system + } + opts.on('-r', '--reinstall', 'reinstall the system (can only be used when a system is specified)') {|reinstall| + required_option :system + options[:reinstall] = reinstall + } + + required_option :ipa + end + + def sync_system(system) + # Create the IPA session and load pallet data + ipa = IPA::Client.new(host: options[:ipa]) + box = system['system.ipa'] || {} + + puts "Processing #{system['net.dns.fqdn']}..." + + # Remove the host object + if options[:reinstall] then + del_host = ipa.host_del(hostname: system['net.dns.fqdn'], params: { 'updatedns' => true } ) + if del_host['error'] then + puts " ! ERROR#{del_host['error']['code']} #{del_host['error']['name']}: #{del_host['error']['message']}" + return + else + puts " * Deleted host object" + end + end + + # + # Create a new host object + unless ipa.host_exists? system['net.dns.fqdn'] + # Generate a random password + add_host_params = { 'random' => true } + + ip_address = nil + system.children(kind:'ipv4_interface') do |interface| + # Find an interface in the same domain as the host + if interface['net.dns.domain'] == system['net.dns.domain'] then + ip_address = interface['net.ipv4.address'] + end + end + + if ip_address then + add_host_params['ip_address'] = ip_address + else + puts " ! WARNING: No IP Address found for #{system['net.dns.fqdn']}, setting force = true." + add_host_params['force'] = true + end + + add_host = ipa.host_add(hostname: system['net.dns.fqdn'], params: add_host_params) + + if add_host['error'] then + puts " ! ERROR#{add_host['error']['code']} #{add_host['error']['name']}: #{add_host['error']['message']}" + return + end + + puts " * Created host object" + # Update the box + # Note that ['result']['result'] is not acutally a typo, the API + # returns data like that. + box['otp'] = add_host['result']['result']['randompassword'] + end + + # Now we know that there is a host object, so query + # the available information. + host_show = ipa.host_show(hostname: system['net.dns.fqdn'], all: true) + host_data = host_show['result']['result'] + + # Sync hostgroups to the warehouse. This will first loop over the + # hostgroups in the warehouse and join the groups the system should + # be a member of, then loop trough all of the hostgroups the system + # is a member of and leave any group not specified in the warehouse. + if system['system.ipa.hostgroups'] then + + hostgroups = host_data['memberof_hostgroup'] || [] + system['system.ipa.hostgroups'].each do |group| + unless hostgroups.include? group then + ipa_mod = ipa.api_post(method: 'hostgroup_add_member', item: group, + params: { 'host' => [ system['net.dns.fqdn'] ] }) + + if ipa_mod['error'] then + puts " ! #{ipa_mod['error']['code']} #{ipa_mod['error']['name']}: #{ipa_mod['error']['message']}" + else + puts " * Added to hostgroup #{group}" + end + end + end + + hostgroups.each do |group| + unless system['system.ipa.hostgroups'].include? group then + ipa_mod = ipa.api_post(method: 'hostgroup_remove_member', item: group, + params: { 'host' => [ system['net.dns.fqdn'] ] }) + + if ipa_mod['error'] then + puts " ! #{ipa_mod['error']['code']} #{ipa_mod['error']['name']}: #{ipa_mod['error']['message']}" + else + puts " * Removed from hostgroup #{group}" + end + end + end + + end + + @ipa_boxes[system['system.name']] = box + + end + + def process + + @ipa_boxes = {} + + if options[:system] then + system = jack.fetch(kind: 'system', name: options[:system]) + sync_system system + else + # Make a sync operation of all systems in the database + jack.each(kind: 'system') do |system| + next unless system['system.role'].include? 'ipa-client' + sync_system system + end + end + + end + + def output + @ipa_boxes.each do |system, content| + pallet_box 'system', system, 'ipa' do + { system: { ipa: content } } + end + end + end +end + +if PalletJack2IPAHost.standalone?(__FILE__) then + PalletJack2IPAHost.run +end From 396bacfd19dd5771f0d05f99a23ee70e2ee5a7f5 Mon Sep 17 00:00:00 2001 From: Karl-Johan Karlsson Date: Wed, 11 Oct 2017 12:54:43 +0200 Subject: [PATCH 2/8] Add dependency on ipa-ruby --- tools/palletjack-tools.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/palletjack-tools.gemspec b/tools/palletjack-tools.gemspec index 56ef97f..aa6e18e 100644 --- a/tools/palletjack-tools.gemspec +++ b/tools/palletjack-tools.gemspec @@ -29,6 +29,7 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency 'dns-zone', '~> 0.3' spec.add_runtime_dependency 'ruby-ip', '~> 0.9' spec.add_runtime_dependency 'json', '~> 1.8' + spec.add_runtime_dependency 'ipa-ruby', '~> 0.0' spec.add_development_dependency 'bundler', '~> 1.13' spec.add_development_dependency 'rake', '~> 10.0' From 982c00109cdce20eae074e65d8e5c4c7fc09f2c2 Mon Sep 17 00:00:00 2001 From: Karl-Johan Karlsson Date: Thu, 12 Oct 2017 09:33:41 +0200 Subject: [PATCH 3/8] Fix remaining RuboCop warnings for Style/MultilineIfThen RuboCop doesn't like including "then" in multi-line "if" statements. We still had a few of those floating around, but ignored. Fix them, and remove the configuration for ignoring them. --- .rubocop_todo.yml | 8 -------- lib/palletjack/keytransformer.rb | 5 ++--- lib/palletjack/tool.rb | 1 - tools/exe/palletjack2unbound | 2 +- 4 files changed, 3 insertions(+), 13 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 75f9d0f..f09cb93 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -398,14 +398,6 @@ Style/MultilineIfModifier: Exclude: - 'tools/exe/palletjack2unbound' -# Offense count: 5 -# Cop supports --auto-correct. -Style/MultilineIfThen: - Exclude: - - 'lib/palletjack/keytransformer.rb' - - 'lib/palletjack/tool.rb' - - 'tools/exe/palletjack2unbound' - # Offense count: 6 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, SupportedStyles. diff --git a/lib/palletjack/keytransformer.rb b/lib/palletjack/keytransformer.rb index aaf11cd..0739168 100644 --- a/lib/palletjack/keytransformer.rb +++ b/lib/palletjack/keytransformer.rb @@ -49,7 +49,7 @@ def synthesize_internal(param, dictionary, result = String.new) case param when String rex = /#\[([[:alnum:]._-]+)\]/ - if md = rex.match(param) then + if md = rex.match(param) result << md.pre_match return unless lookup = dictionary[md[1]] result << lookup.to_s @@ -249,9 +249,8 @@ def transform!(pallet) transforms.each do |t| transform, param = t.flatten - if self.respond_to?(transform.to_sym) then + if self.respond_to?(transform.to_sym) if new_value = self.send(transform.to_sym, param, context) - then new_value = TraceableString.new(new_value) new_value.file = transform.file new_value.line = transform.line diff --git a/lib/palletjack/tool.rb b/lib/palletjack/tool.rb index 5b6ee55..5716e23 100644 --- a/lib/palletjack/tool.rb +++ b/lib/palletjack/tool.rb @@ -457,7 +457,6 @@ def git_header(tool_name, comment_char: '#', include_id: false) #{comment_char}#{comment_char} Repository: #{workdir} #{comment_char}#{comment_char} Branch: #{branch}\n" if include_id - then header += "#{comment_char}#{comment_char} Commit ID: #{repo.head.target_id}\n" end diff --git a/tools/exe/palletjack2unbound b/tools/exe/palletjack2unbound index 451aae0..39933f7 100755 --- a/tools/exe/palletjack2unbound +++ b/tools/exe/palletjack2unbound @@ -51,7 +51,7 @@ stub-zone: stubfile << " stub-addr: #{addr}\n" end - if @transparent then + if @transparent stubfile << "\nserver:\n local-zone: \"#{@zone}\" transparent\n" end end From 1858ce26562650e4d03cce237e9ced3865b7de7b Mon Sep 17 00:00:00 2001 From: Karl-Johan Karlsson Date: Thu, 12 Oct 2017 09:35:44 +0200 Subject: [PATCH 4/8] Disable RuboCop warning Style/Not RuboCop prefers "!" instead of "not", but we don't. Disable that test. --- .rubocop.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index a165dc0..098a271 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -9,3 +9,5 @@ Style/SymbolArray: Lint/HandleExceptions: Exclude: - 'spec/palletjack-tool_spec.rb' +Style/Not: + Enabled: false From 4ea97f2d82a14c2d0dc120d051d8de49de5f3f39 Mon Sep 17 00:00:00 2001 From: Karl-Johan Karlsson Date: Thu, 12 Oct 2017 11:30:07 +0200 Subject: [PATCH 5/8] Major refactoring of pj2ipahost Highlights: * Break up #process into several functions * Handle error returns better * Improved documentation * New dependency: hashie ~> 3 --- tools/exe/palletjack2ipahost | 258 +++++++++++++++++---------------- tools/palletjack-tools.gemspec | 1 + 2 files changed, 138 insertions(+), 121 deletions(-) diff --git a/tools/exe/palletjack2ipahost b/tools/exe/palletjack2ipahost index ff02d96..bf8a194 100755 --- a/tools/exe/palletjack2ipahost +++ b/tools/exe/palletjack2ipahost @@ -1,176 +1,192 @@ #!/usr/bin/env ruby -# Create host objects in a FreeIPA or Red Hat IdM instance. +# Create host objects in a FreeIPA instance. +# +# Requires you to first have a valid Kerberos ticket for a FreeIPA +# administrator. Check if you have one with "klist" and if not, get +# one using "kinit ". +# +# Runs for a single system or, if none is given, every system in the +# warehouse with the role freeipa-client. # -# Unless a specific system is specified this will loop trough all systems -# in the warehouse and ensure that a corresponding host object exists. # Hostgroups are synchronized with the warehouse and any group not # specified in the warehouse will be removed. # # Host objects will be created with a randomly generated password. The -# password is saved in the warehouse as system.ipa.otp, which can later be -# exported to the Configuration Management tool. -# -# When specifying the --reinstall parameter the existing host object will -# be removed and a new one created, allowing the machine be be reinstalled. -# -# If the IPA server is configured as authoritative DNS server, an A record -# will be created for the systems primary interface (interface whose -# domain matches with the system). +# password is saved in the warehouse as system.ipa.otp, for later +# export to Salt. # -# TODO: Implement full synchronization of all relevant DNS records -# -# Data model assumptions: -# - Multiple IP addresses in the same domain is equivalent +# If the --reinstall parameter is specified, the existing host object +# will be removed and a new one created, allowing the machine be be +# reinstalled. require 'palletjack/tool' require 'ipa/client' require 'yaml' +require 'hashie' class PalletJack2IPAHost < PalletJack::Tool def parse_options(opts) opts.banner = -"Usage: #{$PROGRAM_NAME} -w -i [-s [--reinstall]] +"Usage: #{$PROGRAM_NAME} -w -i [-s [--reinstall]] [-v] + +Synchronize systems in the warehouse to host objects in an IPA server. -Synchronize the warehouse with the IPA server." +Requres a valid Kerberos ticket for a FreeIPA administrator." - opts.on('-i SERVER', '--ipa-server SERVER', 'IPA server fqdn', String) {|srv| - options[:ipa] = srv - } - opts.on('-s SYSTEM', '--system SYSTEM', 'name of system to synchronize', String) {|system| - options[:system] = system - } - opts.on('-r', '--reinstall', 'reinstall the system (can only be used when a system is specified)') {|reinstall| - required_option :system - options[:reinstall] = reinstall - } + opts.on('-i SERVER', '--ipa-server SERVER', 'IPA server', + String) { |srv| options[:ipa] = srv } + opts.on('-s SYSTEM', '--system SYSTEM', 'system to synchronize', + String) { |system| options[:system] = system } + opts.on('-r', '--reinstall', 'reinstall the named system') { |reinstall| + required_option :system + options[:reinstall] = reinstall + } + opts.on('-v', '--verbose', 'verbose mode') { |verbose| + options[:verbose] = verbose + } required_option :ipa end - def sync_system(system) - # Create the IPA session and load pallet data - ipa = IPA::Client.new(host: options[:ipa]) - box = system['system.ipa'] || {} + def output + # Do all the processing here in #output, since we're always + # modifying state on an external FreeIPA server and there is no + # local preprocessing to do in #process. + + @ipa_boxes = {} - puts "Processing #{system['net.dns.fqdn']}..." + connect_to_freeipa(options[:ipa]) - # Remove the host object - if options[:reinstall] then - del_host = ipa.host_del(hostname: system['net.dns.fqdn'], params: { 'updatedns' => true } ) - if del_host['error'] then - puts " ! ERROR#{del_host['error']['code']} #{del_host['error']['name']}: #{del_host['error']['message']}" - return - else - puts " * Deleted host object" + if options[:system] + system = jack.fetch(kind: 'system', name: options[:system]) + unless system['system.role'].include? 'freeipa-client' + raise "System \"#{options[:system]}\" does not have the role \"freeipa-client\"" + end + sync_system system + else + jack.each(kind: 'system') do |system| + next unless system['system.role'].include? 'freeipa-client' + sync_system system end end - # - # Create a new host object - unless ipa.host_exists? system['net.dns.fqdn'] - # Generate a random password - add_host_params = { 'random' => true } - - ip_address = nil - system.children(kind:'ipv4_interface') do |interface| - # Find an interface in the same domain as the host - if interface['net.dns.domain'] == system['net.dns.domain'] then - ip_address = interface['net.ipv4.address'] - end + @ipa_boxes.each do |system, content| + pallet_box 'system', system, 'ipa' do + { system: { ipa: content } } end + end + end - if ip_address then - add_host_params['ip_address'] = ip_address - else - puts " ! WARNING: No IP Address found for #{system['net.dns.fqdn']}, setting force = true." - add_host_params['force'] = true - end + private - add_host = ipa.host_add(hostname: system['net.dns.fqdn'], params: add_host_params) + def connect_to_freeipa(host) + @ipa = IPA::Client.new(host: host) + end - if add_host['error'] then - puts " ! ERROR#{add_host['error']['code']} #{add_host['error']['name']}: #{add_host['error']['message']}" - return - end + # Update all FreeIPA server state to match the +system+ from the warehouse + # + # If reinstalling, remove the old host object. Then, create a new + # host object and sync hostgroups. + def sync_system(system) + puts "Syncing #{system['system.name']}" if options[:verbose] - puts " * Created host object" - # Update the box - # Note that ['result']['result'] is not acutally a typo, the API - # returns data like that. - box['otp'] = add_host['result']['result']['randompassword'] + box = system['system.ipa'] || {} + + if options[:reinstall] + puts 'Removing host object' if options[:verbose] + del_host = @ipa.host_del(hostname: system['net.dns.fqdn']) + check_api_error(del_host, 'deleting host') end - # Now we know that there is a host object, so query - # the available information. - host_show = ipa.host_show(hostname: system['net.dns.fqdn'], all: true) - host_data = host_show['result']['result'] + unless @ipa.host_exists? system['net.dns.fqdn'] + puts 'Creating host object' if options[:verbose] + box['otp'] = create_host_object(system) + end - # Sync hostgroups to the warehouse. This will first loop over the - # hostgroups in the warehouse and join the groups the system should - # be a member of, then loop trough all of the hostgroups the system - # is a member of and leave any group not specified in the warehouse. - if system['system.ipa.hostgroups'] then - - hostgroups = host_data['memberof_hostgroup'] || [] - system['system.ipa.hostgroups'].each do |group| - unless hostgroups.include? group then - ipa_mod = ipa.api_post(method: 'hostgroup_add_member', item: group, - params: { 'host' => [ system['net.dns.fqdn'] ] }) - - if ipa_mod['error'] then - puts " ! #{ipa_mod['error']['code']} #{ipa_mod['error']['name']}: #{ipa_mod['error']['message']}" - else - puts " * Added to hostgroup #{group}" - end - end - end + sync_hostgroups(system) - hostgroups.each do |group| - unless system['system.ipa.hostgroups'].include? group then - ipa_mod = ipa.api_post(method: 'hostgroup_remove_member', item: group, - params: { 'host' => [ system['net.dns.fqdn'] ] }) - - if ipa_mod['error'] then - puts " ! #{ipa_mod['error']['code']} #{ipa_mod['error']['name']}: #{ipa_mod['error']['message']}" - else - puts " * Removed from hostgroup #{group}" - end - end + @ipa_boxes[system['system.name']] = box + end + + # Create a host object in FreeIPA representing a +system+ from the + # warehouse. The host object may not already exist. + def create_host_object(system) + # Generate a random password + add_host_params = { 'random' => true } + + ip_address = nil + system.children(kind: 'ipv4_interface') do |interface| + # Find an interface in the same domain as the host and set as + # primary IP address in FreeIPA + if interface['net.dns.domain'] == system['net.dns.domain'] + ip_address = interface['net.ipv4.address'] end + end + if ip_address + add_host_params['ip_address'] = ip_address + puts "Using IP address #{ip_address}" if options[:verbose] + else + raise "No IP address in domain #{system['net.dns.domain']} found for #{system['system.name']}" end - @ipa_boxes[system['system.name']] = box + add_host = @ipa.host_add(hostname: system['net.dns.fqdn'], params: add_host_params) + check_api_error(add_host, 'adding host') + add_host['result']['result']['randompassword'] end - def process + # Sync hostgroups from the warehouse to FreeIPA + # + # Join the system to the groups listed in the warehouse, and leave + # all groups not listed in the warehouse. + def sync_hostgroups(system) + host_show = @ipa.host_show(hostname: system['net.dns.fqdn'], all: true) + check_api_error(host_show, 'getting host information') + host_data = host_show['result']['result'] - @ipa_boxes = {} + server_hostgroups = host_data['memberof_hostgroup'] || [] + warehouse_hostgroups = system['system.ipa.hostgroups'] || [] - if options[:system] then - system = jack.fetch(kind: 'system', name: options[:system]) - sync_system system - else - # Make a sync operation of all systems in the database - jack.each(kind: 'system') do |system| - next unless system['system.role'].include? 'ipa-client' - sync_system system - end + warehouse_hostgroups.each do |group| + next if server_hostgroups.include? group + puts "Adding hostgroup #{group}" if options[:verbose] + ipa_mod = @ipa.api_post(method: 'hostgroup_add_member', item: group, + params: { 'host' => [system['net.dns.fqdn']] }) + check_api_error(ipa_mod, 'adding host to group') end + server_hostgroups.each do |group| + next if warehouse_hostgroups.include? group + puts "Removing hostgroup #{group}" if options[:verbose] + ipa_mod = @ipa.api_post(method: 'hostgroup_remove_member', item: group, + params: { 'host' => [system['net.dns.fqdn']] }) + check_api_error(ipa_mod, 'removing host from group') + end end - def output - @ipa_boxes.each do |system, content| - pallet_box 'system', system, 'ipa' do - { system: { ipa: content } } - end + # Check if the return value +ret+ from the +ipa/client+ API is an + # error or a failure, and if so, raise a RuntimeError including the + # string +action+. + def check_api_error(ret, action) + if ret['error'] + raise "Error #{action}: #{ret['error']['code']} #{ret['error']['name']}: #{ret['error']['message']}" end + + # result.failed contains a complicated and variable structure with + # its leaves being lists of the requested operations that failed. + # If all operations were successful, all these lists are empty. + failed = ret['result']['failed'] + return unless failed + failed.extend Hashie::Extensions::DeepLocate + failures = failed.deep_locate lambda(_, value, _) { + value.is_a?(Array) && !value.empty? + } + raise "Failed #{action}: #{ret['result']['failed']}" unless failures.empty? end end -if PalletJack2IPAHost.standalone?(__FILE__) then +if PalletJack2IPAHost.standalone?(__FILE__) PalletJack2IPAHost.run end diff --git a/tools/palletjack-tools.gemspec b/tools/palletjack-tools.gemspec index aa6e18e..4fb51d2 100644 --- a/tools/palletjack-tools.gemspec +++ b/tools/palletjack-tools.gemspec @@ -30,6 +30,7 @@ Gem::Specification.new do |spec| spec.add_runtime_dependency 'ruby-ip', '~> 0.9' spec.add_runtime_dependency 'json', '~> 1.8' spec.add_runtime_dependency 'ipa-ruby', '~> 0.0' + spec.add_runtime_dependency 'hashie', '~> 3' spec.add_development_dependency 'bundler', '~> 1.13' spec.add_development_dependency 'rake', '~> 10.0' From 71104c202c0dcf8d46d2ac866a54caf81974d2ab Mon Sep 17 00:00:00 2001 From: Karl-Johan Karlsson Date: Thu, 12 Oct 2017 11:34:41 +0200 Subject: [PATCH 6/8] RuboCop configuration for pj2ipahost --- .rubocop_todo.yml | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index f09cb93..49d1a73 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -24,6 +24,7 @@ Lint/AssignmentInCondition: # SupportedStylesAlignWith: either, start_of_block, start_of_line Lint/BlockAlignment: Exclude: + - 'tools/exe/palletjack2ipahost' - 'tools/spec/palletjack2kea_spec.rb' # Offense count: 1 @@ -36,6 +37,10 @@ Lint/NonLocalExitFromIterator: Exclude: - 'lib/palletjack/keytransformer.rb' +Lint/ShadowingOuterLocalVariable: + Exclude: + - 'tools/exe/palletjack2ipahost' + # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. @@ -60,6 +65,7 @@ Lint/UselessAccessModifier: Lint/UselessAssignment: Exclude: - 'lib/palletjack/tool.rb' + - 'tools/exe/palletjack2ipahost' - 'tools/exe/palletjack2unbound' # Offense count: 16 @@ -157,6 +163,7 @@ Style/BlockDelimiters: - 'tools/exe/create_ipv4_interface' - 'tools/exe/create_system' - 'tools/exe/dump_pallet' + - 'tools/exe/palletjack2ipahost' - 'tools/exe/palletjack2unbound' - 'tools/lib/palletjack2zones.rb' - 'tools/spec/palletjack2kea_spec.rb' @@ -223,15 +230,8 @@ Style/Documentation: - 'lib/palletjack/pallet/identity.rb' - 'lib/palletjack/tool.rb' - 'lib/traceable.rb' - - 'tools/exe/create_domain' - - 'tools/exe/create_ipv4_interface' - - 'tools/exe/create_system' - - 'tools/exe/dump_pallet' - - 'tools/exe/palletjack2kea' - - 'tools/exe/palletjack2pxelinux' - - 'tools/exe/palletjack2salt' - - 'tools/exe/palletjack2unbound' - - 'tools/lib/palletjack2zones.rb' + - 'tools/exe/*' + - 'tools/lib/*' # Offense count: 1 # Cop supports --auto-correct. @@ -283,6 +283,7 @@ Layout/FirstParameterIndentation: - 'tools/exe/create_domain' - 'tools/exe/create_ipv4_interface' - 'tools/exe/create_system' + - 'tools/exe/palletjack2ipahost' - 'tools/exe/palletjack2kea' - 'tools/exe/palletjack2pxelinux' - 'tools/exe/palletjack2salt' @@ -318,6 +319,7 @@ Style/GlobalVars: Style/GuardClause: Exclude: - 'lib/traceable.rb' + - 'tools/exe/palletjack2ipahost' - 'tools/exe/palletjack2salt' # Offense count: 3 @@ -336,17 +338,7 @@ Style/HashSyntax: # Configuration parameters: MaxLineLength. Style/IfUnlessModifier: Exclude: - - 'tools/exe/create_domain' - - 'tools/exe/create_ipv4_interface' - - 'tools/exe/create_system' - - 'tools/exe/dump_pallet' - - 'tools/exe/palletjack2kea' - - 'tools/exe/palletjack2knot1' - - 'tools/exe/palletjack2knot2' - - 'tools/exe/palletjack2pxelinux' - - 'tools/exe/palletjack2salt' - - 'tools/exe/palletjack2unbound' - - 'tools/lib/palletjack2zones.rb' + - 'tools/exe/*' # Offense count: 16 # Cop supports --auto-correct. @@ -357,6 +349,7 @@ Layout/IndentAssignment: - 'tools/exe/create_domain' - 'tools/exe/create_ipv4_interface' - 'tools/exe/create_system' + - 'tools/exe/palletjack2ipahost' - 'tools/exe/palletjack2kea' - 'tools/exe/palletjack2pxelinux' - 'tools/exe/palletjack2salt' @@ -493,6 +486,7 @@ Layout/SpaceInsideBlockBraces: - 'tools/exe/create_ipv4_interface' - 'tools/exe/create_system' - 'tools/exe/dump_pallet' + - 'tools/exe/palletjack2ipahost' - 'tools/exe/palletjack2kea' - 'tools/exe/palletjack2pxelinux' - 'tools/exe/palletjack2salt' From ebe24710889d092fbdd7f5b350bead0e34a9d9a3 Mon Sep 17 00:00:00 2001 From: Karl-Johan Karlsson Date: Thu, 12 Oct 2017 11:44:04 +0200 Subject: [PATCH 7/8] Document FreeIPA integration in the example warehouse --- examples/warehouse/system/README | 20 ++++++++++++++++++-- examples/warehouse/system/testvm/role.yaml | 4 ++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/examples/warehouse/system/README b/examples/warehouse/system/README index 345813d..c8c9c4c 100644 --- a/examples/warehouse/system/README +++ b/examples/warehouse/system/README @@ -17,14 +17,21 @@ physical machine, a virtual machine or a container. The link "os" points to the operating system this system runs. role.yaml contains, in the key "system.role", a list of roles which -this system skall have, which in the future will be made available to -the configuration management system. +this system skall have, which will be made available to the +configuration management system. It also contains, in the key +"system.ipa.hostgroups", a list of hostgroups the system should join +in a FreeIPA domain. + +ipa.yaml is updated by the palletjack2ipahost tool and contains, in +the key "system.ipa.otp", a one-time password which can be used to +enroll in a FreeIPA domain. Files: system//architecture.yaml system//role.yaml + system//ipa.yaml Links: @@ -46,6 +53,15 @@ role.yaml: - role1 - role2 ... + ipa: + hostgroups: + - group1 + ... + +ipa.yaml: + system: + ipa: + otp: Auto-generated FreeIPA password Transformed values: diff --git a/examples/warehouse/system/testvm/role.yaml b/examples/warehouse/system/testvm/role.yaml index 641489a..ad79c94 100644 --- a/examples/warehouse/system/testvm/role.yaml +++ b/examples/warehouse/system/testvm/role.yaml @@ -2,3 +2,7 @@ system: role: - web-server - ssh-server + - freeipa-client + ipa: + hostgroups: + - clients From b7f7ca0953d731a6785e752abe1764b87041f74b Mon Sep 17 00:00:00 2001 From: Karl-Johan Karlsson Date: Wed, 25 Oct 2017 14:43:20 +0200 Subject: [PATCH 8/8] Correct syntax in error path --- tools/exe/palletjack2ipahost | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/exe/palletjack2ipahost b/tools/exe/palletjack2ipahost index bf8a194..927ddec 100755 --- a/tools/exe/palletjack2ipahost +++ b/tools/exe/palletjack2ipahost @@ -180,7 +180,7 @@ Requres a valid Kerberos ticket for a FreeIPA administrator." failed = ret['result']['failed'] return unless failed failed.extend Hashie::Extensions::DeepLocate - failures = failed.deep_locate lambda(_, value, _) { + failures = failed.deep_locate lambda { |_, value, _| value.is_a?(Array) && !value.empty? } raise "Failed #{action}: #{ret['result']['failed']}" unless failures.empty?