Class: Puppet::Dashboard::Classifier

Inherits:
Object
  • Object
show all
Defined in:
lib/puppet/dashboard/classifier.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ Classifier

Returns a new instance of Classifier.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/puppet/dashboard/classifier.rb', line 34

def initialize(options)
  # Workaround for the fact that Dashboard is typically insecure.
  @connection_options = {
    :enc_server => options[:enc_server],
    :enc_port => options[:enc_port],
    :enc_ssl => options[:enc_ssl],
    :enc_auth_passwd => options[:enc_auth_passwd],
    :enc_auth_user => options[:enc_auth_user]
  }
  ssldir_save = Puppet[:ssldir]
  Puppet[:ssldir] = '/dev/null'
  @http_connection = Puppet::Network::HttpPool.http_instance(options[:enc_server], options[:enc_port])
  Puppet[:ssldir] = ssldir_save
  if options[:enc_ssl] then
    @http_connection.use_ssl = true
    @uri_scheme = 'https'
    # We intentionally use SSL only for encryption and not authenticity checking
    @http_connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
  else
    @http_connection.use_ssl = false
    @uri_scheme = 'http'
  end
  Puppet.info "Using #{@uri_scheme}://#{options[:enc_server]}:#{options[:enc_port]} as Dashboard."
end

Instance Attribute Details

#connection_optionsObject (readonly)

Returns the value of attribute connection_options.



32
33
34
# File 'lib/puppet/dashboard/classifier.rb', line 32

def connection_options
  @connection_options
end

Class Method Details

.connection(options) ⇒ Object



5
6
7
# File 'lib/puppet/dashboard/classifier.rb', line 5

def self.connection(options)
  @connection ||= Puppet::Dashboard::Classifier.new(options)
end

.handle_json_response(response, action, expected_code = '200') ⇒ Object



231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/puppet/dashboard/classifier.rb', line 231

def self.handle_json_response(response, action, expected_code='200')
  if response.code == expected_code
    Puppet.info "#{action} ... Done"
    PSON.parse response.body
  else
    # I should probably raise an exception!
    Puppet.warning "#{action} ... Failed"
    Puppet.info("Body: #{response.body}")
    Puppet.warning "Server responded with a #{response.code} status"
    case response.code
    when /401/
      Puppet.notice "A 401 response is the HTTP code for an Unauthorized request"
      Puppet.notice "This error likely means you need to supply the --enc-auth-user and --enc-auth-passwd options"
      Puppet.notice "Alternatively set PUPPET_ENC_AUTH_PASSWD environment variable for increased security"
    end
    raise Puppet::Error, "Could not: #{action}, got #{response.code} expected #{expected_code}"
  end
end

.http_request(http, path, options = {}, action = nil, expected_code = '200', data = nil) ⇒ Object

Method to make generic, SSL, Authenticated HTTP requests and parse the JSON response. Primarily for #10377 and #10197



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/puppet/dashboard/classifier.rb', line 203

def self.http_request(http, path, options = {}, action = nil, expected_code = '200', data = nil)
  action ||= path
  # We need to POST data, otherwise we'll use GET
  request = data ? Net::HTTP::Post.new(path) : Net::HTTP::Get.new(path)
  # Set the form data
  request.body = data.to_pson if data
  # Authentication information
  request.basic_auth(options[:enc_auth_user], options[:enc_auth_passwd]) if ! options[:enc_auth_user].nil?
  # Content Type of the request
  #request["Content-Type"]='applicaton/json'
  request.set_content_type('application/json')

  # Wrap the request in an exception handler
  begin
    response = http.start { |http| http.request(request) }
  rescue Errno::ECONNREFUSED => e
    Puppet.warning 'Registering node ... Error'
    Puppet.err "Could not connect to host #{options[:enc_server]} on port #{options[:enc_port]}"
    Puppet.err "This could be because a local host firewall is blocking the connection"
    Puppet.err "Please check your --enc-server and --enc-port options"
    ex = Puppet::Error.new(e)
    ex.set_backtrace(e.backtrace)
    raise ex
  end
  # Return the parsed JSON response
  handle_json_response(response, action, expected_code)
end

.to_array(maybe_array) ⇒ Object

convenience method for array munging



10
11
12
13
14
# File 'lib/puppet/dashboard/classifier.rb', line 10

def self.to_array(maybe_array)
  if maybe_array
    maybe_array.is_a?(Array) ? maybe_array : maybe_array.split(',')
  end
end

.to_hash(maybe_hash) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/puppet/dashboard/classifier.rb', line 16

def self.to_hash(maybe_hash)
  if maybe_hash.is_a?(Hash)
    maybe_hash
  elsif maybe_hash.is_a?(String)
    Hash[maybe_hash.split(',').collect do |tag|
      if tag =~ /(\w+)=(\w+)/
        [$1, $2]
      else
        raise ArgumentError, 'Could not parse hash. Please check your format'
      end
    end ]
  else
    raise ArgumentError, 'Could not parse hash. Please check your format'
  end
end

Instance Method Details

#add_module(module_names, modulepath) ⇒ Object



265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/puppet/dashboard/classifier.rb', line 265

def add_module(module_names, modulepath)
  Dir.chdir(modulepath.split(':').first) do
    module_names.each do |module_name|
      # install the module into the modulepath
      # TODO - port to use the new faces version
      # Puppet::Face[:module, :current].install(module_name)
      `puppet module install #{module_name}`
      author, puppet_module = module_name.split('-', 2)
      register_module(puppet_module || module_name, modulepath)
    end
  end
end

#create(type, action, data) ⇒ Object



190
191
192
193
194
195
196
197
198
199
# File 'lib/puppet/dashboard/classifier.rb', line 190

def create(type, action, data)
  response = Puppet::Dashboard::Classifier.http_request(
    @http_connection,
    "/#{type}.json",
    connection_options,
    action,
    '201',
    data
  )
end

#create_class(name) ⇒ Object

create the specified class this does not check to see if the class exists if you want to check first, use create_classes



77
78
79
80
# File 'lib/puppet/dashboard/classifier.rb', line 77

def create_class(name)
  data = { 'node_class' => { 'name' => name } }
  create('node_classes', "Creating class #{name}", data)
end

#create_classes(klasses) ⇒ Object

given a list of classes, create the ones that do not exist return a hash of all of the specified hashes name => id



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/puppet/dashboard/classifier.rb', line 84

def create_classes(klasses)
  klass_hash = {}
  if klasses
    node_classes = list('node_classes', 'Listing classes')

    # build a hash of class_name => id
    node_classes.each do |x|
      if klasses.include?(x['name'])
        klass_hash[x['name']] = x['id']
      end
    end

    klasses.each do |k|
      unless klass_hash[k]
        # detect any classes that were not found and creat them
        result = create_class(k)
        Puppet.info("Created class: #{result.inspect}")
        klass_hash[result['name']] = result['id']
      end
    end

  end
  klass_hash
end

#create_group(group_name, parameters, parent_groups, classes) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/puppet/dashboard/classifier.rb', line 143

def create_group(group_name, parameters, parent_groups, classes)

  # check if the group already exists
  group = find_group(group_name)

  # stop if group already exists
  return {:status => "Group #{group_name} already exists"} if group

  # make sure that the classes exist, create them if they do not
  klass_ids = (create_classes(classes) || {} ).values

  # make sure that the parent groups exist, fail if they do not...?
  # I dont want to support parent groups yet...
  parent_group_id_hash = {}
  (parent_groups || []).collect do |parent|
    group_hash = find_group(parent)
    if group_hash
      parent_group_id_hash[group_hash['name']] = group_hash['id']
    else
      return {:status => "Parent Group #{parent} for group #{group_name} does not exist"}
    end
  end
  parent_group_ids = (parent_group_id_hash || {} ).values

  # set up the post data
  data = { 'node_group' => { 'name' => group_name } }
  data['node_group']['assigned_node_class_ids'] = klass_ids
  data['node_group']['assigned_node_group_ids'] = parent_group_ids
  data['node_group']['parameter_attributes'] = []
  (parameters || {} ).each do |key, value|
    data['node_group']['parameter_attributes'].push({'key' => key, 'value' => value})
  end

  create('node_groups', "Creating group: #{group_name}", data)

end

#create_node(certname, klasses, parameters, groups) ⇒ Object



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
# File 'lib/puppet/dashboard/classifier.rb', line 109

def create_node(certname, klasses, parameters, groups)

  # find the current list of nodes
  node = find_node(certname)

  # stop if node already exists
  return {:status => "Node #{certname} already exists"} if node

  # create any missing classes
  klass_ids = create_classes(klasses).values

  # make sure all groups exist and get their ids
  group_id_hash = {}
  (groups || []).collect do |group|
    group_hash = find_group(group)
    if group_hash
      group_id_hash[group_hash['name']] = group_hash['id']
    else
      return {:status => "Parent Group #{group} for node #{certname} does not exist"}
    end
  end
  group_ids = (group_id_hash || {} ).values

  data = { 'node' => { 'name' => certname } }
  data['node']['assigned_node_class_ids'] = klass_ids
  data['node']['assigned_node_group_ids'] = group_ids
  data['node']['parameter_attributes'] = []
  (parameters || {}).each do |key, value|
    data['node']['parameter_attributes'].push({'key' => key, 'value' => value})
  end

  create('nodes', "Creating node #{certname}", data)
end

#find_group(group_name) ⇒ Object

find a group if it exists



68
69
70
71
72
# File 'lib/puppet/dashboard/classifier.rb', line 68

def find_group(group_name)
  list('node_groups', "Listing groups").find do |group|
    group['name'] == group_name
  end
end

#find_node(certname) ⇒ Object

find a node by certname



61
62
63
64
65
# File 'lib/puppet/dashboard/classifier.rb', line 61

def find_node(certname)
  list('nodes', "Listing nodes").find do |node|
    node['name'] == certname
  end
end

#list(type, action) ⇒ Object

list expects a return of 200



181
182
183
184
185
186
187
188
# File 'lib/puppet/dashboard/classifier.rb', line 181

def list(type, action)
  nodes = Puppet::Dashboard::Classifier.http_request(
    @http_connection,
    "/#{type}.json",
    connection_options,
    action
  )
end

#register_module(module_name, modulepath) ⇒ Object

regist all classes from a module in the dashboard



251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/puppet/dashboard/classifier.rb', line 251

def register_module(module_name, modulepath)
  Puppet[:modulepath] = modulepath
  klasses = (Puppet::Face[:resource_type, :current].search(module_name) || []).collect do |resource_type|
    # I am not going to bother checking that everything we find is loadable
    # This patch assumes that the modules are properly organized
    if resource_type.type == :hostclass
      resource_type.name
    else
      nil
    end
  end.compact
  create_classes(klasses)
end