palletjack-tools 0.1.2
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
- checksums.yaml.gz.sig +0 -0
- data/Gemfile +4 -0
- data/Rakefile +6 -0
- data/exe/create_domain +52 -0
- data/exe/create_ipv4_interface +51 -0
- data/exe/create_system +45 -0
- data/exe/dump_pallet +50 -0
- data/exe/palletjack2kea +108 -0
- data/exe/palletjack2knot +170 -0
- data/exe/palletjack2pxelinux +191 -0
- data/exe/palletjack2salt +140 -0
- data/exe/palletjack2unbound +129 -0
- data/palletjack-tools.gemspec +37 -0
- data.tar.gz.sig +0 -0
- metadata +200 -0
- metadata.gz.sig +3 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: 1698a81a8f10fd0ae9b078c077a157d62185388d
         | 
| 4 | 
            +
              data.tar.gz: 8e00cb7db1fe2051f3ebe34e015f7285dff5cc07
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: 41a4e3b367d5f38d5b9514df1e6f8e01571046d46cd5d70ab38f1a4913c44fd9930986af3a4e72b1b9a9978b542a58e26cbe96947c2b39dd15b13505685fd17b
         | 
| 7 | 
            +
              data.tar.gz: 1993440970282599886f25403198dc39c86e8f3128451f6a8d63dbef26f2b71b0e5be1b49051f3372ca09490b0dfbaccf72b49669cfbebafea1eda42d628197f
         | 
    
        checksums.yaml.gz.sig
    ADDED
    
    | Binary file | 
    
        data/Gemfile
    ADDED
    
    
    
        data/Rakefile
    ADDED
    
    
    
        data/exe/create_domain
    ADDED
    
    | @@ -0,0 +1,52 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Create a domain and IPv4 network in a warehouse
         | 
| 4 | 
            +
            #
         | 
| 5 | 
            +
            # Data model assumptions:
         | 
| 6 | 
            +
            # - Each domain corresponds uniquely to one IPv4 network
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            require 'palletjack/tool'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            class CreateDomain < PalletJack::Tool
         | 
| 11 | 
            +
              def parse_options(opts)
         | 
| 12 | 
            +
                opts.banner =
         | 
| 13 | 
            +
            "Usage: #{$PROGRAM_NAME} -w <warehouse> -d <domain> -n <IPv4 network>
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            Create domain and ipv4_network objects in a warehouse"
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                opts.on('-d DOMAIN', '--domain DOMAIN', 'domain name', String) {|domain|
         | 
| 18 | 
            +
                  options[:domain] = domain }
         | 
| 19 | 
            +
                opts.on('-n NETWORK', '--network NETWORK',
         | 
| 20 | 
            +
                        'IPv4 network, in CIDR format', String) {|network|
         | 
| 21 | 
            +
                  options[:network] = network.gsub(/\//, '_') }
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                required_option :domain
         | 
| 24 | 
            +
                required_option :network
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
            end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            CreateDomain.run do
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              # Create the IPv4 Network object
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              pallet_dir 'ipv4_network', :network
         | 
| 33 | 
            +
              pallet_box 'ipv4_network', :network, 'dhcp' do
         | 
| 34 | 
            +
                { net:{ dhcp:{ 'tftp-server' => '', 'boot-file' => '' } } }
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
              pallet_box 'ipv4_network', :network, 'identity' do
         | 
| 37 | 
            +
                { net:{ ipv4:{ gateway:'' },
         | 
| 38 | 
            +
                        dns:{ resolver:[''] } } }
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
              # Create the DNS domain object
         | 
| 42 | 
            +
             | 
| 43 | 
            +
              pallet_dir 'domain', :domain
         | 
| 44 | 
            +
              pallet_box 'domain', :domain, 'dns' do
         | 
| 45 | 
            +
                { net:{ dns:{ ns:[''], 'soa-ns' => '', 'soa-contact' => '' } } }
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
              pallet_box 'domain', :domain, 'services' do
         | 
| 48 | 
            +
                { net:{ service:{ syslog:[{address:'', port:'514', protocol:'udp' } ] } } }
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
              pallet_links 'domain', :domain, 'ipv4_network' => ['ipv4_network', :network]
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            end
         | 
| @@ -0,0 +1,51 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Create a physical and logical IPv4 network interface in a warehouse
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            require 'palletjack/tool'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            class CreateIPv4Interface < PalletJack::Tool
         | 
| 8 | 
            +
              def parse_options(opts)
         | 
| 9 | 
            +
                opts.banner =
         | 
| 10 | 
            +
            "Usage: #{$PROGRAM_NAME} -w <warehouse> -s <system> -d <domain> \\
         | 
| 11 | 
            +
                                -m <MAC address> -i <IPv4 address> -n <IPv4 network>
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            Create phy_nic and ipv4_interface objects in a warehouse"
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                opts.on('-s SYSTEM', '--system SYSTEM', 'system name', String) {|system|
         | 
| 16 | 
            +
                  options[:system] = system }
         | 
| 17 | 
            +
                opts.on('-d DOMAIN', '--domain DOMAIN', 'domain name',String) {|domain|
         | 
| 18 | 
            +
                  options[:domain] = domain }
         | 
| 19 | 
            +
                opts.on('-m MAC', '--mac MAC', 'MAC address', String) {|mac|
         | 
| 20 | 
            +
                  options[:mac] = mac }
         | 
| 21 | 
            +
                opts.on('-i ADDR', '--ipv4 ADDR', 'IPv4 address', String) {|addr|
         | 
| 22 | 
            +
                  options[:addr] = addr }
         | 
| 23 | 
            +
                opts.on('-n NETWORK', '--network NETWORK', 'IPv4 network, in CIDR format',
         | 
| 24 | 
            +
                        String) {|network| options[:network] = network.gsub(/\//, '_') }
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                required_option :system
         | 
| 27 | 
            +
                required_option :domain
         | 
| 28 | 
            +
                required_option :mac
         | 
| 29 | 
            +
                required_option :addr
         | 
| 30 | 
            +
                required_option :network
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
            end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            CreateIPv4Interface.run do
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              # Create Physical NIC object
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              pallet_dir 'phy_nic', :mac
         | 
| 39 | 
            +
              pallet_box 'phy_nic', :mac, 'identity' do
         | 
| 40 | 
            +
                { net:{ layer2:{ name:'' } } }
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
              # Create IPv4 Interface object
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              pallet_dir 'ipv4_interface', :addr
         | 
| 46 | 
            +
              pallet_links 'ipv4_interface', :addr,
         | 
| 47 | 
            +
                'ipv4_network' => ['ipv4_network', :network],
         | 
| 48 | 
            +
                'domain' => ['domain', :domain],
         | 
| 49 | 
            +
                'phy_nic' => ['phy_nic', :mac],
         | 
| 50 | 
            +
                'system' => ['system', :system]
         | 
| 51 | 
            +
            end
         | 
    
        data/exe/create_system
    ADDED
    
    | @@ -0,0 +1,45 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Create a system in a warehouse
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            require 'palletjack/tool'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            class CreateSystem < PalletJack::Tool
         | 
| 8 | 
            +
              def parse_options(opts)
         | 
| 9 | 
            +
                opts.banner =
         | 
| 10 | 
            +
            "Usage: #{$PROGRAM_NAME} -w <warehouse> -s <system> -d <domain> \\
         | 
| 11 | 
            +
                                  [ -o <os> | -n <netinstall> ]
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            Create system objects in a warehouse"
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                opts.on('-s SYSTEM', '--system SYSTEM', 'system name', String) {|system|
         | 
| 16 | 
            +
                  options[:system] = system }
         | 
| 17 | 
            +
                opts.on('-d DOMAIN', '--domain DOMAIN', 'domain name', String) {|domain|
         | 
| 18 | 
            +
                  options[:domain] = domain }
         | 
| 19 | 
            +
                opts.on('-o OS', '--os OS', 'operating system name', String) {|os|
         | 
| 20 | 
            +
                  options[:os] = os }
         | 
| 21 | 
            +
                opts.on('-n OS', '--netinstall NETINSTALL', 'network installation profile',
         | 
| 22 | 
            +
                        String) {|netinstall| options[:netinstall] = netinstall }
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                required_option :system
         | 
| 25 | 
            +
                required_option :domain
         | 
| 26 | 
            +
                required_option :os, :netinstall
         | 
| 27 | 
            +
                exclusive_options :os, :netinstall
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
            end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            CreateSystem.run do
         | 
| 32 | 
            +
              pallet_dir 'system', :system
         | 
| 33 | 
            +
              pallet_box 'system', :system, 'role' do
         | 
| 34 | 
            +
                { system:{ role:[ '' ] } }
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
              pallet_links 'system', :system, 'domain' => ['domain', :domain]
         | 
| 37 | 
            +
              pallet_links 'system', :system,
         | 
| 38 | 
            +
                if options[:os]
         | 
| 39 | 
            +
                  { 'os' => ['os', :os], 'netinstall' => [] }
         | 
| 40 | 
            +
                elsif options[:netinstall]
         | 
| 41 | 
            +
                  { 'os' => [], 'netinstall' => ['netinstall', :netinstall] }
         | 
| 42 | 
            +
                else
         | 
| 43 | 
            +
                  abort('Both --os and --netinstall were nil; should not happen!')
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
            end
         | 
    
        data/exe/dump_pallet
    ADDED
    
    | @@ -0,0 +1,50 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Simple script for dumping a specified pallet as YAML.
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            require 'palletjack/tool'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            class DumpPallet < PalletJack::Tool
         | 
| 8 | 
            +
              def parse_options(parser)
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                parser.banner = "Usage: #{$PROGRAM_NAME} [options] [<name> ...]
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            Dump the YAML representation of all named pallets in a Palletjack warehouse
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            If a name is specified, dump only that pallet. Otherwise, dump all
         | 
| 15 | 
            +
            pallets of the specified type."
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                # The preset value for options[:type] will be overridden when
         | 
| 18 | 
            +
                # the parse! method of the parser is eventually run and finds
         | 
| 19 | 
            +
                # a --type option. Keep declarations in that order for clarity.
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                options[:type]='system'
         | 
| 22 | 
            +
                parser.on('-t TYPE', '--type TYPE',
         | 
| 23 | 
            +
                          'type (default "system")',
         | 
| 24 | 
            +
                          String) {|type| options[:type] = type }
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              # Dump the yaml contents of all pallets given to STDOUT
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              def dump_pallets(pallets)
         | 
| 30 | 
            +
                abort('No matching pallets found.') if pallets.empty?
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                pallets.each do |p|
         | 
| 33 | 
            +
                  puts "---"
         | 
| 34 | 
            +
                  puts "# #{p.kind}: #{p.name}"
         | 
| 35 | 
            +
                  puts p.to_yaml
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
            end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            DumpPallet.run do
         | 
| 41 | 
            +
              # Since the Tool framework uses destructive opts.parse!, all options
         | 
| 42 | 
            +
              # were removed from argv, leaving just the names to search for.
         | 
| 43 | 
            +
              if argv.empty?
         | 
| 44 | 
            +
                dump_pallets jack[kind: options[:type]]
         | 
| 45 | 
            +
              else
         | 
| 46 | 
            +
                argv.each do |name|
         | 
| 47 | 
            +
                  dump_pallets jack[kind: options[:type], name: name]
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
              end
         | 
| 50 | 
            +
            end
         | 
    
        data/exe/palletjack2kea
    ADDED
    
    | @@ -0,0 +1,108 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Write configuration for the Kea DHCP server from a Palletjack warehouse
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            require 'palletjack/tool'
         | 
| 6 | 
            +
            require 'json'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            class PalletJack2Kea < PalletJack::Tool
         | 
| 9 | 
            +
              def parse_options(opts)
         | 
| 10 | 
            +
                opts.banner =
         | 
| 11 | 
            +
            "Usage: #{$PROGRAM_NAME} [options] <name> ...
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            Write configuration for the Kea DHCP server from a Palletjack warehouse"
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                opts.on('-s SERVICE', '--service SERVICE',
         | 
| 16 | 
            +
                        'service name for global configuration',
         | 
| 17 | 
            +
                        String) {|service| options[:service] = service }
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                required_option :service
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              def dhcp4_option(code, name, data)
         | 
| 23 | 
            +
                {
         | 
| 24 | 
            +
                  'code' => code,
         | 
| 25 | 
            +
                  'name' => name,
         | 
| 26 | 
            +
                  'space' => 'dhcp4',
         | 
| 27 | 
            +
                  'csv-format' => true,
         | 
| 28 | 
            +
                  'data' => data
         | 
| 29 | 
            +
                }
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              # The internal representation of the generated configuration, ready
         | 
| 33 | 
            +
              # to be tested or printed.
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              attr_reader :kea_config
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              def process
         | 
| 38 | 
            +
                @kea_config = { 'Dhcp4' => {} }
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                jack.each(kind:'service', name:options[:service]) do |service_config|
         | 
| 41 | 
            +
                  @kea_config['Dhcp4'] = service_config['service.kea_v4']
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                @kea_config['Dhcp4']['subnet4'] = []
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                jack.each(kind:'ipv4_network') do |net|
         | 
| 47 | 
            +
                  net_config = {'subnet' => net['net.ipv4.cidr'],
         | 
| 48 | 
            +
                    'reservations' => [],
         | 
| 49 | 
            +
                    'option-data' => []}
         | 
| 50 | 
            +
                  if net['net.ipv4.gateway']
         | 
| 51 | 
            +
                    net_config['option-data'] <<
         | 
| 52 | 
            +
                      dhcp4_option(3, 'routers', net['net.ipv4.gateway'])
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  if net['net.dns.resolver']
         | 
| 56 | 
            +
                    resolvers = ''
         | 
| 57 | 
            +
                    net['net.dns.resolver'].each do |resolver|
         | 
| 58 | 
            +
                      resolvers << resolver << ', '
         | 
| 59 | 
            +
                    end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                    net_config['option-data'] <<
         | 
| 62 | 
            +
                      dhcp4_option(6, 'domain-name-servers', resolvers.chomp(', '))
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  if net['net.dhcp.tftp-server']
         | 
| 66 | 
            +
                    net_config['option-data'] <<
         | 
| 67 | 
            +
                      dhcp4_option(66, 'tftp-server-name', net['net.dhcp.tftp-server'])
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                    # Option 66/tftp-server-name is the standard way of sending a
         | 
| 70 | 
            +
                    # TFTP server address, but some DHCP clients still want it in
         | 
| 71 | 
            +
                    # the next-server field.
         | 
| 72 | 
            +
                    net_config['next-server'] = net['net.dhcp.tftp-server']
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  if net['net.dhcp.boot-file']
         | 
| 76 | 
            +
                    net_config['option-data'] <<
         | 
| 77 | 
            +
                      dhcp4_option(67, 'boot-file-name', net['net.dhcp.boot-file'])
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                  net.children(kind:'ipv4_interface',
         | 
| 81 | 
            +
                               none?:{ 'net.layer2.address' => nil }
         | 
| 82 | 
            +
                              ) do |interface|
         | 
| 83 | 
            +
                    net_config['reservations'] << {
         | 
| 84 | 
            +
                      'hw-address' => interface['net.layer2.address'],
         | 
| 85 | 
            +
                      'ip-address' => interface['net.ipv4.address'],
         | 
| 86 | 
            +
                      'hostname' => interface['net.dns.fqdn'],
         | 
| 87 | 
            +
                      'option-data' =>
         | 
| 88 | 
            +
                      [
         | 
| 89 | 
            +
                        dhcp4_option(15, 'domain-name',
         | 
| 90 | 
            +
                                     interface.parents(kind:'system').first['net.dns.domain'])
         | 
| 91 | 
            +
                      ]
         | 
| 92 | 
            +
                    }
         | 
| 93 | 
            +
                  end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                  @kea_config['Dhcp4']['subnet4'] << net_config
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
              end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
              def output
         | 
| 100 | 
            +
                puts git_header('palletjack2kea')
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                jj @kea_config
         | 
| 103 | 
            +
              end
         | 
| 104 | 
            +
            end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
            if PalletJack2Kea.standalone?(__FILE__)
         | 
| 107 | 
            +
              PalletJack2Kea.run
         | 
| 108 | 
            +
            end
         | 
    
        data/exe/palletjack2knot
    ADDED
    
    | @@ -0,0 +1,170 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Write DNS server zone file from a Palletjack warehouse
         | 
| 4 | 
            +
            #
         | 
| 5 | 
            +
            # Data model assumptions:
         | 
| 6 | 
            +
            # - Each domain corresponds uniquely to one IPv4 network
         | 
| 7 | 
            +
            #
         | 
| 8 | 
            +
            # The YAML key "net.dns.alias" is used to create CNAME aliases for
         | 
| 9 | 
            +
            # each interface. This means that if a "system" object specifies
         | 
| 10 | 
            +
            # "net.dns.alias", each of its interfaces will get that alias in its
         | 
| 11 | 
            +
            # own domain. Aliases explicitly specified on a single interface will
         | 
| 12 | 
            +
            # override this.
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            require 'palletjack/tool'
         | 
| 15 | 
            +
            require 'dns/zone'
         | 
| 16 | 
            +
            require 'ip'
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            class PalletJack2Knot < PalletJack::Tool
         | 
| 19 | 
            +
              def parse_options(opts)
         | 
| 20 | 
            +
                opts.banner =
         | 
| 21 | 
            +
            "Usage: #{$PROGRAM_NAME} -w <warehouse> -o <output directory>
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            Write DNS server zone files from a Palletjack warehouse"
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                opts.on('-o DIR', '--output DIR', 'output directory', String) {|dir|
         | 
| 26 | 
            +
                  options[:output] = dir
         | 
| 27 | 
            +
                  options[:zone_dir] = config_path(:output, "zones")
         | 
| 28 | 
            +
                }
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                required_option :output
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              def zone_config(zone)
         | 
| 34 | 
            +
                "
         | 
| 35 | 
            +
            #{zone} {
         | 
| 36 | 
            +
              file \"zones/#{zone}.zone\";
         | 
| 37 | 
            +
            }
         | 
| 38 | 
            +
            "
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
            end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            PalletJack2Knot.run do
         | 
| 43 | 
            +
              config_dir :zone_dir
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              # Use Unix timestamp as serial number, and get it once so all zones get
         | 
| 46 | 
            +
              # the same one
         | 
| 47 | 
            +
              serial = Time.now.utc.to_i
         | 
| 48 | 
            +
             | 
| 49 | 
            +
              config_file :output, 'zones.conf' do |conf_file|
         | 
| 50 | 
            +
                conf_file << git_header('palletjack2knot')
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                jack.each(kind:'domain') do |domain|
         | 
| 53 | 
            +
                  absolute_domain_name = "#{domain['net.dns.domain']}."
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  zone = DNS::Zone.new
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  zone.origin = absolute_domain_name
         | 
| 58 | 
            +
                  zone.ttl = domain['net.dns.ttl']
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  zone.soa.serial = serial
         | 
| 61 | 
            +
                  zone.soa.label = absolute_domain_name
         | 
| 62 | 
            +
                  zone.soa.nameserver = domain['net.dns.soa-ns']
         | 
| 63 | 
            +
                  zone.soa.email = "#{domain['net.dns.soa-contact']}.".sub('@', '.')
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  if domain['net.dns.mx']
         | 
| 66 | 
            +
                    domain['net.dns.mx'].each do |server|
         | 
| 67 | 
            +
                      mx = DNS::Zone::RR::MX.new
         | 
| 68 | 
            +
                      mx.label = absolute_domain_name
         | 
| 69 | 
            +
                      mx.priority = server['priority']
         | 
| 70 | 
            +
                      mx.exchange = server['server']
         | 
| 71 | 
            +
                      zone.records << mx
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  domain['net.dns.ns'].each do |address|
         | 
| 76 | 
            +
                    ns = DNS::Zone::RR::NS.new
         | 
| 77 | 
            +
                    ns.label = absolute_domain_name
         | 
| 78 | 
            +
                    ns.nameserver = address
         | 
| 79 | 
            +
                    zone.records << ns
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  if domain['net.dns.cname']
         | 
| 83 | 
            +
                    domain['net.dns.cname'].each do |name, target|
         | 
| 84 | 
            +
                      cname = DNS::Zone::RR::CNAME.new
         | 
| 85 | 
            +
                      cname.label = name
         | 
| 86 | 
            +
                      cname.domainname = target
         | 
| 87 | 
            +
                      zone.records << cname
         | 
| 88 | 
            +
                    end
         | 
| 89 | 
            +
                  end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                  if domain['net.dns.srv']
         | 
| 92 | 
            +
                    domain['net.dns.srv'].each do |service|
         | 
| 93 | 
            +
                      srv = DNS::Zone::RR::SRV.new
         | 
| 94 | 
            +
                      srv.label = "_#{service['service']}._#{service['protocol']}"
         | 
| 95 | 
            +
                      srv.target = service['target']
         | 
| 96 | 
            +
                      srv.port = service['port']
         | 
| 97 | 
            +
                      service['priority'] ||= 0
         | 
| 98 | 
            +
                      srv.priority = service['priority']
         | 
| 99 | 
            +
                      service['weight'] ||= 0
         | 
| 100 | 
            +
                      srv.weight = service['weight']
         | 
| 101 | 
            +
                      zone.records << srv
         | 
| 102 | 
            +
                    end
         | 
| 103 | 
            +
                  end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                  domain.children(kind:'ipv4_interface') do |interface|
         | 
| 106 | 
            +
                    a = DNS::Zone::RR::A.new
         | 
| 107 | 
            +
                    a.label = interface['net.dns.name']
         | 
| 108 | 
            +
                    a.address = interface['net.ipv4.address']
         | 
| 109 | 
            +
                    zone.records << a
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                    if interface['net.dns.alias']
         | 
| 112 | 
            +
                      interface['net.dns.alias'].each do |label|
         | 
| 113 | 
            +
                        cname = DNS::Zone::RR::CNAME.new
         | 
| 114 | 
            +
                        cname.label = label
         | 
| 115 | 
            +
                        cname.domainname = interface['net.dns.name']
         | 
| 116 | 
            +
                        zone.records << cname
         | 
| 117 | 
            +
                      end
         | 
| 118 | 
            +
                    end
         | 
| 119 | 
            +
                  end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                  config_file :zone_dir, "#{domain['net.dns.domain']}.zone" do |zonefile|
         | 
| 122 | 
            +
                    zonefile << git_header('palletjack2knot', comment_char: ';')
         | 
| 123 | 
            +
                    zonefile << zone.dump_pretty
         | 
| 124 | 
            +
                  end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                  conf_file << zone_config(domain['net.dns.domain'])
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                  next unless domain['net.ipv4.cidr']
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                  # Assume all delegations happen on octet boundaries for now.
         | 
| 131 | 
            +
                  # TODO: RFC 2317 classless in-addr.arpa delegation
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                  reverse_zone = DNS::Zone.new
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                  ip_net = IP.new(domain['net.ipv4.cidr'])
         | 
| 136 | 
            +
                  absolute_reverse_zone_name = ip_net.to_arpa
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                  prefix_octets, _ = domain['net.ipv4.prefixlen'].to_i.divmod(8)
         | 
| 139 | 
            +
                  reverse_zone.origin = absolute_reverse_zone_name.split('.')[-(2 + prefix_octets) .. 5].join('.')
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                  reverse_zone.ttl = domain['net.dns.ttl']
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                  reverse_zone.soa.serial = serial
         | 
| 144 | 
            +
                  reverse_zone.soa.label = "#{reverse_zone.origin}."
         | 
| 145 | 
            +
                  reverse_zone.soa.nameserver = domain['net.dns.soa-ns']
         | 
| 146 | 
            +
                  reverse_zone.soa.email = "#{domain['net.dns.soa-contact']}.".sub('@', '.')
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                  domain['net.dns.ns'].each do |address|
         | 
| 149 | 
            +
                    ns = DNS::Zone::RR::NS.new
         | 
| 150 | 
            +
                    ns.label = "#{reverse_zone.origin}."
         | 
| 151 | 
            +
                    ns.nameserver = address
         | 
| 152 | 
            +
                    reverse_zone.records << ns
         | 
| 153 | 
            +
                  end
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                  domain.children(kind:'ipv4_interface') do |interface|
         | 
| 156 | 
            +
                    ptr = DNS::Zone::RR::PTR.new
         | 
| 157 | 
            +
                    ptr.label = IP.new(interface['net.ipv4.address']).to_arpa
         | 
| 158 | 
            +
                    ptr.name = "#{interface['net.dns.fqdn']}."
         | 
| 159 | 
            +
                    reverse_zone.records << ptr
         | 
| 160 | 
            +
                  end
         | 
| 161 | 
            +
             | 
| 162 | 
            +
                  config_file :zone_dir, "#{reverse_zone.origin}.zone" do |zonefile|
         | 
| 163 | 
            +
                    zonefile << git_header('palletjack2knot', comment_char: ';')
         | 
| 164 | 
            +
                    zonefile << reverse_zone.dump_pretty
         | 
| 165 | 
            +
                  end
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                  conf_file << zone_config(reverse_zone.origin)
         | 
| 168 | 
            +
                end
         | 
| 169 | 
            +
              end
         | 
| 170 | 
            +
            end
         | 
| @@ -0,0 +1,191 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Write PXELINUX boot menus from a Palletjack warehouse
         | 
| 4 | 
            +
            #
         | 
| 5 | 
            +
            # Data model assumptions:
         | 
| 6 | 
            +
            #
         | 
| 7 | 
            +
            # Each +os+ object will contain the keys:
         | 
| 8 | 
            +
            #   host:
         | 
| 9 | 
            +
            #     kickstart:
         | 
| 10 | 
            +
            #       baseurl: <URL to OS installation>
         | 
| 11 | 
            +
            #     pxelinux:
         | 
| 12 | 
            +
            #       config: <basename of pxelinux menu file w/o .menu>
         | 
| 13 | 
            +
            #
         | 
| 14 | 
            +
            # Each +netinstall+ object will contain the keys:
         | 
| 15 | 
            +
            #   host:
         | 
| 16 | 
            +
            #     kickstart:
         | 
| 17 | 
            +
            #       label: <human-friendly label for variant>
         | 
| 18 | 
            +
            #       ksurl: ["none"|<URL to kickstart file>]
         | 
| 19 | 
            +
            #     pxelinux:
         | 
| 20 | 
            +
            #       config: <basename of pxelinux menu file w/o .menu>
         | 
| 21 | 
            +
            #       kernel: <tftp path of pxelinux kernel>
         | 
| 22 | 
            +
            #       append: <pxelinux kernel parameters>
         | 
| 23 | 
            +
            #
         | 
| 24 | 
            +
            # PXELINUX directory tree:
         | 
| 25 | 
            +
            #
         | 
| 26 | 
            +
            # /var/lib/tftpboot/
         | 
| 27 | 
            +
            #  |- pxelinux.cfg
         | 
| 28 | 
            +
            #  |   |- default -> ../config/default
         | 
| 29 | 
            +
            #  |   |- 01-<MAC address> -> ../config/<OS>-<Variant>.menu
         | 
| 30 | 
            +
            #  |   ...
         | 
| 31 | 
            +
            #  |- config
         | 
| 32 | 
            +
            #  |   |- default
         | 
| 33 | 
            +
            #  |   |- linux.menu
         | 
| 34 | 
            +
            #  |   |- <OS>.menu
         | 
| 35 | 
            +
            #  |   |- <OS>-<Variant>.menu
         | 
| 36 | 
            +
            #  |   ...
         | 
| 37 | 
            +
            #  \- boot
         | 
| 38 | 
            +
            #      |- <OS>
         | 
| 39 | 
            +
            #      |   |- vmlinuz
         | 
| 40 | 
            +
            #      |   |- initrd
         | 
| 41 | 
            +
            #      |   ...
         | 
| 42 | 
            +
            #      ...
         | 
| 43 | 
            +
            #
         | 
| 44 | 
            +
            # Point this script's output directory at /var/lib/tftpboot/, and it
         | 
| 45 | 
            +
            # will create the MAC address symlinks, and menu tree for linux
         | 
| 46 | 
            +
            # installations.
         | 
| 47 | 
            +
            #
         | 
| 48 | 
            +
            # The boot/... and config/default needs to be populated by other means.
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            require 'palletjack/tool'
         | 
| 51 | 
            +
            require 'fileutils'
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            class PalletJack2PXELinux < PalletJack::Tool
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              def parse_options(opts)
         | 
| 56 | 
            +
                opts.banner =
         | 
| 57 | 
            +
            "Usage: #{$PROGRAM_NAME} -w <warehouse> -o <output directory>
         | 
| 58 | 
            +
             | 
| 59 | 
            +
            Write PXELINUX boot configuration files from a Palletjack warehouse"
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                opts.on('-o DIR', '--output DIR', 'output directory (tftpboot/)',
         | 
| 62 | 
            +
                        String) {|dir| options[:output] = dir }
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                required_option :output
         | 
| 65 | 
            +
              end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
              # Generate pxe menus for each OS configuration
         | 
| 68 | 
            +
             | 
| 69 | 
            +
              def pxemenu_for_netinstall(netinstall)
         | 
| 70 | 
            +
                config=netinstall['host.pxelinux.config']
         | 
| 71 | 
            +
                os_label=netinstall['system.os'].gsub(/-/, ' ')
         | 
| 72 | 
            +
                ks_label=(netinstall['host.kickstart.label'] ||
         | 
| 73 | 
            +
                          netinstall['host.kickstart.variant'] )
         | 
| 74 | 
            +
                kernel=netinstall['host.pxelinux.kernel']
         | 
| 75 | 
            +
                append=netinstall['host.pxelinux.append']
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                config_file :output, 'config', "#{config}.menu" do |menufile|
         | 
| 78 | 
            +
                  menufile << git_header('palletjack2pxelinux')
         | 
| 79 | 
            +
                  menufile << "
         | 
| 80 | 
            +
            UI menu.c32
         | 
| 81 | 
            +
            PROMPT 0
         | 
| 82 | 
            +
            MENU INCLUDE /config/graphics.conf
         | 
| 83 | 
            +
            MENU AUTOBOOT Installing #{os_label} (#{ks_label}) in # seconds
         | 
| 84 | 
            +
            TIMEOUT 100
         | 
| 85 | 
            +
             | 
| 86 | 
            +
            MENU TITLE Install #{os_label} (#{ks_label})
         | 
| 87 | 
            +
             | 
| 88 | 
            +
            LABEL #{config}
         | 
| 89 | 
            +
              MENU LABEL ^Install #{os_label} (#{ks_label})
         | 
| 90 | 
            +
              MENU DEFAULT
         | 
| 91 | 
            +
              KERNEL #{kernel}
         | 
| 92 | 
            +
              APPEND #{append}
         | 
| 93 | 
            +
             | 
| 94 | 
            +
            LABEL MainMenu
         | 
| 95 | 
            +
              MENU LABEL ^Main Menu
         | 
| 96 | 
            +
              KERNEL menu.c32
         | 
| 97 | 
            +
              APPEND /config/default
         | 
| 98 | 
            +
            "
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
              end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
              # Generate pxe menus for each OS version
         | 
| 103 | 
            +
             | 
| 104 | 
            +
              def pxemenu_for_operating_system(os)
         | 
| 105 | 
            +
                config=os['host.pxelinux.config']
         | 
| 106 | 
            +
                os_label=config.gsub(/-/, ' ')
         | 
| 107 | 
            +
                kernel=os['host.pxelinux.kernel']
         | 
| 108 | 
            +
                append=os['host.pxelinux.append']
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                config_file :output, 'config', "#{config}.menu" do |menufile|
         | 
| 111 | 
            +
                  menufile << git_header('palletjack2pxelinux')
         | 
| 112 | 
            +
                  menufile << "
         | 
| 113 | 
            +
            UI menu.c32
         | 
| 114 | 
            +
            PROMPT 0
         | 
| 115 | 
            +
            MENU INCLUDE /config/graphics.conf
         | 
| 116 | 
            +
             | 
| 117 | 
            +
            MENU TITLE #{os_label} Installation Menu
         | 
| 118 | 
            +
            "
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                  os.children(kind:'netinstall') do |netinstall|
         | 
| 121 | 
            +
                    config=netinstall['host.pxelinux.config']
         | 
| 122 | 
            +
                    ks_label=(netinstall['host.kickstart.label'] ||
         | 
| 123 | 
            +
                              netinstall['host.kickstart.variant'] )
         | 
| 124 | 
            +
                    kernel=netinstall['host.pxelinux.kernel']
         | 
| 125 | 
            +
                    append=netinstall['host.pxelinux.append']
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                    pxemenu_for_netinstall(netinstall)
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                    menufile << "
         | 
| 130 | 
            +
            LABEL #{config}
         | 
| 131 | 
            +
              MENU LABEL Install #{os_label} (#{ks_label})
         | 
| 132 | 
            +
              KERNEL menu.c32
         | 
| 133 | 
            +
              APPEND /config/#{config}.menu
         | 
| 134 | 
            +
            "
         | 
| 135 | 
            +
                  end
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                  menufile << "
         | 
| 138 | 
            +
            LABEL MainMenu
         | 
| 139 | 
            +
              MENU LABEL ^Main Menu
         | 
| 140 | 
            +
              KERNEL menu.c32
         | 
| 141 | 
            +
              APPEND /config/default
         | 
| 142 | 
            +
            "
         | 
| 143 | 
            +
                end
         | 
| 144 | 
            +
              end
         | 
| 145 | 
            +
            end
         | 
| 146 | 
            +
             | 
| 147 | 
            +
            PalletJack2PXELinux.run do
         | 
| 148 | 
            +
              config_dir :output, 'config'
         | 
| 149 | 
            +
              config_dir :output, 'pxelinux.cfg'
         | 
| 150 | 
            +
             | 
| 151 | 
            +
              config_file :output, 'config', 'linux.menu' do |menufile|
         | 
| 152 | 
            +
                menufile << git_header('palletjack2pxelinux')
         | 
| 153 | 
            +
                menufile << "
         | 
| 154 | 
            +
            UI menu.c32
         | 
| 155 | 
            +
            PROMPT 0
         | 
| 156 | 
            +
            MENU INCLUDE /config/graphics.conf
         | 
| 157 | 
            +
             | 
| 158 | 
            +
            MENU TITLE Linux Installation Menu
         | 
| 159 | 
            +
            "
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                jack.each(kind:'os') do |os|
         | 
| 162 | 
            +
                  next unless config=os['host.pxelinux.config']
         | 
| 163 | 
            +
             | 
| 164 | 
            +
                  label=config.gsub(/-/, ' ')
         | 
| 165 | 
            +
             | 
| 166 | 
            +
                  pxemenu_for_operating_system(os)
         | 
| 167 | 
            +
             | 
| 168 | 
            +
                  menufile << "
         | 
| 169 | 
            +
            LABEL #{config}
         | 
| 170 | 
            +
              MENU LABEL Install #{label}
         | 
| 171 | 
            +
              KERNEL menu.c32
         | 
| 172 | 
            +
              APPEND /config/#{config}.menu
         | 
| 173 | 
            +
            "
         | 
| 174 | 
            +
                end
         | 
| 175 | 
            +
              end
         | 
| 176 | 
            +
             | 
| 177 | 
            +
              # Generate pxe default menu links for each system
         | 
| 178 | 
            +
             | 
| 179 | 
            +
              jack.each(kind:'system') do |system|
         | 
| 180 | 
            +
                system.children(kind:'ipv4_interface') do |nic|
         | 
| 181 | 
            +
                  if system['host.pxelinux.config']
         | 
| 182 | 
            +
                    menuname = "#{system['host.pxelinux.config']}.menu"
         | 
| 183 | 
            +
                    linkname = "01-#{nic['net.layer2.address'].gsub(':', '-').downcase}"
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                    FileUtils.ln_s(config_path('..', 'config', menuname),
         | 
| 186 | 
            +
                                   config_path(:output, 'pxelinux.cfg', linkname),
         | 
| 187 | 
            +
                                   :force => true)
         | 
| 188 | 
            +
                  end
         | 
| 189 | 
            +
                end
         | 
| 190 | 
            +
              end
         | 
| 191 | 
            +
            end
         | 
    
        data/exe/palletjack2salt
    ADDED
    
    | @@ -0,0 +1,140 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Write YAML files containing Salt pillar data.
         | 
| 4 | 
            +
            #
         | 
| 5 | 
            +
            # Intended to be run from a Git post-update hook or similar, since
         | 
| 6 | 
            +
            # running the entire Pallet Jack infrastructure once per minion for
         | 
| 7 | 
            +
            # every pillar refresh is a bit excessive, writing YAML files which
         | 
| 8 | 
            +
            # will be read by a Salt pillar, in one of two modes of operation:
         | 
| 9 | 
            +
            #
         | 
| 10 | 
            +
            # 1. Global pillar data
         | 
| 11 | 
            +
            #
         | 
| 12 | 
            +
            #    Intended to be included in an ordinary Salt pillar root.
         | 
| 13 | 
            +
            #
         | 
| 14 | 
            +
            #    Example Salt master configuration:
         | 
| 15 | 
            +
            #
         | 
| 16 | 
            +
            #    pillar_roots:
         | 
| 17 | 
            +
            #      base:
         | 
| 18 | 
            +
            #        - /etc/salt/pillar
         | 
| 19 | 
            +
            #
         | 
| 20 | 
            +
            #    Example /etc/salt/pillar/top.sls:
         | 
| 21 | 
            +
            #
         | 
| 22 | 
            +
            #    base:
         | 
| 23 | 
            +
            #      '*':
         | 
| 24 | 
            +
            #        - palletjack.global
         | 
| 25 | 
            +
            #
         | 
| 26 | 
            +
            # 2. Per-minion pillar data
         | 
| 27 | 
            +
            #
         | 
| 28 | 
            +
            #    Intended to be read by the `palletjack_yaml_file` external pillar
         | 
| 29 | 
            +
            #    (which you will have to install manually).
         | 
| 30 | 
            +
            #
         | 
| 31 | 
            +
            #    Example Salt master configuration:
         | 
| 32 | 
            +
            #
         | 
| 33 | 
            +
            #    ext_pillar:
         | 
| 34 | 
            +
            #      - palletjack_yaml_file: /var/cache/palletjack/{saltenv}/{minion}.yaml
         | 
| 35 | 
            +
            #
         | 
| 36 | 
            +
            #    Data model assumptions:
         | 
| 37 | 
            +
            #      - Salt minion ID is FQDN
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            require 'palletjack/tool'
         | 
| 40 | 
            +
            require 'yaml'
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            class PalletJack2Salt < PalletJack::Tool
         | 
| 43 | 
            +
              def parse_options(opts)
         | 
| 44 | 
            +
                opts.banner =
         | 
| 45 | 
            +
            "Usage: #{$PROGRAM_NAME} -w <warehouse> [ -g <dir> ] [ -m <dir> ]
         | 
| 46 | 
            +
             | 
| 47 | 
            +
            Write Salt pillar data from a Palletjack warehouse."
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                opts.on('-g DIR', '--global DIR', 'global pillar directory',
         | 
| 50 | 
            +
                        String ) {|dir| options[:global_pillar] = dir }
         | 
| 51 | 
            +
                opts.on('-m DIR', '--minion DIR', 'per-minion pillar directory',
         | 
| 52 | 
            +
                        String) {|dir| options[:minion_pillar] = dir }
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                required_option :global_pillar, :minion_pillar
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
              # Helpers for generating global pillars
         | 
| 58 | 
            +
             | 
| 59 | 
            +
              def process_global_pillar
         | 
| 60 | 
            +
                result = {}
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                config['pillar.global.each-pallet'].each do |dst_key, kind|
         | 
| 63 | 
            +
                  result[dst_key] ||= {}
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  jack.each(kind: kind) do |pallet|
         | 
| 66 | 
            +
                    result[dst_key][pallet.name] = pallet.to_hash.except('pallet')
         | 
| 67 | 
            +
                  end
         | 
| 68 | 
            +
                end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                result
         | 
| 71 | 
            +
              end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
              # Helpers for generating per-minion pillars
         | 
| 74 | 
            +
             | 
| 75 | 
            +
              def ipv4_interfaces(system)
         | 
| 76 | 
            +
                result = Hash.new
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                system.children(kind:'ipv4_interface') do |interface|
         | 
| 79 | 
            +
                  result[interface['net.layer2.name']] = {
         | 
| 80 | 
            +
                    interface['net.ipv4.address'] =>
         | 
| 81 | 
            +
                    interface.filter('net.ipv4', 'net.layer2').to_hash
         | 
| 82 | 
            +
                  }
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                result
         | 
| 86 | 
            +
              end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
              def process_minion_pillars
         | 
| 89 | 
            +
                result = {}
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                jack.each(kind:'system') do |system|
         | 
| 92 | 
            +
                  yaml_output = {}
         | 
| 93 | 
            +
                  yaml_output['ipv4_interfaces'] = ipv4_interfaces(system)
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                  config['pillar.per-minion.each-subtree'].each do |dst_key, src_key|
         | 
| 96 | 
            +
                    yaml_output[dst_key] = system.fetch(src_key)
         | 
| 97 | 
            +
                  end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                  result[system['net.dns.fqdn']] = { 'palletjack' => yaml_output }
         | 
| 100 | 
            +
                end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                result
         | 
| 103 | 
            +
              end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
              # The internal representation of the generated configuration, ready
         | 
| 106 | 
            +
              # to be tested or printed.
         | 
| 107 | 
            +
             | 
| 108 | 
            +
              attr_reader :salt_config
         | 
| 109 | 
            +
             | 
| 110 | 
            +
              def process
         | 
| 111 | 
            +
                @salt_config = {}
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                @salt_config[:global] = process_global_pillar if options[:global_pillar]
         | 
| 114 | 
            +
                @salt_config[:minion] = process_minion_pillars if options[:minion_pillar]
         | 
| 115 | 
            +
              end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
              def output
         | 
| 118 | 
            +
                if @salt_config[:global]
         | 
| 119 | 
            +
                  config_dir :global_pillar, 'palletjack'
         | 
| 120 | 
            +
                  config_file :global_pillar, 'palletjack', 'global.sls' do |slsfile|
         | 
| 121 | 
            +
                    slsfile << git_header('palletjack2salt')
         | 
| 122 | 
            +
                    slsfile <<
         | 
| 123 | 
            +
                      { 'palletjack' => { 'global' => @salt_config[:global] } }.to_yaml
         | 
| 124 | 
            +
                  end
         | 
| 125 | 
            +
                end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                if @salt_config[:minion]
         | 
| 128 | 
            +
                  @salt_config[:minion].each do |id, config|
         | 
| 129 | 
            +
                    config_file :minion_pillar, "#{id}.yaml" do |yamlfile|
         | 
| 130 | 
            +
                      yamlfile << git_header('palletjack2salt')
         | 
| 131 | 
            +
                      yamlfile << config.to_yaml
         | 
| 132 | 
            +
                    end
         | 
| 133 | 
            +
                  end
         | 
| 134 | 
            +
                end
         | 
| 135 | 
            +
              end
         | 
| 136 | 
            +
            end
         | 
| 137 | 
            +
             | 
| 138 | 
            +
            if PalletJack2Salt.standalone?(__FILE__)
         | 
| 139 | 
            +
              PalletJack2Salt.run
         | 
| 140 | 
            +
            end
         | 
| @@ -0,0 +1,129 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
            #
         | 
| 3 | 
            +
            # Write DNS resolver stub declarations from a Palletjack warehouse
         | 
| 4 | 
            +
            #
         | 
| 5 | 
            +
            # Data model assumptions:
         | 
| 6 | 
            +
            #
         | 
| 7 | 
            +
            #   service:
         | 
| 8 | 
            +
            #     unbound:
         | 
| 9 | 
            +
            #       server:
         | 
| 10 | 
            +
            #         - interface: 0.0.0.0
         | 
| 11 | 
            +
            #         - access-control: 192.0.2.0/24 allow
         | 
| 12 | 
            +
            #         - ...
         | 
| 13 | 
            +
            #
         | 
| 14 | 
            +
            # Other configuration categories than +system+ are not supported.
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            require 'palletjack/tool'
         | 
| 17 | 
            +
            require 'ip'
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            class PalletJack2Unbound < PalletJack::Tool
         | 
| 20 | 
            +
              def parse_options(opts)
         | 
| 21 | 
            +
                opts.banner =
         | 
| 22 | 
            +
            "Usage: #{$PROGRAM_NAME} -w <warehouse> -s <service> -o <output directory>
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            Write DNS resolver stub declarations from a Palletjack warehouse
         | 
| 25 | 
            +
            into Salt state configuration for unbound.
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            E.g.
         | 
| 28 | 
            +
              palletjack2unbound -w /etc/salt/respository/warehouse \\
         | 
| 29 | 
            +
                -o /etc/salt/repository/state/unbound/files"
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                opts.on('-o DIR', '--output DIR', 'output directory', String) {|dir|
         | 
| 32 | 
            +
                  options[:output] = dir
         | 
| 33 | 
            +
                  options[:conf_dir] = "#{dir}/conf.d"
         | 
| 34 | 
            +
                  options[:local_dir] = "#{dir}/local.d"
         | 
| 35 | 
            +
                }
         | 
| 36 | 
            +
                opts.on('-s SERVICE', '--service SERVICE',
         | 
| 37 | 
            +
                        'service name for global configuration', String) {|service|
         | 
| 38 | 
            +
                  options[:service] = service
         | 
| 39 | 
            +
                }
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                required_option :output
         | 
| 42 | 
            +
                required_option :service
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              # Write a stub-zone declaration to a file in conf.d/
         | 
| 46 | 
            +
              # If the +transparent+ option is true, also write
         | 
| 47 | 
            +
              # a local-zone ... transparent, declaration e.g. to
         | 
| 48 | 
            +
              # override the builtin RFC1918 blocking in unbound.
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              def stub_zone(zone, stub_addrs, transparent: false)
         | 
| 51 | 
            +
                return if stub_addrs.empty?
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                config_file :conf_dir, "#{zone}.conf" do |stubfile|
         | 
| 54 | 
            +
                  stubfile << git_header('palletjack2unbound')
         | 
| 55 | 
            +
                  stubfile << "
         | 
| 56 | 
            +
            stub-zone:
         | 
| 57 | 
            +
              name: #{zone}\n"
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  stub_addrs.each do |addr|
         | 
| 60 | 
            +
                    stubfile << "  stub-addr: #{addr}\n"
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  if transparent then
         | 
| 64 | 
            +
                    stubfile << "\nserver:\n  local-zone: \"#{zone}\" transparent\n"
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
              # Check if +ip+ belongs to some RFC1918 network
         | 
| 70 | 
            +
             | 
| 71 | 
            +
              def rfc1918?(ip)
         | 
| 72 | 
            +
                rfc1918_nets = [IP.new('10.0.0.0/8'),
         | 
| 73 | 
            +
                                IP.new('172.16.0.0/12'),
         | 
| 74 | 
            +
                                IP.new('192.168.0.0/16')]
         | 
| 75 | 
            +
                rfc1918_nets.any? {|net| ip.is_in?(net)}
         | 
| 76 | 
            +
              end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
              # Generate unbound service configuration
         | 
| 79 | 
            +
             | 
| 80 | 
            +
              def unbound_config(service_name)
         | 
| 81 | 
            +
                service_config = jack.fetch(kind:'service', name: service_name)
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                config_file :local_dir, "#{service_name}.conf" do |configfile|
         | 
| 84 | 
            +
                  configfile << git_header('palletjack2unbound')
         | 
| 85 | 
            +
                  service_config["service.unbound.server"].each do |config|
         | 
| 86 | 
            +
                    config.each do |key, value|
         | 
| 87 | 
            +
                      configfile << "#{key}: #{value}\n"
         | 
| 88 | 
            +
                    end
         | 
| 89 | 
            +
                  end
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
              end
         | 
| 92 | 
            +
            end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
            PalletJack2Unbound.run do
         | 
| 95 | 
            +
              config_dir :conf_dir
         | 
| 96 | 
            +
              config_dir :local_dir
         | 
| 97 | 
            +
             | 
| 98 | 
            +
              jack.each(kind:'domain') do |domain|
         | 
| 99 | 
            +
                zone = domain['net.dns.domain']
         | 
| 100 | 
            +
                stub_addrs = []
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                domain['net.dns.ns'].each do |ns|
         | 
| 103 | 
            +
                  jack.each(kind:'ipv4_interface', all?:{'net.dns.fqdn' => ns}) do |ipv4|
         | 
| 104 | 
            +
                    stub_addrs << ipv4['net.ipv4.address']
         | 
| 105 | 
            +
                  end
         | 
| 106 | 
            +
                end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                stub_zone(zone, stub_addrs)
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                next unless domain['net.ipv4.cidr']
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                # Assume all delegations happen on octet boundaries for now.
         | 
| 113 | 
            +
                # TODO: RFC 2317 classless in-addr.arpa delegation
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                ip_net = IP.new(domain['net.ipv4.cidr'])
         | 
| 116 | 
            +
                reverse_zone = ip_net.to_arpa
         | 
| 117 | 
            +
                prefix_octets, _ = domain['net.ipv4.prefixlen'].to_i.divmod(8)
         | 
| 118 | 
            +
                reverse_zone =
         | 
| 119 | 
            +
                  ip_net.to_arpa.split('.')[-(2 + prefix_octets) .. 5].join('.')
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                # Make the same assumption that palletjack2knot does;
         | 
| 122 | 
            +
                # reverse delegations are made to the same nameserver
         | 
| 123 | 
            +
                # as forward delegations.
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                stub_zone(reverse_zone, stub_addrs, transparent: rfc1918?(ip_net))
         | 
| 126 | 
            +
              end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
              unbound_config(options[:service])
         | 
| 129 | 
            +
            end
         | 
| @@ -0,0 +1,37 @@ | |
| 1 | 
            +
            #-*- ruby -*-
         | 
| 2 | 
            +
            # coding: utf-8
         | 
| 3 | 
            +
            lib = File.expand_path('../../lib', __FILE__)
         | 
| 4 | 
            +
            $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
         | 
| 5 | 
            +
            require 'palletjack/version'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            Gem::Specification.new do |spec|
         | 
| 8 | 
            +
              spec.name	= 'palletjack-tools'
         | 
| 9 | 
            +
              spec.version	= PalletJack::VERSION
         | 
| 10 | 
            +
              spec.authors	= ['Karl-Johan Karlsson']
         | 
| 11 | 
            +
              spec.email	= ['karl-johan.karlsson@saabgroup.com']
         | 
| 12 | 
            +
              spec.summary	= 'Tools for the Pallet Jack Lightweight Configuration Management Database'
         | 
| 13 | 
            +
              spec.description	= spec.summary
         | 
| 14 | 
            +
              spec.homepage    = 'https://github.com/saab-simc-admin/palletjack'
         | 
| 15 | 
            +
              spec.license	= 'MIT'
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              spec.files         = `git ls-files -z`.split("\x0").reject do |f|
         | 
| 18 | 
            +
                f.match(%r{^(test|spec|features)/})
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
              spec.bindir        = "exe"
         | 
| 21 | 
            +
              spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              spec.platform	= Gem::Platform::RUBY
         | 
| 24 | 
            +
              spec.required_ruby_version = '~>2'
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              spec.add_runtime_dependency 'palletjack', PalletJack::VERSION
         | 
| 27 | 
            +
              spec.add_runtime_dependency 'dns-zone', '~> 0.3'
         | 
| 28 | 
            +
              spec.add_runtime_dependency 'ruby-ip', '~> 0.9'
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              spec.add_development_dependency "bundler", "~> 1.13"
         | 
| 31 | 
            +
              spec.add_development_dependency "rake", "~> 10.0"
         | 
| 32 | 
            +
              spec.add_development_dependency "rspec", "~> 3.0"
         | 
| 33 | 
            +
              spec.add_development_dependency "rspec_structure_matcher", "~> 0.0.6"
         | 
| 34 | 
            +
              spec.add_development_dependency "rspec-collection_matchers", "~> 1.1.2"
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              spec.has_rdoc	= true
         | 
| 37 | 
            +
            end
         | 
    
        data.tar.gz.sig
    ADDED
    
    | Binary file | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,200 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: palletjack-tools
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.1.2
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Karl-Johan Karlsson
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: exe
         | 
| 10 | 
            +
            cert_chain:
         | 
| 11 | 
            +
            - |
         | 
| 12 | 
            +
              -----BEGIN CERTIFICATE-----
         | 
| 13 | 
            +
              MIIDljCCAn6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBIMRYwFAYDVQQDDA1jYWxs
         | 
| 14 | 
            +
              ZS5lbmdsdW5kMRkwFwYKCZImiZPyLGQBGRYJc2FhYmdyb3VwMRMwEQYKCZImiZPy
         | 
| 15 | 
            +
              LGQBGRYDY29tMB4XDTE2MTEwMjA5MjYyN1oXDTE3MTEwMjA5MjYyN1owSDEWMBQG
         | 
| 16 | 
            +
              A1UEAwwNY2FsbGUuZW5nbHVuZDEZMBcGCgmSJomT8ixkARkWCXNhYWJncm91cDET
         | 
| 17 | 
            +
              MBEGCgmSJomT8ixkARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
         | 
| 18 | 
            +
              ggEBAM7OxaztzD0LyOwK1mPcg3BhioX1EDVbD/qAFOAzBSGGlAhtmHMqAkyvJMvs
         | 
| 19 | 
            +
              iiG7xvBidWUapxiEiBwamXiOTSrp2eW+XSXW9omdWHXjBZcwHqwb1VmAlYRDkSHf
         | 
| 20 | 
            +
              dzcM/z4xlV+DJw/pFyMRWzqNdVBtWTbVXAFGjJSqQ6q21ACYJldV9U71AIpXo+oF
         | 
| 21 | 
            +
              VEMf6PZS2uhB1G+FgAtnX/xmy7OM1Cy3qc/CaJbWSddpegxWJMUn2HNQxFwIe40g
         | 
| 22 | 
            +
              WoEoiFA7qQg9DnR/5i3lW6QyfIaA5k9cv2su1VyjqKLbkFTTTjYw0P1BJmvfXjtc
         | 
| 23 | 
            +
              rMl+3HCWYj6UunZwfZi2wDGsBkkCAwEAAaOBijCBhzAJBgNVHRMEAjAAMAsGA1Ud
         | 
| 24 | 
            +
              DwQEAwIEsDAdBgNVHQ4EFgQUwHCMEKgrIMaiTkTVLKZn6yOD1SIwJgYDVR0RBB8w
         | 
| 25 | 
            +
              HYEbY2FsbGUuZW5nbHVuZEBzYWFiZ3JvdXAuY29tMCYGA1UdEgQfMB2BG2NhbGxl
         | 
| 26 | 
            +
              LmVuZ2x1bmRAc2FhYmdyb3VwLmNvbTANBgkqhkiG9w0BAQUFAAOCAQEAP9OnE0jP
         | 
| 27 | 
            +
              2vRHI/vnOkgCvLFNoOqK/YB4yDVVW69Pza+xIXcmUBvl7DQ+bBdF5AK0B1A7U0rp
         | 
| 28 | 
            +
              Pbdj0bpQtWxmUmMIbnE1w6iuVCXAabsyUfHY4mlztToWXMVOXc1SPlJ/S2XXaRd5
         | 
| 29 | 
            +
              fiNj/nBTb0YTQA0E4pZ0Aud80qZ2WLdc6FfzHUEMW91BL3bhLeDL40noHK5Lvk52
         | 
| 30 | 
            +
              phzVHIrDjCowUMTnGiPZCXEo4KZW76KwYYV6oQ6LzcrYBw5mJ4XpdgQKZgnTnRBP
         | 
| 31 | 
            +
              f8wtQllq82VF0AXUYeLtTh1f+DW3WW5BO1e2OCu5eOV7dbyaVPaNK/+rHjCN8kM/
         | 
| 32 | 
            +
              DGZSwUoNADmVkQ==
         | 
| 33 | 
            +
              -----END CERTIFICATE-----
         | 
| 34 | 
            +
            date: 2016-11-02 00:00:00.000000000 Z
         | 
| 35 | 
            +
            dependencies:
         | 
| 36 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 37 | 
            +
              name: palletjack
         | 
| 38 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 39 | 
            +
                requirements:
         | 
| 40 | 
            +
                - - '='
         | 
| 41 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 42 | 
            +
                    version: 0.1.2
         | 
| 43 | 
            +
              type: :runtime
         | 
| 44 | 
            +
              prerelease: false
         | 
| 45 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 46 | 
            +
                requirements:
         | 
| 47 | 
            +
                - - '='
         | 
| 48 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 49 | 
            +
                    version: 0.1.2
         | 
| 50 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 51 | 
            +
              name: dns-zone
         | 
| 52 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 53 | 
            +
                requirements:
         | 
| 54 | 
            +
                - - ~>
         | 
| 55 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 56 | 
            +
                    version: '0.3'
         | 
| 57 | 
            +
              type: :runtime
         | 
| 58 | 
            +
              prerelease: false
         | 
| 59 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 60 | 
            +
                requirements:
         | 
| 61 | 
            +
                - - ~>
         | 
| 62 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 63 | 
            +
                    version: '0.3'
         | 
| 64 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 65 | 
            +
              name: ruby-ip
         | 
| 66 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 67 | 
            +
                requirements:
         | 
| 68 | 
            +
                - - ~>
         | 
| 69 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 70 | 
            +
                    version: '0.9'
         | 
| 71 | 
            +
              type: :runtime
         | 
| 72 | 
            +
              prerelease: false
         | 
| 73 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 74 | 
            +
                requirements:
         | 
| 75 | 
            +
                - - ~>
         | 
| 76 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 77 | 
            +
                    version: '0.9'
         | 
| 78 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 79 | 
            +
              name: bundler
         | 
| 80 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 81 | 
            +
                requirements:
         | 
| 82 | 
            +
                - - ~>
         | 
| 83 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 84 | 
            +
                    version: '1.13'
         | 
| 85 | 
            +
              type: :development
         | 
| 86 | 
            +
              prerelease: false
         | 
| 87 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 88 | 
            +
                requirements:
         | 
| 89 | 
            +
                - - ~>
         | 
| 90 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 91 | 
            +
                    version: '1.13'
         | 
| 92 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 93 | 
            +
              name: rake
         | 
| 94 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 95 | 
            +
                requirements:
         | 
| 96 | 
            +
                - - ~>
         | 
| 97 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 98 | 
            +
                    version: '10.0'
         | 
| 99 | 
            +
              type: :development
         | 
| 100 | 
            +
              prerelease: false
         | 
| 101 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 102 | 
            +
                requirements:
         | 
| 103 | 
            +
                - - ~>
         | 
| 104 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 105 | 
            +
                    version: '10.0'
         | 
| 106 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 107 | 
            +
              name: rspec
         | 
| 108 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 109 | 
            +
                requirements:
         | 
| 110 | 
            +
                - - ~>
         | 
| 111 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 112 | 
            +
                    version: '3.0'
         | 
| 113 | 
            +
              type: :development
         | 
| 114 | 
            +
              prerelease: false
         | 
| 115 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 116 | 
            +
                requirements:
         | 
| 117 | 
            +
                - - ~>
         | 
| 118 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 119 | 
            +
                    version: '3.0'
         | 
| 120 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 121 | 
            +
              name: rspec_structure_matcher
         | 
| 122 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 123 | 
            +
                requirements:
         | 
| 124 | 
            +
                - - ~>
         | 
| 125 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 126 | 
            +
                    version: 0.0.6
         | 
| 127 | 
            +
              type: :development
         | 
| 128 | 
            +
              prerelease: false
         | 
| 129 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 130 | 
            +
                requirements:
         | 
| 131 | 
            +
                - - ~>
         | 
| 132 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 133 | 
            +
                    version: 0.0.6
         | 
| 134 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 135 | 
            +
              name: rspec-collection_matchers
         | 
| 136 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 137 | 
            +
                requirements:
         | 
| 138 | 
            +
                - - ~>
         | 
| 139 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 140 | 
            +
                    version: 1.1.2
         | 
| 141 | 
            +
              type: :development
         | 
| 142 | 
            +
              prerelease: false
         | 
| 143 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 144 | 
            +
                requirements:
         | 
| 145 | 
            +
                - - ~>
         | 
| 146 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 147 | 
            +
                    version: 1.1.2
         | 
| 148 | 
            +
            description: Tools for the Pallet Jack Lightweight Configuration Management Database
         | 
| 149 | 
            +
            email:
         | 
| 150 | 
            +
            - karl-johan.karlsson@saabgroup.com
         | 
| 151 | 
            +
            executables:
         | 
| 152 | 
            +
            - create_domain
         | 
| 153 | 
            +
            - create_ipv4_interface
         | 
| 154 | 
            +
            - create_system
         | 
| 155 | 
            +
            - dump_pallet
         | 
| 156 | 
            +
            - palletjack2kea
         | 
| 157 | 
            +
            - palletjack2knot
         | 
| 158 | 
            +
            - palletjack2pxelinux
         | 
| 159 | 
            +
            - palletjack2salt
         | 
| 160 | 
            +
            - palletjack2unbound
         | 
| 161 | 
            +
            extensions: []
         | 
| 162 | 
            +
            extra_rdoc_files: []
         | 
| 163 | 
            +
            files:
         | 
| 164 | 
            +
            - Gemfile
         | 
| 165 | 
            +
            - Rakefile
         | 
| 166 | 
            +
            - exe/create_domain
         | 
| 167 | 
            +
            - exe/create_ipv4_interface
         | 
| 168 | 
            +
            - exe/create_system
         | 
| 169 | 
            +
            - exe/dump_pallet
         | 
| 170 | 
            +
            - exe/palletjack2kea
         | 
| 171 | 
            +
            - exe/palletjack2knot
         | 
| 172 | 
            +
            - exe/palletjack2pxelinux
         | 
| 173 | 
            +
            - exe/palletjack2salt
         | 
| 174 | 
            +
            - exe/palletjack2unbound
         | 
| 175 | 
            +
            - palletjack-tools.gemspec
         | 
| 176 | 
            +
            homepage: https://github.com/saab-simc-admin/palletjack
         | 
| 177 | 
            +
            licenses:
         | 
| 178 | 
            +
            - MIT
         | 
| 179 | 
            +
            metadata: {}
         | 
| 180 | 
            +
            post_install_message: 
         | 
| 181 | 
            +
            rdoc_options: []
         | 
| 182 | 
            +
            require_paths:
         | 
| 183 | 
            +
            - lib
         | 
| 184 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 185 | 
            +
              requirements:
         | 
| 186 | 
            +
              - - ~>
         | 
| 187 | 
            +
                - !ruby/object:Gem::Version
         | 
| 188 | 
            +
                  version: '2'
         | 
| 189 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 190 | 
            +
              requirements:
         | 
| 191 | 
            +
              - - '>='
         | 
| 192 | 
            +
                - !ruby/object:Gem::Version
         | 
| 193 | 
            +
                  version: '0'
         | 
| 194 | 
            +
            requirements: []
         | 
| 195 | 
            +
            rubyforge_project: 
         | 
| 196 | 
            +
            rubygems_version: 2.0.14
         | 
| 197 | 
            +
            signing_key: 
         | 
| 198 | 
            +
            specification_version: 4
         | 
| 199 | 
            +
            summary: Tools for the Pallet Jack Lightweight Configuration Management Database
         | 
| 200 | 
            +
            test_files: []
         | 
    
        metadata.gz.sig
    ADDED