Class: ChefRundeck

Inherits:
Sinatra::Base
  • Object
show all
Includes:
Chef::Mixin::XMLEscape
Defined in:
lib/chef-rundeck.rb

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Class Attribute Details

.api_urlObject

Returns the value of attribute api_url.



45
46
47
# File 'lib/chef-rundeck.rb', line 45

def api_url
  @api_url
end

.cache_timeoutObject

Returns the value of attribute cache_timeout.



48
49
50
# File 'lib/chef-rundeck.rb', line 48

def cache_timeout
  @cache_timeout
end

.client_keyObject

Returns the value of attribute client_key.



46
47
48
# File 'lib/chef-rundeck.rb', line 46

def client_key
  @client_key
end

.config_fileObject

Returns the value of attribute config_file.



42
43
44
# File 'lib/chef-rundeck.rb', line 42

def config_file
  @config_file
end

.partial_searchObject

Returns the value of attribute partial_search.



49
50
51
# File 'lib/chef-rundeck.rb', line 49

def partial_search
  @partial_search
end

.project_configObject

Returns the value of attribute project_config.



47
48
49
# File 'lib/chef-rundeck.rb', line 47

def project_config
  @project_config
end

.usernameObject

Returns the value of attribute username.



43
44
45
# File 'lib/chef-rundeck.rb', line 43

def username
  @username
end

.web_ui_urlObject

Returns the value of attribute web_ui_url.



44
45
46
# File 'lib/chef-rundeck.rb', line 44

def web_ui_url
  @web_ui_url
end

Class Method Details

.configureObject



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
# File 'lib/chef-rundeck.rb', line 51

def configure
  Chef::Config.from_file(ChefRundeck.config_file)
  Chef::Log.level = Chef::Config[:log_level]

  unless ChefRundeck.api_url
    ChefRundeck.api_url = Chef::Config[:chef_server_url]
  end

  unless ChefRundeck.client_key
    ChefRundeck.client_key = Chef::Config[:client_key]
  end


  if (File.exists?(ChefRundeck.project_config)) then
    Chef::Log.info("Using JSON project file #{ChefRundeck.project_config}")
    projects = File.open(ChefRundeck.project_config, "r") { |f| JSON.parse(f.read) }
    projects.keys.each do | project |
      get "/#{project}" do
        content_type 'text/xml'
        Chef::Log.info("Loading nodes for /#{project}")
        # TODO: Validate project data before rendering the document?
        send_file build_project project, projects[project]['pattern'], (projects[project]['username'].nil? ? ChefRundeck.username : projects[project]['username']), (projects[project]['hostname'].nil? ? "fqdn" : projects[project]['hostname']), projects[project]['attributes']
      end
      cache_file = "#{Dir.tmpdir}/chef-rundeck-#{project}.xml"
      at_exit { File.delete(cache_file) if File.exist?(cache_file) }
    end
  end
  
  get '/' do
    content_type 'text/xml'
    Chef::Log.info("Loading all nodes for /")
    send_file build_project
  end
  
  cache_file = "#{Dir.tmpdir}/chef-rundeck-default.xml"
  at_exit { File.delete(cache_file) if File.exist?(cache_file) }
end

Instance Method Details

#build_project(project = "default", pattern = "*:*", username = ChefRundeck.username, hostname = "fqdn", custom_attributes = nil) ⇒ Object



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
161
162
163
164
165
166
# File 'lib/chef-rundeck.rb', line 90

def build_project (project="default", pattern="*:*", username=ChefRundeck.username, hostname="fqdn", custom_attributes=nil)
  response = nil
  begin

    # file is too new use it again
    if (File.exists?("#{Dir.tmpdir}/chef-rundeck-#{project}.xml") && (Time.now - File.atime("#{Dir.tmpdir}/chef-rundeck-#{project}.xml") < ChefRundeck.cache_timeout)) then 
      return "#{Dir.tmpdir}/chef-rundeck-#{project}.xml"
    end

    results = []
    if ChefRundeck.partial_search then
      keys = { 'name' => ['name'],
               'kernel_machine' => [ 'kernel', 'machine' ],
               'kernel_os' => [ 'kernel', 'os' ],
               'fqdn' => [ 'fqdn' ],
               'run_list' => [ 'run_list' ],
               'roles' => [ 'roles' ],
               'recipes' => [ 'recipes' ],
               'chef_environment' => [ 'chef_environment' ],
               'platform' => [ 'platform'],
               'platform_version' => [ 'platform_version' ],
               'tags' => [ 'tags' ],
               'hostname' => [hostname]
             }  
      if !custom_attributes.nil? then
        custom_attributes.each do |attr|
        attr_name = attr.gsub('.', '_')
        attr_value = attr.split('.')
        keys[attr_name] = attr_value
        end
      end
      # do search
      Chef::Log.info("partial search started (project: '#{project}')")
      results = partial_search(:node,pattern, :keys => keys)
      Chef::Log.info("partial search finshed (project: '#{project}', count: #{results.length})")
    else 
      q = Chef::Search::Query.new
      Chef::Log.info("search started (project: '#{project}')")
      results = q.search("node",pattern)[0]
      Chef::Log.info("search finshed (project: '#{project}', count: #{results.length})")
      results = convert_results(results, hostname, custom_attributes)
    end
    
    response = File.open("#{Dir.tmpdir}/chef-rundeck-#{project}.xml", 'w')
    response.write '<?xml version="1.0" encoding="UTF-8"?>'
    response.write '<!DOCTYPE project PUBLIC "-//DTO Labs Inc.//DTD Resources Document 1.0//EN" "project.dtd">'
    response.write '<project>'

    Chef::Log.info("building nodes (project: '#{project}')")
    failed = 0
    results.each do |node|
      begin
        # validate the node
        begin
          node_is_valid? node
        rescue ArgumentError => ae
          Chef::Log.warn("invalid node element: #{ae}")
          failed = failed + 1
          next
        end
        
        #write the node to the project
        response.write build_node(node, username, hostname, custom_attributes)
      rescue Exception => e
        Chef::Log.error("=== could not generate xml for #{node}:  #{e.message}")
        Chef::Log.debug(e.backtrace.join('\n'))
      end
    end
    Chef::Log.info("nodes complete (project: '#{project}', total: #{results.length - failed}, failed: #{failed})")
    
    response.write "</project>"
    Chef::Log.debug(response)
  ensure
    response.close unless response == nil
  end
  return response.path
end