File: generator.rb

package info (click to toggle)
ruby-beaker-hostgenerator 1.7.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,556 kB
  • sloc: ruby: 3,059; makefile: 3
file content (160 lines) | stat: -rw-r--r-- 5,298 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
require 'beaker-hostgenerator/data'
require 'beaker-hostgenerator/roles'
require 'beaker-hostgenerator/hypervisor'
require 'beaker-hostgenerator/parser'

module BeakerHostGenerator
  class Generator
    include BeakerHostGenerator::Data
    include BeakerHostGenerator::Parser
    include BeakerHostGenerator::Roles

    # Main host generation entry point, returns a Ruby map for the given host
    # specification and optional configuration.
    #
    # @param layout [String] The raw hosts specification user input.
    #                        For example `"centos6-64m-redhat7-64a"`.
    # @param options [Hash] Global, optional configuration such as the default
    #                       hypervisor or OS info version.
    #
    # @returns [Hash] A complete Ruby map as defining the HOSTS and CONFIG
    #                 sections as required by Beaker.
    def generate(layout, options)
      layout = prepare(layout)
      tokens = tokenize_layout(layout)
      config = {}.deeper_merge(BASE_CONFIG)
      nodeid = Hash.new(1)
      ostype = nil
      bhg_version = options[:osinfo_version] || 0

      tokens.each do |token|
        if is_ostype_token?(token, bhg_version)
          if nodeid[ostype] == 1 and ostype != nil
            raise "Error: no nodes generated for #{ostype}"
          end
          ostype = token
          next
        end

        node_info = parse_node_info_token(token)

        # Build node host name
        platform = "#{ostype}-#{node_info['bits']}"
        host_name = "#{platform}-#{nodeid[ostype]}"

        node_info['platform'] = platform
        node_info['ostype'] = ostype
        node_info['nodeid'] = nodeid[ostype]

        host_config = base_host_config(options)

        # Delegate to the hypervisor
        hypervisor = BeakerHostGenerator::Hypervisor.create(node_info, options)
        host_config = hypervisor.generate_node(node_info, host_config, bhg_version)
        config['CONFIG'].deeper_merge!(hypervisor.global_config())

        # Merge in any arbitrary key-value host settings. Treat the 'hostname'
        # setting specially, and don't merge it in as an arbitrary setting.
        arbitrary_settings = node_info['host_settings']
        host_name = arbitrary_settings.delete('hostname') if
          arbitrary_settings.has_key?('hostname')
        host_config.merge!(arbitrary_settings)

        if PE_USE_WIN32 && ostype =~ /windows/ && node_info['bits'] == "64"
          host_config['ruby_arch'] = 'x86'
          host_config['install_32'] = true
        end

        generate_host_roles!(host_config, node_info, options)

        config['HOSTS'][host_name] = host_config
        nodeid[ostype] += 1
      end

      # Merge in global configuration settings after the hypervisor defaults
      if options[:global_config]
        decoded = prepare(options[:global_config])
        # Support for strings without '{}' was introduced, so just double
        # check here to ensure that we pass in values surrounded by '{}'.
        if !decoded.start_with?('{')
          decoded = "{#{decoded}}"
        end
        global_config = settings_string_to_map(decoded)
        config['CONFIG'].deeper_merge!(global_config)
      end

      # Munge non-string scalar values into proper data types
      unstringify_values!(config)

      return config
    end

    def get_host_roles(node_info)
      roles = []

      node_info['roles'].each_char do |c|
        roles << ROLES[c]
      end

      node_info['arbitrary_roles'].each do |role|
        roles << role
      end

      return roles
    end

    private

    def generate_host_roles!(host_config, node_info, options)
      if not options[:disable_default_role]
        host_config['roles'] = ['agent']
      else
        host_config['roles'] = []
      end

      host_config['roles'].concat get_host_roles(node_info)
      host_config['roles'].uniq!

      if not options[:disable_role_config]
        host_config['roles'].each do |role|
          host_config.deeper_merge! get_role_config(role)
        end
      end
    end

    # Passes over all the values of config['HOSTS'] and config['CONFIG'] and
    # subsequent arrays to convert numbers or booleans into proper integer
    # or boolean types.
    def unstringify_values!(config)
      config['HOSTS'].each do |host, settings|
        settings.each do |k, v|
          config['HOSTS'][host][k] = unstringify_value(v)
        end
      end
      config['CONFIG'].each do |k, v|
        config['CONFIG'][k] = unstringify_value(v)
      end
    end

    # Attempts to convert numeric strings and boolean strings into proper
    # integer and boolean types. If value is an array, it will recurse
    # through those values.
    # Returns the input value if it's not a number string or boolean string.
    # For example "123" would be converted to 123, and "true"/"false" would be
    # converted to true/false.
    # The only valid boolean-strings are "true" and "false".
    def unstringify_value(value)
      result = Integer(value) rescue value
      if value == 'true'
        result = true
      elsif value == 'false'
        result = false
      elsif value.kind_of?(Array)
        value.each_with_index do |v, i|
          result[i] = unstringify_value(v)
        end
      end
      result
    end
  end
end