├── manifests └── init.pp ├── Modulefile ├── lib ├── facter │ └── noah_config.rb └── puppet │ └── parser │ └── functions │ ├── noah_get.rb │ └── noah_put.rb ├── bin └── noah_enc └── README.md /manifests/init.pp: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Modulefile: -------------------------------------------------------------------------------- 1 | name 'puppet-noah' 2 | version '0.0.1' 3 | license 'Apache 2.0' 4 | summary 'Puppet integration with Noah' 5 | project_page 'https://github.com/jamtur01/puppet-noah' 6 | -------------------------------------------------------------------------------- /lib/facter/noah_config.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'json' 3 | require 'uri' 4 | require 'net/http' 5 | 6 | NOAH_URL = "http://localhost:9292" 7 | 8 | url = ENV['NOAH_URL'] || NOAH_URL 9 | uri = URI.parse("#{url}/configurations/") 10 | 11 | begin 12 | request = Net::HTTP::Get.new(uri.path, initheader = {'Accept' => 'text/json'}) 13 | http = Net::HTTP.new(uri.host, uri.port) 14 | result = http.start {|http| http.request(request)} 15 | case result 16 | when Net::HTTPSuccess 17 | res = JSON.parse(result.body) 18 | res.each { |k,v| Facter.add(k) { setcode { v["body"] } } } 19 | else 20 | Facter.debug "Error: #{result.code} #{result.message.strip} for #{url}." 21 | end 22 | rescue => e 23 | Facter.debug "Unable to connect to Redis - #{e}" 24 | end 25 | -------------------------------------------------------------------------------- /bin/noah_enc: -------------------------------------------------------------------------------- 1 | #! /usr/bin/ruby 2 | # 3 | # External Node script for Puppet with Noah 4 | # 5 | # == puppet.conf Configuration 6 | # 7 | # [main] 8 | # external_nodes = /path/to/external_node 9 | # node_terminus = exec 10 | 11 | require 'rubygems' 12 | require 'yaml' 13 | require 'json' 14 | require 'uri' 15 | require 'net/http' 16 | 17 | NOAH_URL = "http://localhost:9292" 18 | 19 | if ARGV.size == 0 20 | STDERR.puts "Error: must specify a node to query"; exit 1 21 | else 22 | NODE = ARGV.first 23 | end 24 | 25 | url = ENV['NOAH_URL'] || NOAH_URL 26 | uri = URI.parse("#{url}/hosts/#{NODE}") 27 | 28 | request = Net::HTTP::Get.new(uri.path, initheader = {'Accept' => 'text/json'}) 29 | http = Net::HTTP.new(uri.host, uri.port) 30 | result = http.start {|http| http.request(request)} 31 | 32 | case result 33 | when Net::HTTPSuccess 34 | res = JSON.parse(result.body) 35 | output = {} 36 | output['classes'] = res["services"].keys 37 | puts output.to_yaml 38 | exit 0 39 | else 40 | STDERR.puts "Error: #{result.code} #{result.message.strip} for node #{NODE}." 41 | exit 1 42 | end 43 | -------------------------------------------------------------------------------- /lib/puppet/parser/functions/noah_get.rb: -------------------------------------------------------------------------------- 1 | Puppet::Parser::Functions::newfunction(:noah_get, :type => :rvalue, :doc => " 2 | Returns values from Noah. Takes a Noah URL, request type and data and returns data from a running Noah instance: 3 | 4 | noah_get($noah_url, $type, $data) 5 | 6 | Where `$noah_url` is the URL of a valid Noah server, `$type` is one of application, host, configuration or service and `$data` is the name of the data type to return. 7 | 8 | noah_get('http://localhost:9292', host, $hostname) 9 | 10 | This will retrieve the host information for the `$hostname` variable from the Noah server at `http://localhost:9292`. 11 | ") do |args| 12 | 13 | request_types = ["host", "service", "application", "configuration"] 14 | begin 15 | require 'rest-client' 16 | require 'uri' 17 | rescue LoadError => detail 18 | Puppet.info "noah_get(): You need to install the rest-client gem to use the noah_get function" 19 | end 20 | 21 | raise ArgumentError, ("noah_get(): Wrong number of arguments (#{args.length}; must be 3)") if args.length != 3 22 | 23 | noah_url, type, data = URI.parse(args[0]), args[1], args[2] 24 | 25 | raise ArgumentError, ("noah_get(): Request type #{type} invalid. Valid request types are #{request_types.join(',')}") unless request_types.include?(type) 26 | 27 | case type 28 | when "host" 29 | resource = "/hosts/#{data}" 30 | when "application" 31 | resource = "/applications/#{data}" 32 | when "service" 33 | resource = "/services/#{data}" 34 | when "configuration" 35 | resource = "/configurations/#{data}" 36 | end 37 | 38 | RestClient.get("#{noah_url}#{resource}"){ |response, request, result, &block| 39 | case response.code 40 | when 200 41 | Puppet.debug "noah_get(): Returned #{type} #{data} from Noah server #{noah_url}" 42 | output = JSON.parse(response) 43 | if type == "service" 44 | output.values.each do |hosts| 45 | hosts.values.each do |host| 46 | begin 47 | host['data'] = JSON.parse(host['data']) 48 | rescue JSON::ParserError => e 49 | # Leave non-json data as is 50 | end 51 | end 52 | end 53 | end 54 | output 55 | when 404 56 | Puppet.info "noah_get(): No #{type} #{data} available on Noah server #{noah_url}" 57 | when 500 58 | Puppet.info "noah_get(): Noah server #{noah_url} returned error" 59 | else 60 | Puppet.err "noah_get(): Query failed #{request}, #{result}" 61 | end 62 | } 63 | end 64 | -------------------------------------------------------------------------------- /lib/puppet/parser/functions/noah_put.rb: -------------------------------------------------------------------------------- 1 | Puppet::Parser::Functions::newfunction(:noah_put, :type => :rvalue, :doc => " 2 | Puts values into Noah. Takes a Noah URL, request type and data and puts data into a running Noah instance: 3 | 4 | noah_put($noah_url, $type, $data) 5 | 6 | Where `$noah_url` is the URL of a valid Noah server, `$type` is one of application, host, configuration or service and `$data` is the data to send to the 7 | server. 8 | 9 | noah_put('http://localhost:9292', host, 'up') 10 | 11 | This will create a host using the `fqdn` fact as the hostname with a status of `up` (valid statuses are `up` or `down`) to the Noah server 12 | at `http://localhost:9292`. You can similarly create applications, services and configuration items. 13 | ") do |args| 14 | 15 | request_types = ["host", "service", "application", "configuration"] 16 | begin 17 | require 'rest-client' 18 | require 'uri' 19 | rescue LoadError => detail 20 | Puppet.info "noah_put(): You need to install the rest-client gem to use the noah_put function" 21 | end 22 | 23 | raise ArgumentError, ("noah_put(): Wrong number of arguments (#{args.length}; must be 3)") if args.length != 3 24 | 25 | noah_url, type, data = URI.parse(args[0]), args[1], args[2] 26 | 27 | raise ArgumentError, ("noah_put(): Request type #{type} invalid. Valid request types are #{request_types.join(',')}") unless request_types.include?(type) 28 | 29 | case type 30 | when "host" 31 | resource = "/hosts/#{lookupvar('fqdn')}" 32 | status = [ "up", "down"] 33 | raise ArgumentError, ("noah_put(): Host status must be 'up' or 'down'") unless status.include?(data) 34 | payload = { "status" => "#{data}" } 35 | when "application" 36 | resource = "/applications/#{data}" 37 | payload = { "name" => "#{data}" } 38 | when "service" 39 | resource = "/services/#{data}/#{lookupvar('fqdn')}" 40 | payload = { "status" => "up", "host_status" => "up" } 41 | when "configuration" 42 | resource = "/configurations/#{data}" 43 | payload = { "format" => "string", "body" => "#{data}" } 44 | end 45 | 46 | RestClient.put("#{noah_url}#{resource}", payload.to_json){ |response, request, result, &block| 47 | case response.code 48 | when 200 49 | Puppet.info "noah_put(): Posted #{type} #{data} to Noah server #{noah_url}" 50 | when 404 51 | Puppet.info "noah_put(): No #{type} #{data} available on Noah server #{noah_url}" 52 | else 53 | Puppet.err "noah_put(): Query failed #{request}, #{result}" 54 | end 55 | } 56 | end 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | puppet-noah 2 | =========== 3 | 4 | Description 5 | ----------- 6 | 7 | A collection of Puppet bits and pieces for working with Noah (https://github.com/lusis/noah) 8 | 9 | Requirements 10 | ------------ 11 | 12 | * `Noah` 13 | * `puppet` 14 | 15 | Installation 16 | ------------ 17 | 18 | Install `puppet-noah` as a module in your Puppet master's module 19 | path. You then need to use `pluginsync` to sync the functions and fact onto your master. 20 | 21 | To use the ENC you can copy the ENC script to somewhere appropriate and enable external node 22 | configuration in your Puppet master's `puppet.conf` configuration file. 23 | 24 | Usage 25 | ----- 26 | 27 | ### ENC 28 | 29 | The module contains an ENC (External Node Classifier) for Noah in 30 | `bin/noah_enc`. The ENC connecs to a Noah server and returns node 31 | classification data. It connects either to a hard-coded Noah URL 32 | (defaulting to `http://localhost:9292`) which you can change in the ENC 33 | or override by setting the `NOAH_URL` environmental variable. 34 | 35 | The ENC queries the hosts defined on a Noah server via the [Host 36 | API](https://github.com/lusis/Noah/wiki/Host-API) and returns a list of 37 | classes based on services defined on that host, for example if the host 38 | has the services `mysql` and `redis` then the ENC will return the 39 | classes: 40 | 41 | - mysql 42 | - redis 43 | 44 | See information on ENC's 45 | [here](http://docs.puppetlabs.com/guides/external_nodes.html) 46 | 47 | ### The noah_get function 48 | 49 | The `noah_get` function returns data from a Noah instance. It can use 50 | the Host, Service, Configuration and Application APIs to do this. It 51 | requires the `rest-client` gem to work correctly: 52 | 53 | $ gem install rest-client 54 | 55 | You can call the function like so by specifying the URL, the type of data, 56 | and the name of the data item being returned: 57 | 58 | $foo = noah_get("http://localhost:9292", host, bar) 59 | 60 | This will return data about the `bar` host in the form of a hash 61 | and assign it to the variable `$foo`. The function accepts the following 62 | data types: 63 | 64 | - host 65 | - application 66 | - service 67 | - configuration 68 | 69 | You can see the full list of applicable APIs 70 | [here](https://github.com/lusis/Noah/wiki/Final-API-1.0). 71 | 72 | ### The noah_put function 73 | 74 | The `noah_put` function sends data to a Noah server. It can use the 75 | Host, Service, Configuration and Application APIs to do this (although 76 | its use of the Service and Configuration API is currently somewhat limited). It 77 | requires the `rest-client` gem to work correctly: 78 | 79 | $ gem install rest-client 80 | 81 | You can call the function like so by specifying the URL, the type of data, 82 | and the data being sent: 83 | 84 | noah_put("http://localhost:9292", host, up) 85 | 86 | This will create a host named using the value of the `fqdn` fact with a 87 | status of `up` (valid statuses are `up` or `down`). 88 | 89 | You can also create a new application: 90 | 91 | noah_put("http://localhost:9292", application, foo) 92 | 93 | This will create a new application called `foo` on the Noah server. 94 | 95 | You can create a new service: 96 | 97 | noah_put("http://localhost:9292", service, mysql) 98 | 99 | This will create a new service called `mysql` with a status of `up`. As 100 | yet you can't control the service or host status of the service being 101 | created. 102 | 103 | Or you can create configuration items: 104 | 105 | noah_put("http://localhost:9292", configuration, bar) 106 | 107 | This creates a configuration item called `bar` with a format of `string` 108 | and a value of `bar`. Further work is needed to extend this to support 109 | the full set of naming and data types avaialble. Consider this an MVP. 110 | 111 | ### Noah facts 112 | 113 | The `noah_data` fact connects to the Noah server and returns all of 114 | the configurations items via the [Configuration 115 | API](https://github.com/lusis/Noah/wiki/Configuration-API) as fact 116 | values. As Facter currently only supports strings as fact values this 117 | means some data isn't yet in an ideal format. 118 | 119 | Author 120 | ------ 121 | 122 | James Turnbull 123 | 124 | License 125 | ------- 126 | 127 | Author:: James Turnbull () 128 | Copyright:: Copyright (c) 2011 James Turnbull 129 | License:: Apache License, Version 2.0 130 | 131 | Licensed under the Apache License, Version 2.0 (the "License"); 132 | you may not use this file except in compliance with the License. 133 | You may obtain a copy of the License at 134 | 135 | http://www.apache.org/licenses/LICENSE-2.0 136 | 137 | Unless required by applicable law or agreed to in writing, software 138 | distributed under the License is distributed on an "AS IS" BASIS, 139 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 140 | See the License for the specific language governing permissions and 141 | limitations under the License. 142 | --------------------------------------------------------------------------------